cd77f4cb8143777de30d94d39215fdc9236b4fae
[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, (fd_set*)NULL,
244                                        (fd_set*)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                         int n = sizeof name;
342
343                         while (*p && q < &name[sizeof name - 1]) {
344                                 if (isupper(*p))
345                                         upper = 1;
346                                 else if (islower(*p))
347                                         lower = 1;
348                                 else if (isdigit(*p))
349                                         digit++;
350                                 *q++ = *p++;
351                         }
352                 } else if (!(PL && PP))
353                         rval = getname();
354                 if (rval == 2 || (PL && PP)) {
355                         oflush();
356                         alarm(0);
357                         limit.rlim_max = RLIM_INFINITY;
358                         limit.rlim_cur = RLIM_INFINITY;
359                         (void)setrlimit(RLIMIT_CPU, &limit);
360                         execle(PP, "ppplogin", ttyn, (char *) 0, env);
361                         syslog(LOG_ERR, "%s: %m", PP);
362                         exit(1);
363                 } else if (rval || AL) {
364                         int i;
365
366                         oflush();
367                         alarm(0);
368                         signal(SIGALRM, SIG_DFL);
369                         if (name[0] == '-') {
370                                 puts("user names may not start with '-'.");
371                                 continue;
372                         }
373                         if (!(upper || lower || digit))
374                                 continue;
375                         set_flags(2);
376                         if (crmod) {
377                                 tmode.c_iflag |= ICRNL;
378                                 tmode.c_oflag |= ONLCR;
379                         }
380 #if REALLY_OLD_TTYS
381                         if (upper || UC)
382                                 tmode.sg_flags |= LCASE;
383                         if (lower || LC)
384                                 tmode.sg_flags &= ~LCASE;
385 #endif
386                         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
387                                 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
388                                 exit(1);
389                         }
390                         signal(SIGINT, SIG_DFL);
391                         for (i = 0; environ[i] != NULL; i++)
392                                 env[i] = environ[i];
393                         makeenv(&env[i]);
394
395                         limit.rlim_max = RLIM_INFINITY;
396                         limit.rlim_cur = RLIM_INFINITY;
397                         (void)setrlimit(RLIMIT_CPU, &limit);
398                         execle(LO, "login", AL ? "-fp" : "-p", name,
399                             (char *) 0, env);
400                         syslog(LOG_ERR, "%s: %m", LO);
401                         exit(1);
402                 }
403                 alarm(0);
404                 signal(SIGALRM, SIG_DFL);
405                 signal(SIGINT, SIG_IGN);
406                 if (NX && *NX)
407                         tname = NX;
408         }
409 }
410
411 static int
412 opentty(const char *ttyn, int flags)
413 {
414         int i, j = 0;
415         int failopenlogged = 0;
416
417         while (j < 10 && (i = open(ttyn, flags)) == -1)
418         {
419                 if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
420                         syslog(LOG_ERR, "open %s: %m", ttyn);
421                         failopenlogged = 1;
422                 }
423                 j++;
424                 sleep(60);
425         }
426         if (i == -1) {
427                 syslog(LOG_ERR, "open %s: %m", ttyn);
428                 return 0;
429         }
430         else {
431                 if (login_tty(i) < 0) { 
432                         if (daemon(0,0) < 0) {
433                                 syslog(LOG_ERR,"daemon: %m");
434                                 close(i);
435                                 return 0;
436                         }
437                         if (login_tty(i) < 0) {
438                                 syslog(LOG_ERR, "login_tty %s: %m", ttyn);
439                                 close(i);
440                                 return 0;
441                         }
442                 }
443                 return 1;
444         }
445 }
446
447 static void
448 setdefttymode(const char *tname)
449 {
450         if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
451                 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
452                 exit(1);
453         }
454         tmode.c_iflag = TTYDEF_IFLAG;
455         tmode.c_oflag = TTYDEF_OFLAG;
456         tmode.c_lflag = TTYDEF_LFLAG;
457         tmode.c_cflag = TTYDEF_CFLAG;
458         omode = tmode;
459         setttymode(tname, 1);
460 }
461
462 static void
463 setttymode(const char *tname, int raw)
464 {
465         int off = 0;
466
467         gettable(tname, tabent);
468         if (OPset || EPset || APset)
469                 APset++, OPset++, EPset++;
470         setdefaults();
471         (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */
472         ioctl(STDIN_FILENO, FIONBIO, &off);     /* turn off non-blocking mode */
473         ioctl(STDIN_FILENO, FIOASYNC, &off);    /* ditto for async mode */
474
475         if (IS)
476                 cfsetispeed(&tmode, speed(IS));
477         else if (SP)
478                 cfsetispeed(&tmode, speed(SP));
479         if (OS)
480                 cfsetospeed(&tmode, speed(OS));
481         else if (SP)
482                 cfsetospeed(&tmode, speed(SP));
483         set_flags(0);
484         setchars();
485         if (raw)
486                 cfmakeraw(&tmode);
487         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
488                 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
489                 exit(1);
490         }
491 }
492
493
494 static int
495 getname(void)
496 {
497         int c;
498         char *np;
499         unsigned char cs;
500         int ppp_state = 0;
501         int ppp_connection = 0;
502
503         /*
504          * Interrupt may happen if we use CBREAK mode
505          */
506         if (setjmp(intrupt)) {
507                 signal(SIGINT, SIG_IGN);
508                 return (0);
509         }
510         signal(SIGINT, interrupt);
511         set_flags(1);
512         prompt();
513         oflush();
514         if (PF > 0) {
515                 sleep(PF);
516                 PF = 0;
517         }
518         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
519                 syslog(LOG_ERR, "%s: %m", ttyn);
520                 exit(1);
521         }
522         crmod = digit = lower = upper = 0;
523         np = name;
524         for (;;) {
525                 oflush();
526                 if (read(STDIN_FILENO, &cs, 1) <= 0)
527                         exit(0);
528                 if ((c = cs&0177) == 0)
529                         return (0);
530
531                 /* PPP detection state machine..
532                    Look for sequences:
533                    PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
534                    PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
535                    See RFC1662.
536                    Derived from code from Michael Hancock, <michaelh@cet.co.jp>
537                    and Erik 'PPP' Olson, <eriko@wrq.com>
538                  */
539
540                 if (PP && (cs == PPP_FRAME)) {
541                         ppp_state = 1;
542                 } else if (ppp_state == 1 && cs == PPP_STATION) {
543                         ppp_state = 2;
544                 } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
545                         ppp_state = 3;
546                 } else if ((ppp_state == 2 && cs == PPP_CONTROL)
547                         || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
548                         ppp_state = 4;
549                 } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
550                         ppp_state = 5;
551                 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
552                         ppp_connection = 1;
553                         break;
554                 } else {
555                         ppp_state = 0;
556                 }
557
558                 if (c == EOT || c == CTRL('d'))
559                         exit(1);
560                 if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
561                         putf("\r\n");
562                         break;
563                 }
564                 if (islower(c))
565                         lower = 1;
566                 else if (isupper(c))
567                         upper = 1;
568                 else if (c == ERASE || c == '\b' || c == 0177) {
569                         if (np > name) {
570                                 np--;
571                                 if (cfgetospeed(&tmode) >= 1200)
572                                         puts("\b \b");
573                                 else
574                                         putchr(cs);
575                         }
576                         continue;
577                 } else if (c == KILL || c == CTRL('u')) {
578                         putchr('\r');
579                         if (cfgetospeed(&tmode) < 1200)
580                                 putchr('\n');
581                         /* this is the way they do it down under ... */
582                         else if (np > name)
583                                 puts("                                     \r");
584                         prompt();
585                         np = name;
586                         continue;
587                 } else if (isdigit(c))
588                         digit++;
589                 if (IG && (c <= ' ' || c > 0176))
590                         continue;
591                 *np++ = c;
592                 putchr(cs);
593         }
594         signal(SIGINT, SIG_IGN);
595         *np = 0;
596         if (c == '\r')
597                 crmod = 1;
598         if ((upper && !lower && !LC) || UC)
599                 for (np = name; *np; np++)
600                         if (isupper(*np))
601                                 *np = tolower(*np);
602         return (1 + ppp_connection);
603 }
604
605 static void
606 putpad(const char *s)
607 {
608         int pad = 0;
609         speed_t ospeed;
610
611         ospeed = cfgetospeed(&tmode);
612
613         if (isdigit(*s)) {
614                 while (isdigit(*s)) {
615                         pad *= 10;
616                         pad += *s++ - '0';
617                 }
618                 pad *= 10;
619                 if (*s == '.' && isdigit(s[1])) {
620                         pad += s[1] - '0';
621                         s += 2;
622                 }
623         }
624
625         puts(s);
626         /*
627          * If no delay needed, or output speed is
628          * not comprehensible, then don't try to delay.
629          */
630         if (pad == 0 || ospeed <= 0)
631                 return;
632
633         /*
634          * Round up by a half a character frame, and then do the delay.
635          * Too bad there are no user program accessible programmed delays.
636          * Transmitting pad characters slows many terminals down and also
637          * loads the system.
638          */
639         pad = (pad * ospeed + 50000) / 100000;
640         while (pad--)
641                 putchr(*PC);
642 }
643
644 static void
645 puts(const char *s)
646 {
647         while (*s)
648                 putchr(*s++);
649 }
650
651 char    outbuf[OBUFSIZ];
652 int     obufcnt = 0;
653
654 static void
655 putchr(int cc)
656 {
657         char c;
658
659         c = cc;
660         if (!NP) {
661                 c |= partab[c&0177] & 0200;
662                 if (OP)
663                         c ^= 0200;
664         }
665         if (!UB) {
666                 outbuf[obufcnt++] = c;
667                 if (obufcnt >= OBUFSIZ)
668                         oflush();
669         } else
670                 write(STDOUT_FILENO, &c, 1);
671 }
672
673 static void
674 oflush(void)
675 {
676         if (obufcnt)
677                 write(STDOUT_FILENO, outbuf, obufcnt);
678         obufcnt = 0;
679 }
680
681 static void
682 prompt(void)
683 {
684         putf(LM);
685         if (CO)
686                 putchr('\n');
687 }
688
689
690 static char *
691 getline(int fd)
692 {
693         int i = 0;
694         static char linebuf[512];
695
696         /*
697          * This is certainly slow, but it avoids having to include
698          * stdio.h unnecessarily. Issue files should be small anyway.
699          */
700         while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
701                 if (linebuf[i] == '\n') {
702                         /* Don't rely on newline mode, assume raw */
703                         linebuf[i++] = '\r';
704                         linebuf[i++] = '\n';
705                         linebuf[i] = '\0';
706                         return linebuf;
707                 }
708                 ++i;
709         }
710         linebuf[i] = '\0';
711         return i ? linebuf : 0;
712 }
713
714 static void
715 putf(const char *cp)
716 {
717         extern char editedhost[];
718         time_t t;
719         char *slash, db[100];
720
721         static struct utsname kerninfo;
722
723         if (!*kerninfo.sysname)
724                 uname(&kerninfo);
725
726         while (*cp) {
727                 if (*cp != '%') {
728                         putchr(*cp++);
729                         continue;
730                 }
731                 switch (*++cp) {
732
733                 case 't':
734                         slash = strrchr(ttyn, '/');
735                         if (slash == (char *) 0)
736                                 puts(ttyn);
737                         else
738                                 puts(&slash[1]);
739                         break;
740
741                 case 'h':
742                         puts(editedhost);
743                         break;
744
745                 case 'd': {
746                         t = (time_t)0;
747                         (void)time(&t);
748                         if (Lo)
749                                 (void)setlocale(LC_TIME, Lo);
750                         (void)strftime(db, sizeof(db), DF, localtime(&t));
751                         puts(db);
752                         break;
753
754                 case 's':
755                         puts(kerninfo.sysname);
756                         break;
757
758                 case 'm':
759                         puts(kerninfo.machine);
760                         break;
761
762                 case 'r':
763                         puts(kerninfo.release);
764                         break;
765
766                 case 'v':
767                         puts(kerninfo.version);
768                         break;
769                 }
770
771                 case '%':
772                         putchr('%');
773                         break;
774                 }
775                 cp++;
776         }
777 }