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