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