]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - libexec/getty/main.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 #endif /* not lint */
45 #include <sys/cdefs.h>
46 __FBSDID("$FreeBSD$");
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(void)
458 {
459         struct termios def;
460
461         /* Start with default tty settings. */
462         if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
463                 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
464                 exit(1);
465         }
466         omode = tmode; /* fill c_cc for dogettytab() */
467         dogettytab();
468         /*
469          * Don't rely on the driver too much, and initialize crucial
470          * things according to <sys/ttydefaults.h>.  Avoid clobbering
471          * the c_cc[] settings however, the console drivers might wish
472          * to leave their idea of the preferred VERASE key value
473          * there.
474          */
475         cfmakesane(&def);
476         tmode.c_iflag = def.c_iflag;
477         tmode.c_oflag = def.c_oflag;
478         tmode.c_lflag = def.c_lflag;
479         tmode.c_cflag = def.c_cflag;
480         if (NC)
481                 tmode.c_cflag |= CLOCAL;
482         omode = tmode;
483 }
484
485 static void
486 setttymode(int raw)
487 {
488         int off = 0;
489
490         (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */
491         ioctl(STDIN_FILENO, FIONBIO, &off);     /* turn off non-blocking mode */
492         ioctl(STDIN_FILENO, FIOASYNC, &off);    /* ditto for async mode */
493
494         if (IS)
495                 cfsetispeed(&tmode, speed(IS));
496         else if (SP)
497                 cfsetispeed(&tmode, speed(SP));
498         if (OS)
499                 cfsetospeed(&tmode, speed(OS));
500         else if (SP)
501                 cfsetospeed(&tmode, speed(SP));
502         set_flags(0);
503         setchars();
504         if (raw)
505                 cfmakeraw(&tmode);
506         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
507                 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
508                 exit(1);
509         }
510 }
511
512
513 static int
514 getname(void)
515 {
516         int c;
517         char *np;
518         unsigned char cs;
519         int ppp_state = 0;
520         int ppp_connection = 0;
521
522         /*
523          * Interrupt may happen if we use CBREAK mode
524          */
525         if (setjmp(intrupt)) {
526                 signal(SIGINT, SIG_IGN);
527                 return (0);
528         }
529         signal(SIGINT, interrupt);
530         set_flags(1);
531         prompt();
532         oflush();
533         if (PF > 0) {
534                 sleep(PF);
535                 PF = 0;
536         }
537         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
538                 syslog(LOG_ERR, "%s: %m", ttyn);
539                 exit(1);
540         }
541         crmod = digit = lower = upper = 0;
542         np = name;
543         for (;;) {
544                 oflush();
545                 if (read(STDIN_FILENO, &cs, 1) <= 0)
546                         exit(0);
547                 if ((c = cs&0177) == 0)
548                         return (0);
549
550                 /* PPP detection state machine..
551                    Look for sequences:
552                    PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
553                    PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
554                    See RFC1662.
555                    Derived from code from Michael Hancock, <michaelh@cet.co.jp>
556                    and Erik 'PPP' Olson, <eriko@wrq.com>
557                  */
558
559                 if (PP && (cs == PPP_FRAME)) {
560                         ppp_state = 1;
561                 } else if (ppp_state == 1 && cs == PPP_STATION) {
562                         ppp_state = 2;
563                 } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
564                         ppp_state = 3;
565                 } else if ((ppp_state == 2 && cs == PPP_CONTROL)
566                         || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
567                         ppp_state = 4;
568                 } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
569                         ppp_state = 5;
570                 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
571                         ppp_connection = 1;
572                         break;
573                 } else {
574                         ppp_state = 0;
575                 }
576
577                 if (c == EOT || c == CTRL('d'))
578                         exit(0);
579                 if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
580                         putf("\r\n");
581                         break;
582                 }
583                 if (islower(c))
584                         lower = 1;
585                 else if (isupper(c))
586                         upper = 1;
587                 else if (c == ERASE || c == '\b' || c == 0177) {
588                         if (np > name) {
589                                 np--;
590                                 if (cfgetospeed(&tmode) >= 1200)
591                                         puts("\b \b");
592                                 else
593                                         putchr(cs);
594                         }
595                         continue;
596                 } else if (c == KILL || c == CTRL('u')) {
597                         putchr('\r');
598                         if (cfgetospeed(&tmode) < 1200)
599                                 putchr('\n');
600                         /* this is the way they do it down under ... */
601                         else if (np > name)
602                                 puts("                                     \r");
603                         prompt();
604                         digit = lower = upper = 0;
605                         np = name;
606                         continue;
607                 } else if (isdigit(c))
608                         digit = 1;
609                 if (IG && (c <= ' ' || c > 0176))
610                         continue;
611                 *np++ = c;
612                 putchr(cs);
613         }
614         signal(SIGINT, SIG_IGN);
615         *np = 0;
616         if (c == '\r')
617                 crmod = 1;
618         if ((upper && !lower && !LC) || UC)
619                 for (np = name; *np; np++)
620                         if (isupper(*np))
621                                 *np = tolower(*np);
622         return (1 + ppp_connection);
623 }
624
625 static void
626 putpad(const char *s)
627 {
628         int pad = 0;
629         speed_t ospeed = cfgetospeed(&tmode);
630
631         if (isdigit(*s)) {
632                 while (isdigit(*s)) {
633                         pad *= 10;
634                         pad += *s++ - '0';
635                 }
636                 pad *= 10;
637                 if (*s == '.' && isdigit(s[1])) {
638                         pad += s[1] - '0';
639                         s += 2;
640                 }
641         }
642
643         puts(s);
644         /*
645          * If no delay needed, or output speed is
646          * not comprehensible, then don't try to delay.
647          */
648         if (pad == 0 || ospeed <= 0)
649                 return;
650
651         /*
652          * Round up by a half a character frame, and then do the delay.
653          * Too bad there are no user program accessible programmed delays.
654          * Transmitting pad characters slows many terminals down and also
655          * loads the system.
656          */
657         pad = (pad * ospeed + 50000) / 100000;
658         while (pad--)
659                 putchr(*PC);
660 }
661
662 static void
663 puts(const char *s)
664 {
665         while (*s)
666                 putchr(*s++);
667 }
668
669 char    outbuf[OBUFSIZ];
670 int     obufcnt = 0;
671
672 static void
673 putchr(int cc)
674 {
675         char c;
676
677         c = cc;
678         if (!NP) {
679                 c |= partab[c&0177] & 0200;
680                 if (OP)
681                         c ^= 0200;
682         }
683         if (!UB) {
684                 outbuf[obufcnt++] = c;
685                 if (obufcnt >= OBUFSIZ)
686                         oflush();
687         } else
688                 write(STDOUT_FILENO, &c, 1);
689 }
690
691 static void
692 oflush(void)
693 {
694         if (obufcnt)
695                 write(STDOUT_FILENO, outbuf, obufcnt);
696         obufcnt = 0;
697 }
698
699 static void
700 prompt(void)
701 {
702
703         putf(LM);
704         if (CO)
705                 putchr('\n');
706 }
707
708
709 static char *
710 getline(int fd)
711 {
712         int i = 0;
713         static char linebuf[512];
714
715         /*
716          * This is certainly slow, but it avoids having to include
717          * stdio.h unnecessarily. Issue files should be small anyway.
718          */
719         while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
720                 if (linebuf[i] == '\n') {
721                         /* Don't rely on newline mode, assume raw */
722                         linebuf[i++] = '\r';
723                         linebuf[i++] = '\n';
724                         linebuf[i] = '\0';
725                         return linebuf;
726                 }
727                 ++i;
728         }
729         linebuf[i] = '\0';
730         return i ? linebuf : 0;
731 }
732
733 static void
734 putf(const char *cp)
735 {
736         extern char editedhost[];
737         time_t t;
738         char *slash, db[100];
739
740         static struct utsname kerninfo;
741
742         if (!*kerninfo.sysname)
743                 uname(&kerninfo);
744
745         while (*cp) {
746                 if (*cp != '%') {
747                         putchr(*cp++);
748                         continue;
749                 }
750                 switch (*++cp) {
751
752                 case 't':
753                         slash = strrchr(ttyn, '/');
754                         if (slash == (char *) 0)
755                                 puts(ttyn);
756                         else
757                                 puts(&slash[1]);
758                         break;
759
760                 case 'h':
761                         puts(editedhost);
762                         break;
763
764                 case 'd': {
765                         t = (time_t)0;
766                         (void)time(&t);
767                         if (Lo)
768                                 (void)setlocale(LC_TIME, Lo);
769                         (void)strftime(db, sizeof(db), DF, localtime(&t));
770                         puts(db);
771                         break;
772
773                 case 's':
774                         puts(kerninfo.sysname);
775                         break;
776
777                 case 'm':
778                         puts(kerninfo.machine);
779                         break;
780
781                 case 'r':
782                         puts(kerninfo.release);
783                         break;
784
785                 case 'v':
786                         puts(kerninfo.version);
787                         break;
788                 }
789
790                 case '%':
791                         putchr('%');
792                         break;
793                 }
794                 cp++;
795         }
796 }
797
798 /*
799  * Read a gettytab database entry and perform necessary quirks.
800  */
801 static void
802 dogettytab(void)
803 {
804         
805         /* Read the database entry. */
806         gettable(tname, tabent);
807
808         /*
809          * Avoid inheriting the parity values from the default entry
810          * if any of them is set in the current entry.
811          * Mixing different parity settings is unreasonable.
812          */
813         if (OPset || EPset || APset || NPset)
814                 OPset = EPset = APset = NPset = 1;
815
816         /* Fill in default values for unset capabilities. */
817         setdefaults();
818 }