]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libexec/getty/main.c
Fix privilege escalation in cd(4) driver.
[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. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #ifndef lint
31 static const char copyright[] =
32 "@(#) Copyright (c) 1980, 1993\n\
33         The Regents of the University of California.  All rights reserved.\n";
34 #endif /* not lint */
35
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)from: main.c        8.1 (Berkeley) 6/20/93";
39 #endif
40 #endif /* not lint */
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43
44 #include <sys/param.h>
45 #include <sys/ioctl.h>
46 #include <sys/time.h>
47 #include <sys/resource.h>
48 #include <sys/stat.h>
49 #include <sys/ttydefaults.h>
50 #include <sys/utsname.h>
51
52 #include <ctype.h>
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <locale.h>
56 #include <libutil.h>
57 #include <setjmp.h>
58 #include <signal.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <syslog.h>
62 #include <termios.h>
63 #include <time.h>
64 #include <unistd.h>
65
66 #include "gettytab.h"
67 #include "extern.h"
68 #include "pathnames.h"
69
70 /*
71  * Set the amount of running time that getty should accumulate
72  * before deciding that something is wrong and exit.
73  */
74 #define GETTY_TIMEOUT   60 /* seconds */
75
76 #undef CTRL
77 #define CTRL(x)  (x&037)
78
79 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
80
81 #define PPP_FRAME           0x7e  /* PPP Framing character */
82 #define PPP_STATION         0xff  /* "All Station" character */
83 #define PPP_ESCAPE          0x7d  /* Escape Character */
84 #define PPP_CONTROL         0x03  /* PPP Control Field */
85 #define PPP_CONTROL_ESCAPED 0x23  /* PPP Control Field, escaped */
86 #define PPP_LCP_HI          0xc0  /* LCP protocol - high byte */
87 #define PPP_LCP_LOW         0x21  /* LCP protocol - low byte */
88
89 /* original mode; flags've been reset using values from <sys/ttydefaults.h> */
90 struct termios omode;
91 /* current mode */
92 struct termios tmode;
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 const   char *tname;
107
108 char    *env[128];
109
110 char partab[] = {
111         0001,0201,0201,0001,0201,0001,0001,0201,
112         0202,0004,0003,0205,0005,0206,0201,0001,
113         0201,0001,0001,0201,0001,0201,0201,0001,
114         0001,0201,0201,0001,0201,0001,0001,0201,
115         0200,0000,0000,0200,0000,0200,0200,0000,
116         0000,0200,0200,0000,0200,0000,0000,0200,
117         0000,0200,0200,0000,0200,0000,0000,0200,
118         0200,0000,0000,0200,0000,0200,0200,0000,
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         0000,0200,0200,0000,0200,0000,0000,0200,
124         0200,0000,0000,0200,0000,0200,0200,0000,
125         0200,0000,0000,0200,0000,0200,0200,0000,
126         0000,0200,0200,0000,0200,0000,0000,0201
127 };
128
129 #define ERASE   tmode.c_cc[VERASE]
130 #define KILL    tmode.c_cc[VKILL]
131 #define EOT     tmode.c_cc[VEOF]
132
133 #define puts    Gputs
134
135 static void     defttymode(void);
136 static void     dingdong(int);
137 static void     dogettytab(void);
138 static int      getname(void);
139 static void     interrupt(int);
140 static void     oflush(void);
141 static void     prompt(void);
142 static void     putchr(int);
143 static void     putf(const char *);
144 static void     putpad(const char *);
145 static void     puts(const char *);
146 static void     timeoverrun(int);
147 static char     *getline(int);
148 static void     setttymode(int);
149 static int      opentty(const char *, int);
150
151 jmp_buf timeout;
152
153 static void
154 dingdong(int signo __unused)
155 {
156         alarm(0);
157         longjmp(timeout, 1);
158 }
159
160 jmp_buf intrupt;
161
162 static void
163 interrupt(int signo __unused)
164 {
165         longjmp(intrupt, 1);
166 }
167
168 /*
169  * Action to take when getty is running too long.
170  */
171 static void
172 timeoverrun(int signo __unused)
173 {
174
175         syslog(LOG_ERR, "getty exiting due to excessive running time");
176         exit(1);
177 }
178
179 int
180 main(int argc, char *argv[])
181 {
182         extern  char **environ;
183         int first_sleep = 1, first_time = 1;
184         struct rlimit limit;
185         int rval;
186
187         signal(SIGINT, SIG_IGN);
188         signal(SIGQUIT, SIG_IGN);
189
190         openlog("getty", LOG_CONS|LOG_PID, LOG_AUTH);
191         gethostname(hostname, sizeof(hostname) - 1);
192         hostname[sizeof(hostname) - 1] = '\0';
193         if (hostname[0] == '\0')
194                 strcpy(hostname, "Amnesiac");
195
196         /*
197          * Limit running time to deal with broken or dead lines.
198          */
199         (void)signal(SIGXCPU, timeoverrun);
200         limit.rlim_max = RLIM_INFINITY;
201         limit.rlim_cur = GETTY_TIMEOUT;
202         (void)setrlimit(RLIMIT_CPU, &limit);
203
204         gettable("default", defent);
205         gendefaults();
206         tname = "default";
207         if (argc > 1)
208                 tname = argv[1];
209
210         /*
211          * The following is a work around for vhangup interactions
212          * which cause great problems getting window systems started.
213          * If the tty line is "-", we do the old style getty presuming
214          * that the file descriptors are already set up for us.
215          * J. Gettys - MIT Project Athena.
216          */
217         if (argc <= 2 || strcmp(argv[2], "-") == 0)
218             strcpy(ttyn, ttyname(STDIN_FILENO));
219         else {
220             strcpy(ttyn, dev);
221             strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
222             if (strcmp(argv[0], "+") != 0) {
223                 chown(ttyn, 0, 0);
224                 chmod(ttyn, 0600);
225                 revoke(ttyn);
226
227                 /*
228                  * Do the first scan through gettytab.
229                  * Terminal mode parameters will be wrong until
230                  * defttymode() called, but they're irrelevant for
231                  * the initial setup of the terminal device.
232                  */
233                 dogettytab();
234
235                 /*
236                  * Init or answer modem sequence has been specified.
237                  */
238                 if (IC || AC) {
239                         if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
240                                 exit(1);
241                         defttymode();
242                         setttymode(1);
243                 }
244
245                 if (IC) {
246                         if (getty_chat(IC, CT, DC) > 0) {
247                                 syslog(LOG_ERR, "modem init problem on %s", ttyn);
248                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
249                                 exit(1);
250                         }
251                 }
252
253                 if (AC) {
254                         int i, rfds;
255                         struct timeval to;
256
257                         rfds = 1 << 0;  /* FD_SET */
258                         to.tv_sec = RT;
259                         to.tv_usec = 0;
260                         i = select(32, (fd_set*)&rfds, (fd_set*)NULL,
261                                        (fd_set*)NULL, RT ? &to : NULL);
262                         if (i < 0) {
263                                 syslog(LOG_ERR, "select %s: %m", ttyn);
264                         } else if (i == 0) {
265                                 syslog(LOG_NOTICE, "recycle tty %s", ttyn);
266                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
267                                 exit(0);  /* recycle for init */
268                         }
269                         i = getty_chat(AC, CT, DC);
270                         if (i > 0) {
271                                 syslog(LOG_ERR, "modem answer problem on %s", ttyn);
272                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
273                                 exit(1);
274                         }
275                 } else { /* maybe blocking open */
276                         if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
277                                 exit(1);
278                 }
279             }
280         }
281
282         defttymode();
283         for (;;) {
284
285                 /*
286                  * if a delay was specified then sleep for that 
287                  * number of seconds before writing the initial prompt
288                  */
289                 if (first_sleep && DE) {
290                     sleep(DE);
291                     /* remove any noise */
292                     (void)tcflush(STDIN_FILENO, TCIOFLUSH);
293                 }
294                 first_sleep = 0;
295
296                 setttymode(0);
297                 if (AB) {
298                         tname = autobaud();
299                         dogettytab();
300                         continue;
301                 }
302                 if (PS) {
303                         tname = portselector();
304                         dogettytab();
305                         continue;
306                 }
307                 if (CL && *CL)
308                         putpad(CL);
309                 edithost(HE);
310
311                 /* if this is the first time through this, and an
312                    issue file has been given, then send it */
313                 if (first_time && IF) {
314                         int fd;
315
316                         if ((fd = open(IF, O_RDONLY)) != -1) {
317                                 char * cp;
318
319                                 while ((cp = getline(fd)) != NULL) {
320                                           putf(cp);
321                                 }
322                                 close(fd);
323                         }
324                 }
325                 first_time = 0;
326
327                 if (IMP && *IMP && !(PL && PP))
328                         system(IMP);
329                 if (IM && *IM && !(PL && PP))
330                         putf(IM);
331                 if (setjmp(timeout)) {
332                         cfsetispeed(&tmode, B0);
333                         cfsetospeed(&tmode, B0);
334                         (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
335                         exit(1);
336                 }
337                 if (TO) {
338                         signal(SIGALRM, dingdong);
339                         alarm(TO);
340                 }
341
342                 rval = 0;
343                 if (AL) {
344                         const char *p = AL;
345                         char *q = name;
346
347                         while (*p && q < &name[sizeof name - 1]) {
348                                 if (isupper(*p))
349                                         upper = 1;
350                                 else if (islower(*p))
351                                         lower = 1;
352                                 else if (isdigit(*p))
353                                         digit = 1;
354                                 *q++ = *p++;
355                         }
356                 } else if (!(PL && PP))
357                         rval = getname();
358                 if (rval == 2 || (PL && PP)) {
359                         oflush();
360                         alarm(0);
361                         limit.rlim_max = RLIM_INFINITY;
362                         limit.rlim_cur = RLIM_INFINITY;
363                         (void)setrlimit(RLIMIT_CPU, &limit);
364                         execle(PP, "ppplogin", ttyn, (char *) 0, env);
365                         syslog(LOG_ERR, "%s: %m", PP);
366                         exit(1);
367                 } else if (rval || AL) {
368                         int i;
369
370                         oflush();
371                         alarm(0);
372                         signal(SIGALRM, SIG_DFL);
373                         if (name[0] == '\0')
374                                 continue;
375                         if (name[0] == '-') {
376                                 puts("user names may not start with '-'.");
377                                 continue;
378                         }
379                         if (!(upper || lower || digit)) {
380                                 if (AL) {
381                                         syslog(LOG_ERR,
382                                             "invalid auto-login name: %s", AL);
383                                         exit(1);
384                                 } else
385                                         continue;
386                         }
387                         set_flags(2);
388                         if (crmod) {
389                                 tmode.c_iflag |= ICRNL;
390                                 tmode.c_oflag |= ONLCR;
391                         }
392 #if REALLY_OLD_TTYS
393                         if (upper || UC)
394                                 tmode.sg_flags |= LCASE;
395                         if (lower || LC)
396                                 tmode.sg_flags &= ~LCASE;
397 #endif
398                         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
399                                 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
400                                 exit(1);
401                         }
402                         signal(SIGINT, SIG_DFL);
403                         for (i = 0; environ[i] != (char *)0; i++)
404                                 env[i] = environ[i];
405                         makeenv(&env[i]);
406
407                         limit.rlim_max = RLIM_INFINITY;
408                         limit.rlim_cur = RLIM_INFINITY;
409                         (void)setrlimit(RLIMIT_CPU, &limit);
410                         execle(LO, "login", AL ? "-fp" : "-p", name,
411                             (char *) 0, env);
412                         syslog(LOG_ERR, "%s: %m", LO);
413                         exit(1);
414                 }
415                 alarm(0);
416                 signal(SIGALRM, SIG_DFL);
417                 signal(SIGINT, SIG_IGN);
418                 if (NX && *NX) {
419                         tname = NX;
420                         dogettytab();
421                 }
422         }
423 }
424
425 static int
426 opentty(const char *tty, int flags)
427 {
428         int i;
429         int failopenlogged = 0;
430
431         while ((i = open(tty, flags)) == -1)
432         {
433                 if (!failopenlogged) {
434                         syslog(LOG_ERR, "open %s: %m", tty);
435                         failopenlogged = 1;
436                 }
437                 sleep(60);
438         }
439         if (login_tty(i) < 0) { 
440                 if (daemon(0,0) < 0) {
441                         syslog(LOG_ERR,"daemon: %m");
442                         close(i);
443                         return 0;
444                 }
445                 if (login_tty(i) < 0) {
446                         syslog(LOG_ERR, "login_tty %s: %m", tty);
447                         close(i);
448                         return 0;
449                 }
450         }
451         return 1;
452 }
453
454 static void
455 defttymode(void)
456 {
457         struct termios def;
458
459         /* Start with default tty settings. */
460         if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
461                 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
462                 exit(1);
463         }
464         omode = tmode; /* fill c_cc for dogettytab() */
465         dogettytab();
466         /*
467          * Don't rely on the driver too much, and initialize crucial
468          * things according to <sys/ttydefaults.h>.  Avoid clobbering
469          * the c_cc[] settings however, the console drivers might wish
470          * to leave their idea of the preferred VERASE key value
471          * there.
472          */
473         cfmakesane(&def);
474         tmode.c_iflag = def.c_iflag;
475         tmode.c_oflag = def.c_oflag;
476         tmode.c_lflag = def.c_lflag;
477         tmode.c_cflag = def.c_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(void)
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 }