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