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