]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libexec/getty/main.c
Add the missing rerelease target back.
[FreeBSD/FreeBSD.git] / libexec / getty / main.c
1 /*-
2  * Copyright (c) 1980, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1980, 1993\n\
37         The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)from: main.c        8.1 (Berkeley) 6/20/93";
43 #endif
44 static const char rcsid[] =
45         "$Id$";
46 #endif /* not lint */
47
48 #include <sys/param.h>
49 #include <sys/stat.h>
50 #include <sys/ioctl.h>
51 #include <sys/resource.h>
52 #include <sys/ttydefaults.h>
53 #include <sys/utsname.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <locale.h>
58 #include <libutil.h>
59 #include <signal.h>
60 #include <setjmp.h>
61 #include <signal.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <syslog.h>
65 #include <termios.h>
66 #include <time.h>
67 #include <unistd.h>
68
69 #include "gettytab.h"
70 #include "pathnames.h"
71 #include "extern.h"
72
73 /*
74  * Set the amount of running time that getty should accumulate
75  * before deciding that something is wrong and exit.
76  */
77 #define GETTY_TIMEOUT   60 /* seconds */
78
79 #undef CTRL
80 #define CTRL(x)  (x&037)
81
82 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
83
84 #define PPP_FRAME           0x7e  /* PPP Framing character */
85 #define PPP_STATION         0xff  /* "All Station" character */
86 #define PPP_ESCAPE          0x7d  /* Escape Character */
87 #define PPP_CONTROL         0x03  /* PPP Control Field */
88 #define PPP_CONTROL_ESCAPED 0x23  /* PPP Control Field, escaped */
89 #define PPP_LCP_HI          0xc0  /* LCP protocol - high byte */
90 #define PPP_LCP_LOW         0x21  /* LCP protocol - low byte */
91
92 struct termios tmode, omode;
93
94 int crmod, digit, lower, upper;
95
96 char    hostname[MAXHOSTNAMELEN];
97 char    name[MAXLOGNAME*3];
98 char    dev[] = _PATH_DEV;
99 char    ttyn[32];
100
101 #define OBUFSIZ         128
102 #define TABBUFSIZ       512
103
104 char    defent[TABBUFSIZ];
105 char    tabent[TABBUFSIZ];
106
107 char    *env[128];
108
109 char partab[] = {
110         0001,0201,0201,0001,0201,0001,0001,0201,
111         0202,0004,0003,0205,0005,0206,0201,0001,
112         0201,0001,0001,0201,0001,0201,0201,0001,
113         0001,0201,0201,0001,0201,0001,0001,0201,
114         0200,0000,0000,0200,0000,0200,0200,0000,
115         0000,0200,0200,0000,0200,0000,0000,0200,
116         0000,0200,0200,0000,0200,0000,0000,0200,
117         0200,0000,0000,0200,0000,0200,0200,0000,
118         0200,0000,0000,0200,0000,0200,0200,0000,
119         0000,0200,0200,0000,0200,0000,0000,0200,
120         0000,0200,0200,0000,0200,0000,0000,0200,
121         0200,0000,0000,0200,0000,0200,0200,0000,
122         0000,0200,0200,0000,0200,0000,0000,0200,
123         0200,0000,0000,0200,0000,0200,0200,0000,
124         0200,0000,0000,0200,0000,0200,0200,0000,
125         0000,0200,0200,0000,0200,0000,0000,0201
126 };
127
128 #define ERASE   tmode.c_cc[VERASE]
129 #define KILL    tmode.c_cc[VKILL]
130 #define EOT     tmode.c_cc[VEOF]
131
132 static void     dingdong __P((int));
133 static int      getname __P((void));
134 static void     interrupt __P((int));
135 static void     oflush __P((void));
136 static void     prompt __P((void));
137 static void     putchr __P((int));
138 static void     putf __P((const char *));
139 static void     putpad __P((const char *));
140 static void     puts __P((const char *));
141 static void     timeoverrun __P((int));
142 static char     *getline __P((int));
143 static void     setttymode __P((const char *, int));
144 static void     setdefttymode __P((const char *));
145 static int      opentty __P((const char *, int));
146
147 int             main __P((int, char **));
148
149 jmp_buf timeout;
150
151 static void
152 dingdong(signo)
153         int signo;
154 {
155         alarm(0);
156         longjmp(timeout, 1);
157 }
158
159 jmp_buf intrupt;
160
161 static void
162 interrupt(signo)
163         int signo;
164 {
165         longjmp(intrupt, 1);
166 }
167
168 /*
169  * Action to take when getty is running too long.
170  */
171 static void
172 timeoverrun(signo)
173         int signo;
174 {
175
176         syslog(LOG_ERR, "getty exiting due to excessive running time");
177         exit(1);
178 }
179
180 int
181 main(argc, argv)
182         int argc;
183         char **argv;
184 {
185         extern  char **environ;
186         const char *tname;
187         int first_sleep = 1, first_time = 1;
188         struct rlimit limit;
189         int rval;
190
191         signal(SIGINT, SIG_IGN);
192         signal(SIGQUIT, SIG_IGN);
193
194         openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
195         gethostname(hostname, sizeof(hostname));
196         if (hostname[0] == '\0')
197                 strcpy(hostname, "Amnesiac");
198
199         /*
200          * Limit running time to deal with broken or dead lines.
201          */
202         (void)signal(SIGXCPU, timeoverrun);
203         limit.rlim_max = RLIM_INFINITY;
204         limit.rlim_cur = GETTY_TIMEOUT;
205         (void)setrlimit(RLIMIT_CPU, &limit);
206
207         gettable("default", defent);
208         gendefaults();
209         tname = "default";
210         if (argc > 1)
211                 tname = argv[1];
212
213         /*
214          * The following is a work around for vhangup interactions
215          * which cause great problems getting window systems started.
216          * If the tty line is "-", we do the old style getty presuming
217          * that the file descriptors are already set up for us.
218          * J. Gettys - MIT Project Athena.
219          */
220         if (argc <= 2 || strcmp(argv[2], "-") == 0)
221             strcpy(ttyn, ttyname(STDIN_FILENO));
222         else {
223             strcpy(ttyn, dev);
224             strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
225             if (strcmp(argv[0], "+") != 0) {
226                 chown(ttyn, 0, 0);
227                 chmod(ttyn, 0600);
228                 revoke(ttyn);
229
230                 gettable(tname, tabent);
231
232                 /* Init modem sequence has been specified
233                  */
234                 if (IC) {
235                         if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
236                                 exit(1);
237                         setdefttymode(tname);
238                         if (getty_chat(IC, CT, DC) > 0) {
239                                 syslog(LOG_ERR, "modem init problem on %s", ttyn);
240                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
241                                 exit(1);
242                         }
243                 }
244
245                 if (AC) {
246                         int i, rfds;
247                         struct timeval timeout;
248
249                         if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
250                                 exit(1);
251                         setdefttymode(tname);
252                         rfds = 1 << 0;  /* FD_SET */
253                         timeout.tv_sec = RT;
254                         timeout.tv_usec = 0;
255                         i = select(32, (fd_set*)&rfds, (fd_set*)NULL,
256                                        (fd_set*)NULL, RT ? &timeout : NULL);
257                         if (i < 0) {
258                                 syslog(LOG_ERR, "select %s: %m", ttyn);
259                         } else if (i == 0) {
260                                 syslog(LOG_NOTICE, "recycle tty %s", ttyn);
261                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
262                                 exit(0);  /* recycle for init */
263                         }
264                         i = getty_chat(AC, CT, DC);
265                         if (i > 0) {
266                                 syslog(LOG_ERR, "modem answer problem on %s", ttyn);
267                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
268                                 exit(1);
269                         }
270                 } else { /* blocking open */
271                         if (!opentty(ttyn, O_RDWR))
272                                 exit(1);
273                 }
274             }
275         }
276
277         /* Start with default tty settings */
278         if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
279                 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
280                 exit(1);
281         }
282         /*
283          * Don't rely on the driver too much, and initialize crucial
284          * things according to <sys/ttydefaults.h>.  Avoid clobbering
285          * the c_cc[] settings however, the console drivers might wish
286          * to leave their idea of the preferred VERASE key value
287          * there.
288          */
289         tmode.c_iflag = TTYDEF_IFLAG;
290         tmode.c_oflag = TTYDEF_OFLAG;
291         tmode.c_lflag = TTYDEF_LFLAG;
292         tmode.c_cflag = TTYDEF_CFLAG;
293         omode = tmode;
294
295         for (;;) {
296
297                 /*
298                  * if a delay was specified then sleep for that 
299                  * number of seconds before writing the initial prompt
300                  */
301                 if (first_sleep && DE) {
302                     sleep(DE);
303                     /* remove any noise */
304                     (void)tcflush(STDIN_FILENO, TCIOFLUSH);
305                 }
306                 first_sleep = 0;
307
308                 setttymode(tname, 0);
309                 if (AB) {
310                         tname = autobaud();
311                         continue;
312                 }
313                 if (PS) {
314                         tname = portselector();
315                         continue;
316                 }
317                 if (CL && *CL)
318                         putpad(CL);
319                 edithost(HE);
320
321                 /* if this is the first time through this, and an
322                    issue file has been given, then send it */
323                 if (first_time && IF) {
324                         int fd;
325
326                         if ((fd = open(IF, O_RDONLY)) != -1) {
327                                 char * cp;
328
329                                 while ((cp = getline(fd)) != NULL) {
330                                           putf(cp);
331                                 }
332                                 close(fd);
333                         }
334                 }
335                 first_time = 0;
336
337                 if (IM && *IM)
338                         putf(IM);
339                 if (setjmp(timeout)) {
340                         cfsetispeed(&tmode, B0);
341                         cfsetospeed(&tmode, B0);
342                         (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
343                         exit(1);
344                 }
345                 if (TO) {
346                         signal(SIGALRM, dingdong);
347                         alarm(TO);
348                 }
349                 if ((rval = getname()) == 2) {
350                         oflush();
351                         alarm(0);
352                         execle(PP, "ppplogin", ttyn, (char *) 0, env);
353                         syslog(LOG_ERR, "%s: %m", PP);
354                         exit(1);
355                 } else if (rval) {
356                         register int i;
357
358                         oflush();
359                         alarm(0);
360                         signal(SIGALRM, SIG_DFL);
361                         if (name[0] == '-') {
362                                 puts("user names may not start with '-'.");
363                                 continue;
364                         }
365                         if (!(upper || lower || digit))
366                                 continue;
367                         setflags(2);
368                         if (crmod) {
369                                 tmode.c_iflag |= ICRNL;
370                                 tmode.c_oflag |= ONLCR;
371                         }
372 #if REALLY_OLD_TTYS
373                         if (upper || UC)
374                                 tmode.sg_flags |= LCASE;
375                         if (lower || LC)
376                                 tmode.sg_flags &= ~LCASE;
377 #endif
378                         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
379                                 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
380                                 exit(1);
381                         }
382                         signal(SIGINT, SIG_DFL);
383                         for (i = 0; environ[i] != (char *)0; i++)
384                                 env[i] = environ[i];
385                         makeenv(&env[i]);
386
387                         limit.rlim_max = RLIM_INFINITY;
388                         limit.rlim_cur = RLIM_INFINITY;
389                         (void)setrlimit(RLIMIT_CPU, &limit);
390                         execle(LO, "login", "-p", name, (char *) 0, env);
391                         syslog(LOG_ERR, "%s: %m", LO);
392                         exit(1);
393                 }
394                 alarm(0);
395                 signal(SIGALRM, SIG_DFL);
396                 signal(SIGINT, SIG_IGN);
397                 if (NX && *NX)
398                         tname = NX;
399         }
400 }
401
402 static int
403 opentty(const char *ttyn, int flags)
404 {
405         int i, j = 0;
406         int failopenlogged = 0;
407
408         while (j < 10 && (i = open(ttyn, flags)) == -1)
409         {
410                 if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
411                         syslog(LOG_ERR, "open %s: %m", ttyn);
412                         failopenlogged = 1;
413                 }
414                 j++;
415                 sleep(60);
416         }
417         if (i == -1) {
418                 syslog(LOG_ERR, "open %s: %m", ttyn);
419                 return 0;
420         }
421         else {
422                 login_tty(i);
423                 return 1;
424         }
425 }
426
427 static void
428 setdefttymode(tname)
429         const char * tname;
430 {
431         if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
432                 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
433                 exit(1);
434         }
435         tmode.c_iflag = TTYDEF_IFLAG;
436         tmode.c_oflag = TTYDEF_OFLAG;
437         tmode.c_lflag = TTYDEF_LFLAG;
438         tmode.c_cflag = TTYDEF_CFLAG;
439         omode = tmode;
440         setttymode(tname, 1);
441 }
442
443 static void
444 setttymode(tname, raw)
445         const char * tname;
446         int raw;
447 {
448         int off = 0;
449
450         gettable(tname, tabent);
451         if (OPset || EPset || APset)
452                 APset++, OPset++, EPset++;
453         setdefaults();
454         (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */
455         ioctl(STDIN_FILENO, FIONBIO, &off);     /* turn off non-blocking mode */
456         ioctl(STDIN_FILENO, FIOASYNC, &off);    /* ditto for async mode */
457
458         if (IS)
459                 cfsetispeed(&tmode, speed(IS));
460         else if (SP)
461                 cfsetispeed(&tmode, speed(SP));
462         if (OS)
463                 cfsetospeed(&tmode, speed(OS));
464         else if (SP)
465                 cfsetospeed(&tmode, speed(SP));
466         setflags(0);
467         setchars();
468         if (raw)
469                 cfmakeraw(&tmode);
470         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
471                 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
472                 exit(1);
473         }
474 }
475
476
477 static int
478 getname()
479 {
480         register int c;
481         register char *np;
482         unsigned char cs;
483         int ppp_state = 0;
484         int ppp_connection = 0;
485
486         /*
487          * Interrupt may happen if we use CBREAK mode
488          */
489         if (setjmp(intrupt)) {
490                 signal(SIGINT, SIG_IGN);
491                 return (0);
492         }
493         signal(SIGINT, interrupt);
494         setflags(1);
495         prompt();
496         oflush();
497         if (PF > 0) {
498                 sleep(PF);
499                 PF = 0;
500         }
501         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
502                 syslog(LOG_ERR, "%s: %m", ttyn);
503                 exit(1);
504         }
505         crmod = digit = lower = upper = 0;
506         np = name;
507         for (;;) {
508                 oflush();
509                 if (read(STDIN_FILENO, &cs, 1) <= 0)
510                         exit(0);
511                 if ((c = cs&0177) == 0)
512                         return (0);
513
514                 /* PPP detection state machine..
515                    Look for sequences:
516                    PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
517                    PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
518                    See RFC1662.
519                    Derived from code from Michael Hancock, <michaelh@cet.co.jp>
520                    and Erik 'PPP' Olson, <eriko@wrq.com>
521                  */
522
523                 if (PP && (cs == PPP_FRAME)) {
524                         ppp_state = 1;
525                 } else if (ppp_state == 1 && cs == PPP_STATION) {
526                         ppp_state = 2;
527                 } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
528                         ppp_state = 3;
529                 } else if ((ppp_state == 2 && cs == PPP_CONTROL)
530                         || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
531                         ppp_state = 4;
532                 } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
533                         ppp_state = 5;
534                 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
535                         ppp_connection = 1;
536                         break;
537                 } else {
538                         ppp_state = 0;
539                 }
540
541                 if (c == EOT || c == CTRL('d'))
542                         exit(1);
543                 if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
544                         putf("\r\n");
545                         break;
546                 }
547                 if (islower(c))
548                         lower = 1;
549                 else if (isupper(c))
550                         upper = 1;
551                 else if (c == ERASE || c == '\b' || c == 0177) {
552                         if (np > name) {
553                                 np--;
554                                 if (cfgetospeed(&tmode) >= 1200)
555                                         puts("\b \b");
556                                 else
557                                         putchr(cs);
558                         }
559                         continue;
560                 } else if (c == KILL || c == CTRL('u')) {
561                         putchr('\r');
562                         if (cfgetospeed(&tmode) < 1200)
563                                 putchr('\n');
564                         /* this is the way they do it down under ... */
565                         else if (np > name)
566                                 puts("                                     \r");
567                         prompt();
568                         np = name;
569                         continue;
570                 } else if (isdigit(c))
571                         digit++;
572                 if (IG && (c <= ' ' || c > 0176))
573                         continue;
574                 *np++ = c;
575                 putchr(cs);
576         }
577         signal(SIGINT, SIG_IGN);
578         *np = 0;
579         if (c == '\r')
580                 crmod = 1;
581         if ((upper && !lower && !LC) || UC)
582                 for (np = name; *np; np++)
583                         if (isupper(*np))
584                                 *np = tolower(*np);
585         return (1 + ppp_connection);
586 }
587
588 static void
589 putpad(s)
590         register const char *s;
591 {
592         register pad = 0;
593         speed_t ospeed = cfgetospeed(&tmode);
594
595         if (isdigit(*s)) {
596                 while (isdigit(*s)) {
597                         pad *= 10;
598                         pad += *s++ - '0';
599                 }
600                 pad *= 10;
601                 if (*s == '.' && isdigit(s[1])) {
602                         pad += s[1] - '0';
603                         s += 2;
604                 }
605         }
606
607         puts(s);
608         /*
609          * If no delay needed, or output speed is
610          * not comprehensible, then don't try to delay.
611          */
612         if (pad == 0 || ospeed <= 0)
613                 return;
614
615         /*
616          * Round up by a half a character frame, and then do the delay.
617          * Too bad there are no user program accessible programmed delays.
618          * Transmitting pad characters slows many terminals down and also
619          * loads the system.
620          */
621         pad = (pad * ospeed + 50000) / 100000;
622         while (pad--)
623                 putchr(*PC);
624 }
625
626 static void
627 puts(s)
628         register const char *s;
629 {
630         while (*s)
631                 putchr(*s++);
632 }
633
634 char    outbuf[OBUFSIZ];
635 int     obufcnt = 0;
636
637 static void
638 putchr(cc)
639         int cc;
640 {
641         char c;
642
643         c = cc;
644         if (!NP) {
645                 c |= partab[c&0177] & 0200;
646                 if (OP)
647                         c ^= 0200;
648         }
649         if (!UB) {
650                 outbuf[obufcnt++] = c;
651                 if (obufcnt >= OBUFSIZ)
652                         oflush();
653         } else
654                 write(STDOUT_FILENO, &c, 1);
655 }
656
657 static void
658 oflush()
659 {
660         if (obufcnt)
661                 write(STDOUT_FILENO, outbuf, obufcnt);
662         obufcnt = 0;
663 }
664
665 static void
666 prompt()
667 {
668
669         putf(LM);
670         if (CO)
671                 putchr('\n');
672 }
673
674
675 static char *
676 getline(fd)
677         int fd;
678 {
679         int i = 0;
680         static char linebuf[512];
681
682         /*
683          * This is certainly slow, but it avoids having to include
684          * stdio.h unnecessarily. Issue files should be small anyway.
685          */
686         while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
687                 if (linebuf[i] == '\n') {
688                         /* Don't rely on newline mode, assume raw */
689                         linebuf[i++] = '\r';
690                         linebuf[i++] = '\n';
691                         linebuf[i] = '\0';
692                         return linebuf;
693                 }
694                 ++i;
695         }
696         linebuf[i] = '\0';
697         return i ? linebuf : 0;
698 }
699
700 static void
701 putf(cp)
702         register const char *cp;
703 {
704         extern char editedhost[];
705         time_t t;
706         char *slash, db[100];
707
708         static struct utsname kerninfo;
709
710         if (!*kerninfo.sysname)
711                 uname(&kerninfo);
712
713         while (*cp) {
714                 if (*cp != '%') {
715                         putchr(*cp++);
716                         continue;
717                 }
718                 switch (*++cp) {
719
720                 case 't':
721                         slash = strrchr(ttyn, '/');
722                         if (slash == (char *) 0)
723                                 puts(ttyn);
724                         else
725                                 puts(&slash[1]);
726                         break;
727
728                 case 'h':
729                         puts(editedhost);
730                         break;
731
732                 case 'd': {
733                         t = (time_t)0;
734                         (void)time(&t);
735                         if (Lo)
736                                 (void)setlocale(LC_TIME, Lo);
737                         (void)strftime(db, sizeof(db), "%+", localtime(&t));
738                         puts(db);
739                         break;
740
741                 case 's':
742                         puts(kerninfo.sysname);
743                         break;
744
745                 case 'm':
746                         puts(kerninfo.machine);
747                         break;
748
749                 case 'r':
750                         puts(kerninfo.release);
751                         break;
752
753                 case 'v':
754                         puts(kerninfo.version);
755                         break;
756                 }
757
758                 case '%':
759                         putchr('%');
760                         break;
761                 }
762                 cp++;
763         }
764 }