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