]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libexec/getty/main.c
MFV r316893:
[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                         int i, rfds;
256                         struct timeval to;
257
258                         rfds = 1 << 0;  /* FD_SET */
259                         to.tv_sec = RT;
260                         to.tv_usec = 0;
261                         i = select(32, (fd_set*)&rfds, (fd_set*)NULL,
262                                        (fd_set*)NULL, RT ? &to : NULL);
263                         if (i < 0) {
264                                 syslog(LOG_ERR, "select %s: %m", ttyn);
265                         } else if (i == 0) {
266                                 syslog(LOG_NOTICE, "recycle tty %s", ttyn);
267                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
268                                 exit(0);  /* recycle for init */
269                         }
270                         i = getty_chat(AC, CT, DC);
271                         if (i > 0) {
272                                 syslog(LOG_ERR, "modem answer problem on %s", ttyn);
273                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
274                                 exit(1);
275                         }
276                 } else { /* maybe blocking open */
277                         if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
278                                 exit(1);
279                 }
280             }
281         }
282
283         defttymode();
284         for (;;) {
285
286                 /*
287                  * if a delay was specified then sleep for that 
288                  * number of seconds before writing the initial prompt
289                  */
290                 if (first_sleep && DE) {
291                     sleep(DE);
292                     /* remove any noise */
293                     (void)tcflush(STDIN_FILENO, TCIOFLUSH);
294                 }
295                 first_sleep = 0;
296
297                 setttymode(0);
298                 if (AB) {
299                         tname = autobaud();
300                         dogettytab();
301                         continue;
302                 }
303                 if (PS) {
304                         tname = portselector();
305                         dogettytab();
306                         continue;
307                 }
308                 if (CL && *CL)
309                         putpad(CL);
310                 edithost(HE);
311
312                 /* if this is the first time through this, and an
313                    issue file has been given, then send it */
314                 if (first_time && IF) {
315                         int fd;
316
317                         if ((fd = open(IF, O_RDONLY)) != -1) {
318                                 char * cp;
319
320                                 while ((cp = get_line(fd)) != NULL) {
321                                           putf(cp);
322                                 }
323                                 close(fd);
324                         }
325                 }
326                 first_time = 0;
327
328                 if (IMP && *IMP && !(PL && PP))
329                         system(IMP);
330                 if (IM && *IM && !(PL && PP))
331                         putf(IM);
332                 if (setjmp(timeout)) {
333                         cfsetispeed(&tmode, B0);
334                         cfsetospeed(&tmode, B0);
335                         (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
336                         exit(1);
337                 }
338                 if (TO) {
339                         signal(SIGALRM, dingdong);
340                         alarm(TO);
341                 }
342
343                 rval = 0;
344                 if (AL) {
345                         const char *p = AL;
346                         char *q = name;
347
348                         while (*p && q < &name[sizeof name - 1]) {
349                                 if (isupper(*p))
350                                         upper = 1;
351                                 else if (islower(*p))
352                                         lower = 1;
353                                 else if (isdigit(*p))
354                                         digit = 1;
355                                 *q++ = *p++;
356                         }
357                 } else if (!(PL && PP))
358                         rval = getname();
359                 if (rval == 2 || (PL && PP)) {
360                         oflush();
361                         alarm(0);
362                         limit.rlim_max = RLIM_INFINITY;
363                         limit.rlim_cur = RLIM_INFINITY;
364                         (void)setrlimit(RLIMIT_CPU, &limit);
365                         execle(PP, "ppplogin", ttyn, (char *) 0, env);
366                         syslog(LOG_ERR, "%s: %m", PP);
367                         exit(1);
368                 } else if (rval || AL) {
369                         int i;
370
371                         oflush();
372                         alarm(0);
373                         signal(SIGALRM, SIG_DFL);
374                         if (name[0] == '\0')
375                                 continue;
376                         if (name[0] == '-') {
377                                 puts("user names may not start with '-'.");
378                                 continue;
379                         }
380                         if (!(upper || lower || digit)) {
381                                 if (AL) {
382                                         syslog(LOG_ERR,
383                                             "invalid auto-login name: %s", AL);
384                                         exit(1);
385                                 } else
386                                         continue;
387                         }
388                         set_flags(2);
389                         if (crmod) {
390                                 tmode.c_iflag |= ICRNL;
391                                 tmode.c_oflag |= ONLCR;
392                         }
393 #if REALLY_OLD_TTYS
394                         if (upper || UC)
395                                 tmode.sg_flags |= LCASE;
396                         if (lower || LC)
397                                 tmode.sg_flags &= ~LCASE;
398 #endif
399                         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
400                                 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
401                                 exit(1);
402                         }
403                         signal(SIGINT, SIG_DFL);
404                         for (i = 0; environ[i] != (char *)0; i++)
405                                 env[i] = environ[i];
406                         makeenv(&env[i]);
407
408                         limit.rlim_max = RLIM_INFINITY;
409                         limit.rlim_cur = RLIM_INFINITY;
410                         (void)setrlimit(RLIMIT_CPU, &limit);
411                         execle(LO, "login", AL ? "-fp" : "-p", name,
412                             (char *) 0, env);
413                         syslog(LOG_ERR, "%s: %m", LO);
414                         exit(1);
415                 }
416                 alarm(0);
417                 signal(SIGALRM, SIG_DFL);
418                 signal(SIGINT, SIG_IGN);
419                 if (NX && *NX) {
420                         tname = NX;
421                         dogettytab();
422                 }
423         }
424 }
425
426 static int
427 opentty(const char *tty, int flags)
428 {
429         int i;
430         int failopenlogged = 0;
431
432         while ((i = open(tty, flags)) == -1)
433         {
434                 if (!failopenlogged) {
435                         syslog(LOG_ERR, "open %s: %m", tty);
436                         failopenlogged = 1;
437                 }
438                 sleep(60);
439         }
440         if (login_tty(i) < 0) { 
441                 if (daemon(0,0) < 0) {
442                         syslog(LOG_ERR,"daemon: %m");
443                         close(i);
444                         return 0;
445                 }
446                 if (login_tty(i) < 0) {
447                         syslog(LOG_ERR, "login_tty %s: %m", tty);
448                         close(i);
449                         return 0;
450                 }
451         }
452         return 1;
453 }
454
455 static void
456 defttymode(void)
457 {
458         struct termios def;
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         cfmakesane(&def);
475         tmode.c_iflag = def.c_iflag;
476         tmode.c_oflag = def.c_oflag;
477         tmode.c_lflag = def.c_lflag;
478         tmode.c_cflag = def.c_cflag;
479         if (NC)
480                 tmode.c_cflag |= CLOCAL;
481         omode = tmode;
482 }
483
484 static void
485 setttymode(int raw)
486 {
487         int off = 0;
488
489         (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */
490         ioctl(STDIN_FILENO, FIONBIO, &off);     /* turn off non-blocking mode */
491         ioctl(STDIN_FILENO, FIOASYNC, &off);    /* ditto for async mode */
492
493         if (IS)
494                 cfsetispeed(&tmode, speed(IS));
495         else if (SP)
496                 cfsetispeed(&tmode, speed(SP));
497         if (OS)
498                 cfsetospeed(&tmode, speed(OS));
499         else if (SP)
500                 cfsetospeed(&tmode, speed(SP));
501         set_flags(0);
502         setchars();
503         if (raw)
504                 cfmakeraw(&tmode);
505         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
506                 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
507                 exit(1);
508         }
509 }
510
511
512 static int
513 getname(void)
514 {
515         int c;
516         char *np;
517         unsigned char cs;
518         int ppp_state = 0;
519         int ppp_connection = 0;
520
521         /*
522          * Interrupt may happen if we use CBREAK mode
523          */
524         if (setjmp(intrupt)) {
525                 signal(SIGINT, SIG_IGN);
526                 return (0);
527         }
528         signal(SIGINT, interrupt);
529         set_flags(1);
530         prompt();
531         oflush();
532         if (PF > 0) {
533                 sleep(PF);
534                 PF = 0;
535         }
536         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
537                 syslog(LOG_ERR, "%s: %m", ttyn);
538                 exit(1);
539         }
540         crmod = digit = lower = upper = 0;
541         np = name;
542         for (;;) {
543                 oflush();
544                 if (read(STDIN_FILENO, &cs, 1) <= 0)
545                         exit(0);
546                 if ((c = cs&0177) == 0)
547                         return (0);
548
549                 /* PPP detection state machine..
550                    Look for sequences:
551                    PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
552                    PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
553                    See RFC1662.
554                    Derived from code from Michael Hancock, <michaelh@cet.co.jp>
555                    and Erik 'PPP' Olson, <eriko@wrq.com>
556                  */
557
558                 if (PP && (cs == PPP_FRAME)) {
559                         ppp_state = 1;
560                 } else if (ppp_state == 1 && cs == PPP_STATION) {
561                         ppp_state = 2;
562                 } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
563                         ppp_state = 3;
564                 } else if ((ppp_state == 2 && cs == PPP_CONTROL)
565                         || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
566                         ppp_state = 4;
567                 } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
568                         ppp_state = 5;
569                 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
570                         ppp_connection = 1;
571                         break;
572                 } else {
573                         ppp_state = 0;
574                 }
575
576                 if (c == EOT || c == CTRL('d'))
577                         exit(0);
578                 if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
579                         putf("\r\n");
580                         break;
581                 }
582                 if (islower(c))
583                         lower = 1;
584                 else if (isupper(c))
585                         upper = 1;
586                 else if (c == ERASE || c == '\b' || c == 0177) {
587                         if (np > name) {
588                                 np--;
589                                 if (cfgetospeed(&tmode) >= 1200)
590                                         puts("\b \b");
591                                 else
592                                         putchr(cs);
593                         }
594                         continue;
595                 } else if (c == KILL || c == CTRL('u')) {
596                         putchr('\r');
597                         if (cfgetospeed(&tmode) < 1200)
598                                 putchr('\n');
599                         /* this is the way they do it down under ... */
600                         else if (np > name)
601                                 puts("                                     \r");
602                         prompt();
603                         digit = lower = upper = 0;
604                         np = name;
605                         continue;
606                 } else if (isdigit(c))
607                         digit = 1;
608                 if (IG && (c <= ' ' || c > 0176))
609                         continue;
610                 *np++ = c;
611                 putchr(cs);
612         }
613         signal(SIGINT, SIG_IGN);
614         *np = 0;
615         if (c == '\r')
616                 crmod = 1;
617         if ((upper && !lower && !LC) || UC)
618                 for (np = name; *np; np++)
619                         if (isupper(*np))
620                                 *np = tolower(*np);
621         return (1 + ppp_connection);
622 }
623
624 static void
625 putpad(const char *s)
626 {
627         int pad = 0;
628         speed_t ospeed = cfgetospeed(&tmode);
629
630         if (isdigit(*s)) {
631                 while (isdigit(*s)) {
632                         pad *= 10;
633                         pad += *s++ - '0';
634                 }
635                 pad *= 10;
636                 if (*s == '.' && isdigit(s[1])) {
637                         pad += s[1] - '0';
638                         s += 2;
639                 }
640         }
641
642         puts(s);
643         /*
644          * If no delay needed, or output speed is
645          * not comprehensible, then don't try to delay.
646          */
647         if (pad == 0 || ospeed <= 0)
648                 return;
649
650         /*
651          * Round up by a half a character frame, and then do the delay.
652          * Too bad there are no user program accessible programmed delays.
653          * Transmitting pad characters slows many terminals down and also
654          * loads the system.
655          */
656         pad = (pad * ospeed + 50000) / 100000;
657         while (pad--)
658                 putchr(*PC);
659 }
660
661 static void
662 puts(const char *s)
663 {
664         while (*s)
665                 putchr(*s++);
666 }
667
668 static char     outbuf[OBUFSIZ];
669 static int      obufcnt = 0;
670
671 static void
672 putchr(int cc)
673 {
674         char c;
675
676         c = cc;
677         if (!NP) {
678                 c |= partab[c&0177] & 0200;
679                 if (OP)
680                         c ^= 0200;
681         }
682         if (!UB) {
683                 outbuf[obufcnt++] = c;
684                 if (obufcnt >= OBUFSIZ)
685                         oflush();
686         } else
687                 write(STDOUT_FILENO, &c, 1);
688 }
689
690 static void
691 oflush(void)
692 {
693         if (obufcnt)
694                 write(STDOUT_FILENO, outbuf, obufcnt);
695         obufcnt = 0;
696 }
697
698 static void
699 prompt(void)
700 {
701
702         putf(LM);
703         if (CO)
704                 putchr('\n');
705 }
706
707
708 static char *
709 get_line(int fd)
710 {
711         int i = 0;
712         static char linebuf[512];
713
714         /*
715          * This is certainly slow, but it avoids having to include
716          * stdio.h unnecessarily. Issue files should be small anyway.
717          */
718         while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
719                 if (linebuf[i] == '\n') {
720                         /* Don't rely on newline mode, assume raw */
721                         linebuf[i++] = '\r';
722                         linebuf[i++] = '\n';
723                         linebuf[i] = '\0';
724                         return linebuf;
725                 }
726                 ++i;
727         }
728         linebuf[i] = '\0';
729         return i ? linebuf : 0;
730 }
731
732 static void
733 putf(const char *cp)
734 {
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 }