Merge branch 'vendor/GCC47'
[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  * 4. 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 int             main (int, char **);
135
136 jmp_buf timeout;
137
138 static void
139 dingdong(int signo)
140 {
141         alarm(0);
142         longjmp(timeout, 1);
143 }
144
145 jmp_buf intrupt;
146
147 static void
148 interrupt(int signo)
149 {
150         longjmp(intrupt, 1);
151 }
152
153 /*
154  * Action to take when getty is running too long.
155  */
156 static void
157 timeoverrun(int signo)
158 {
159         syslog(LOG_ERR, "getty exiting due to excessive running time");
160         exit(1);
161 }
162
163 int
164 main(int argc, char **argv)
165 {
166         extern  char **environ;
167         const char *tname;
168         int first_sleep = 1, first_time = 1;
169         struct rlimit limit;
170         int rval;
171
172         signal(SIGINT, SIG_IGN);
173         signal(SIGQUIT, SIG_IGN);
174
175         openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
176         gethostname(hostname, sizeof(hostname) - 1);
177         hostname[sizeof(hostname) - 1] = '\0';
178         if (hostname[0] == '\0')
179                 strcpy(hostname, "Amnesiac");
180
181         /*
182          * Limit running time to deal with broken or dead lines.
183          */
184         (void)signal(SIGXCPU, timeoverrun);
185         limit.rlim_max = RLIM_INFINITY;
186         limit.rlim_cur = GETTY_TIMEOUT;
187         (void)setrlimit(RLIMIT_CPU, &limit);
188
189         gettable("default", defent);
190         gendefaults();
191         tname = "default";
192         if (argc > 1)
193                 tname = argv[1];
194
195         /*
196          * The following is a work around for vhangup interactions
197          * which cause great problems getting window systems started.
198          * If the tty line is "-", we do the old style getty presuming
199          * that the file descriptors are already set up for us.
200          * J. Gettys - MIT Project Athena.
201          */
202         if (argc <= 2 || strcmp(argv[2], "-") == 0)
203             strcpy(ttyn, ttyname(STDIN_FILENO));
204         else {
205             strcpy(ttyn, dev);
206             strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
207             if (strcmp(argv[0], "+") != 0) {
208                 chown(ttyn, 0, 0);
209                 chmod(ttyn, 0600);
210                 revoke(ttyn);
211
212                 gettable(tname, tabent);
213
214                 /* Init modem sequence has been specified
215                  */
216                 if (IC) {
217                         if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
218                                 exit(1);
219                         setdefttymode(tname);
220                         if (getty_chat(IC, CT, DC) > 0) {
221                                 syslog(LOG_ERR, "modem init problem on %s", ttyn);
222                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
223                                 exit(1);
224                         }
225                 }
226
227                 if (AC) {
228                         int i, rfds;
229                         struct timeval timeout;
230
231                         if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
232                                 exit(1);
233                         setdefttymode(tname);
234                         rfds = 1 << 0;  /* FD_SET */
235                         timeout.tv_sec = RT;
236                         timeout.tv_usec = 0;
237                         i = select(32, (fd_set*)&rfds, NULL,
238                                        NULL, RT ? &timeout : NULL);
239                         if (i < 0) {
240                                 syslog(LOG_ERR, "select %s: %m", ttyn);
241                         } else if (i == 0) {
242                                 syslog(LOG_NOTICE, "recycle tty %s", ttyn);
243                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
244                                 exit(0);  /* recycle for init */
245                         }
246                         i = getty_chat(AC, CT, DC);
247                         if (i > 0) {
248                                 syslog(LOG_ERR, "modem answer problem on %s", ttyn);
249                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
250                                 exit(1);
251                         }
252                 } else { /* maybe blocking open */
253                         if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
254                                 exit(1);
255                 }
256             }
257         }
258
259         /* Start with default tty settings */
260         if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
261                 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
262                 exit(1);
263         }
264         /*
265          * Don't rely on the driver too much, and initialize crucial
266          * things according to <sys/ttydefaults.h>.  Avoid clobbering
267          * the c_cc[] settings however, the console drivers might wish
268          * to leave their idea of the preferred VERASE key value
269          * there.
270          */
271         tmode.c_iflag = TTYDEF_IFLAG;
272         tmode.c_oflag = TTYDEF_OFLAG;
273         tmode.c_lflag = TTYDEF_LFLAG;
274         tmode.c_cflag = TTYDEF_CFLAG;
275         tmode.c_cflag |= (NC ? CLOCAL : 0);
276         omode = tmode;
277
278         for (;;) {
279
280                 /*
281                  * if a delay was specified then sleep for that 
282                  * number of seconds before writing the initial prompt
283                  */
284                 if (first_sleep && DE) {
285                     sleep(DE);
286                     /* remove any noise */
287                     (void)tcflush(STDIN_FILENO, TCIOFLUSH);
288                 }
289                 first_sleep = 0;
290
291                 setttymode(tname, 0);
292                 if (AB) {
293                         tname = autobaud();
294                         continue;
295                 }
296                 if (PS) {
297                         tname = portselector();
298                         continue;
299                 }
300                 if (CL && *CL)
301                         putpad(CL);
302                 edithost(HE);
303
304                 /* if this is the first time through this, and an
305                    issue file has been given, then send it */
306                 if (first_time && IF) {
307                         int fd;
308
309                         if ((fd = open(IF, O_RDONLY)) != -1) {
310                                 char * cp;
311
312                                 while ((cp = getline(fd)) != NULL) {
313                                           putf(cp);
314                                 }
315                                 close(fd);
316                         }
317                 }
318                 first_time = 0;
319
320                 if (IM && *IM && !(PL && PP))
321                         putf(IM);
322                 if (setjmp(timeout)) {
323                         cfsetispeed(&tmode, B0);
324                         cfsetospeed(&tmode, B0);
325                         (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
326                         exit(1);
327                 }
328                 if (TO) {
329                         signal(SIGALRM, dingdong);
330                         alarm(TO);
331                 }
332                 if (AL) {
333                         const char *p = AL;
334                         char *q = name;
335
336                         while (*p && q < &name[sizeof name - 1]) {
337                                 if (isupper(*p))
338                                         upper = 1;
339                                 else if (islower(*p))
340                                         lower = 1;
341                                 else if (isdigit(*p))
342                                         digit++;
343                                 *q++ = *p++;
344                         }
345                 } else if (!(PL && PP))
346                         rval = getname();
347                 if (rval == 2 || (PL && PP)) {
348                         oflush();
349                         alarm(0);
350                         limit.rlim_max = RLIM_INFINITY;
351                         limit.rlim_cur = RLIM_INFINITY;
352                         (void)setrlimit(RLIMIT_CPU, &limit);
353                         execle(PP, "ppplogin", ttyn, NULL, env);
354                         syslog(LOG_ERR, "%s: %m", PP);
355                         exit(1);
356                 } else if (rval || AL) {
357                         int i;
358
359                         oflush();
360                         alarm(0);
361                         signal(SIGALRM, SIG_DFL);
362                         if (name[0] == '-') {
363                                 puts("user names may not start with '-'.");
364                                 continue;
365                         }
366                         if (!(upper || lower || digit))
367                                 continue;
368                         set_flags(2);
369                         if (crmod) {
370                                 tmode.c_iflag |= ICRNL;
371                                 tmode.c_oflag |= ONLCR;
372                         }
373 #if REALLY_OLD_TTYS
374                         if (upper || UC)
375                                 tmode.sg_flags |= LCASE;
376                         if (lower || LC)
377                                 tmode.sg_flags &= ~LCASE;
378 #endif
379                         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
380                                 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
381                                 exit(1);
382                         }
383                         signal(SIGINT, SIG_DFL);
384                         for (i = 0; environ[i] != NULL; i++)
385                                 env[i] = environ[i];
386                         makeenv(&env[i]);
387
388                         limit.rlim_max = RLIM_INFINITY;
389                         limit.rlim_cur = RLIM_INFINITY;
390                         (void)setrlimit(RLIMIT_CPU, &limit);
391                         execle(LO, "login", AL ? "-fp" : "-p", name,
392                             NULL, env);
393                         syslog(LOG_ERR, "%s: %m", LO);
394                         exit(1);
395                 }
396                 alarm(0);
397                 signal(SIGALRM, SIG_DFL);
398                 signal(SIGINT, SIG_IGN);
399                 if (NX && *NX)
400                         tname = NX;
401         }
402 }
403
404 static int
405 opentty(const char *ttyn, int flags)
406 {
407         int i, j = 0;
408         int failopenlogged = 0;
409
410         while (j < 10 && (i = open(ttyn, flags)) == -1)
411         {
412                 if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
413                         syslog(LOG_ERR, "open %s: %m", ttyn);
414                         failopenlogged = 1;
415                 }
416                 j++;
417                 sleep(60);
418         }
419         if (i == -1) {
420                 syslog(LOG_ERR, "open %s: %m", ttyn);
421                 return 0;
422         }
423         else {
424                 if (login_tty(i) < 0) { 
425                         if (daemon(0,0) < 0) {
426                                 syslog(LOG_ERR,"daemon: %m");
427                                 close(i);
428                                 return 0;
429                         }
430                         if (login_tty(i) < 0) {
431                                 syslog(LOG_ERR, "login_tty %s: %m", ttyn);
432                                 close(i);
433                                 return 0;
434                         }
435                 }
436                 return 1;
437         }
438 }
439
440 static void
441 setdefttymode(const char *tname)
442 {
443         if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
444                 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
445                 exit(1);
446         }
447         tmode.c_iflag = TTYDEF_IFLAG;
448         tmode.c_oflag = TTYDEF_OFLAG;
449         tmode.c_lflag = TTYDEF_LFLAG;
450         tmode.c_cflag = TTYDEF_CFLAG;
451         omode = tmode;
452         setttymode(tname, 1);
453 }
454
455 static void
456 setttymode(const char *tname, int raw)
457 {
458         int off = 0;
459
460         gettable(tname, tabent);
461         if (OPset || EPset || APset)
462                 APset++, OPset++, EPset++;
463         setdefaults();
464         (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */
465         ioctl(STDIN_FILENO, FIONBIO, &off);     /* turn off non-blocking mode */
466         ioctl(STDIN_FILENO, FIOASYNC, &off);    /* ditto for async mode */
467
468         if (IS)
469                 cfsetispeed(&tmode, speed(IS));
470         else if (SP)
471                 cfsetispeed(&tmode, speed(SP));
472         if (OS)
473                 cfsetospeed(&tmode, speed(OS));
474         else if (SP)
475                 cfsetospeed(&tmode, speed(SP));
476         set_flags(0);
477         setchars();
478         if (raw)
479                 cfmakeraw(&tmode);
480         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
481                 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
482                 exit(1);
483         }
484 }
485
486
487 static int
488 getname(void)
489 {
490         int c;
491         char *np;
492         unsigned char cs;
493         int ppp_state = 0;
494         int ppp_connection = 0;
495
496         /*
497          * Interrupt may happen if we use CBREAK mode
498          */
499         if (setjmp(intrupt)) {
500                 signal(SIGINT, SIG_IGN);
501                 return (0);
502         }
503         signal(SIGINT, interrupt);
504         set_flags(1);
505         prompt();
506         oflush();
507         if (PF > 0) {
508                 sleep(PF);
509                 PF = 0;
510         }
511         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
512                 syslog(LOG_ERR, "%s: %m", ttyn);
513                 exit(1);
514         }
515         crmod = digit = lower = upper = 0;
516         np = name;
517         for (;;) {
518                 oflush();
519                 if (read(STDIN_FILENO, &cs, 1) <= 0)
520                         exit(0);
521                 if ((c = cs&0177) == 0)
522                         return (0);
523
524                 /* PPP detection state machine..
525                    Look for sequences:
526                    PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
527                    PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
528                    See RFC1662.
529                    Derived from code from Michael Hancock, <michaelh@cet.co.jp>
530                    and Erik 'PPP' Olson, <eriko@wrq.com>
531                  */
532
533                 if (PP && (cs == PPP_FRAME)) {
534                         ppp_state = 1;
535                 } else if (ppp_state == 1 && cs == PPP_STATION) {
536                         ppp_state = 2;
537                 } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
538                         ppp_state = 3;
539                 } else if ((ppp_state == 2 && cs == PPP_CONTROL)
540                         || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
541                         ppp_state = 4;
542                 } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
543                         ppp_state = 5;
544                 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
545                         ppp_connection = 1;
546                         break;
547                 } else {
548                         ppp_state = 0;
549                 }
550
551                 if (c == EOT || c == CTRL('d'))
552                         exit(1);
553                 if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
554                         putf("\r\n");
555                         break;
556                 }
557                 if (islower(c))
558                         lower = 1;
559                 else if (isupper(c))
560                         upper = 1;
561                 else if (c == ERASE || c == '\b' || c == 0177) {
562                         if (np > name) {
563                                 np--;
564                                 if (cfgetospeed(&tmode) >= 1200)
565                                         puts("\b \b");
566                                 else
567                                         putchr(cs);
568                         }
569                         continue;
570                 } else if (c == KILL || c == CTRL('u')) {
571                         putchr('\r');
572                         if (cfgetospeed(&tmode) < 1200)
573                                 putchr('\n');
574                         /* this is the way they do it down under ... */
575                         else if (np > name)
576                                 puts("                                     \r");
577                         prompt();
578                         np = name;
579                         continue;
580                 } else if (isdigit(c))
581                         digit++;
582                 if (IG && (c <= ' ' || c > 0176))
583                         continue;
584                 *np++ = c;
585                 putchr(cs);
586         }
587         signal(SIGINT, SIG_IGN);
588         *np = 0;
589         if (c == '\r')
590                 crmod = 1;
591         if ((upper && !lower && !LC) || UC)
592                 for (np = name; *np; np++)
593                         if (isupper(*np))
594                                 *np = tolower(*np);
595         return (1 + ppp_connection);
596 }
597
598 static void
599 putpad(const char *s)
600 {
601         int pad = 0;
602         speed_t ospeed;
603
604         ospeed = cfgetospeed(&tmode);
605
606         if (isdigit(*s)) {
607                 while (isdigit(*s)) {
608                         pad *= 10;
609                         pad += *s++ - '0';
610                 }
611                 pad *= 10;
612                 if (*s == '.' && isdigit(s[1])) {
613                         pad += s[1] - '0';
614                         s += 2;
615                 }
616         }
617
618         puts(s);
619         /*
620          * If no delay needed, or output speed is
621          * not comprehensible, then don't try to delay.
622          */
623         if (pad == 0 || ospeed <= 0)
624                 return;
625
626         /*
627          * Round up by a half a character frame, and then do the delay.
628          * Too bad there are no user program accessible programmed delays.
629          * Transmitting pad characters slows many terminals down and also
630          * loads the system.
631          */
632         pad = (pad * ospeed + 50000) / 100000;
633         while (pad--)
634                 putchr(*PC);
635 }
636
637 static void
638 puts(const char *s)
639 {
640         while (*s)
641                 putchr(*s++);
642 }
643
644 char    outbuf[OBUFSIZ];
645 int     obufcnt = 0;
646
647 static void
648 putchr(int cc)
649 {
650         char c;
651
652         c = cc;
653         if (!NP) {
654                 c |= partab[c&0177] & 0200;
655                 if (OP)
656                         c ^= 0200;
657         }
658         if (!UB) {
659                 outbuf[obufcnt++] = c;
660                 if (obufcnt >= OBUFSIZ)
661                         oflush();
662         } else
663                 write(STDOUT_FILENO, &c, 1);
664 }
665
666 static void
667 oflush(void)
668 {
669         if (obufcnt)
670                 write(STDOUT_FILENO, outbuf, obufcnt);
671         obufcnt = 0;
672 }
673
674 static void
675 prompt(void)
676 {
677         putf(LM);
678         if (CO)
679                 putchr('\n');
680 }
681
682
683 static char *
684 getline(int fd)
685 {
686         int i = 0;
687         static char linebuf[512];
688
689         /*
690          * This is certainly slow, but it avoids having to include
691          * stdio.h unnecessarily. Issue files should be small anyway.
692          */
693         while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
694                 if (linebuf[i] == '\n') {
695                         /* Don't rely on newline mode, assume raw */
696                         linebuf[i++] = '\r';
697                         linebuf[i++] = '\n';
698                         linebuf[i] = '\0';
699                         return linebuf;
700                 }
701                 ++i;
702         }
703         linebuf[i] = '\0';
704         return i ? linebuf : 0;
705 }
706
707 static void
708 putf(const char *cp)
709 {
710         extern char editedhost[];
711         time_t t;
712         char *slash, db[100];
713
714         static struct utsname kerninfo;
715
716         if (!*kerninfo.sysname)
717                 uname(&kerninfo);
718
719         while (*cp) {
720                 if (*cp != '%') {
721                         putchr(*cp++);
722                         continue;
723                 }
724                 switch (*++cp) {
725
726                 case 't':
727                         slash = strrchr(ttyn, '/');
728                         if (slash == NULL)
729                                 puts(ttyn);
730                         else
731                                 puts(&slash[1]);
732                         break;
733
734                 case 'h':
735                         puts(editedhost);
736                         break;
737
738                 case 'd': {
739                         t = (time_t)0;
740                         (void)time(&t);
741                         if (Lo)
742                                 (void)setlocale(LC_TIME, Lo);
743                         (void)strftime(db, sizeof(db), DF, localtime(&t));
744                         puts(db);
745                         break;
746
747                 case 's':
748                         puts(kerninfo.sysname);
749                         break;
750
751                 case 'm':
752                         puts(kerninfo.machine);
753                         break;
754
755                 case 'r':
756                         puts(kerninfo.release);
757                         break;
758
759                 case 'v':
760                         puts(kerninfo.version);
761                         break;
762                 }
763
764                 case '%':
765                         putchr('%');
766                         break;
767                 }
768                 cp++;
769         }
770 }