]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libexec/getty/main.c
ping(8): Fix a mandoc related issue
[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     ttyn[32];
101
102 #define OBUFSIZ         128
103 #define TABBUFSIZ       512
104
105 static char     defent[TABBUFSIZ];
106 static char     tabent[TABBUFSIZ];
107 static const char       *tname;
108
109 static char     *env[128];
110
111 static char partab[] = {
112         0001,0201,0201,0001,0201,0001,0001,0201,
113         0202,0004,0003,0205,0005,0206,0201,0001,
114         0201,0001,0001,0201,0001,0201,0201,0001,
115         0001,0201,0201,0001,0201,0001,0001,0201,
116         0200,0000,0000,0200,0000,0200,0200,0000,
117         0000,0200,0200,0000,0200,0000,0000,0200,
118         0000,0200,0200,0000,0200,0000,0000,0200,
119         0200,0000,0000,0200,0000,0200,0200,0000,
120         0200,0000,0000,0200,0000,0200,0200,0000,
121         0000,0200,0200,0000,0200,0000,0000,0200,
122         0000,0200,0200,0000,0200,0000,0000,0200,
123         0200,0000,0000,0200,0000,0200,0200,0000,
124         0000,0200,0200,0000,0200,0000,0000,0200,
125         0200,0000,0000,0200,0000,0200,0200,0000,
126         0200,0000,0000,0200,0000,0200,0200,0000,
127         0000,0200,0200,0000,0200,0000,0000,0201
128 };
129
130 #define ERASE   tmode.c_cc[VERASE]
131 #define KILL    tmode.c_cc[VKILL]
132 #define EOT     tmode.c_cc[VEOF]
133
134 #define puts    Gputs
135
136 static void     defttymode(void);
137 static void     dingdong(int);
138 static void     dogettytab(void);
139 static int      getname(void);
140 static void     interrupt(int);
141 static void     oflush(void);
142 static void     prompt(void);
143 static void     putchr(int);
144 static void     putf(const char *);
145 static void     putpad(const char *);
146 static void     puts(const char *);
147 static void     timeoverrun(int);
148 static char     *get_line(int);
149 static void     setttymode(int);
150 static int      opentty(const char *, int);
151
152 static jmp_buf timeout;
153
154 static void
155 dingdong(int signo __unused)
156 {
157         alarm(0);
158         longjmp(timeout, 1);
159 }
160
161 static jmp_buf  intrupt;
162
163 static void
164 interrupt(int signo __unused)
165 {
166         longjmp(intrupt, 1);
167 }
168
169 /*
170  * Action to take when getty is running too long.
171  */
172 static void
173 timeoverrun(int signo __unused)
174 {
175
176         syslog(LOG_ERR, "getty exiting due to excessive running time");
177         exit(1);
178 }
179
180 int
181 main(int argc, char *argv[])
182 {
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, _PATH_DEV);
221             strlcat(ttyn, argv[2], sizeof(ttyn));
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                         fd_set rfds;
255                         struct timeval to;
256                         int i;
257
258                         FD_ZERO(&rfds);
259                         FD_SET(0, &rfds);
260                         to.tv_sec = RT;
261                         to.tv_usec = 0;
262                         i = select(32, &rfds, NULL, 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 failopenlogged = 0, i, saved_errno;
430
431         while ((i = open(tty, flags)) == -1)
432         {
433                 saved_errno = errno;
434                 if (!failopenlogged) {
435                         syslog(LOG_ERR, "open %s: %m", tty);
436                         failopenlogged = 1;
437                 }
438                 if (saved_errno == ENOENT)
439                         return 0;
440                 sleep(60);
441         }
442         if (login_tty(i) < 0) { 
443                 if (daemon(0,0) < 0) {
444                         syslog(LOG_ERR,"daemon: %m");
445                         close(i);
446                         return 0;
447                 }
448                 if (login_tty(i) < 0) {
449                         syslog(LOG_ERR, "login_tty %s: %m", tty);
450                         close(i);
451                         return 0;
452                 }
453         }
454         return 1;
455 }
456
457 static void
458 defttymode(void)
459 {
460         struct termios def;
461
462         /* Start with default tty settings. */
463         if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
464                 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
465                 exit(1);
466         }
467         omode = tmode; /* fill c_cc for dogettytab() */
468         dogettytab();
469         /*
470          * Don't rely on the driver too much, and initialize crucial
471          * things according to <sys/ttydefaults.h>.  Avoid clobbering
472          * the c_cc[] settings however, the console drivers might wish
473          * to leave their idea of the preferred VERASE key value
474          * there.
475          */
476         cfmakesane(&def);
477         tmode.c_iflag = def.c_iflag;
478         tmode.c_oflag = def.c_oflag;
479         tmode.c_lflag = def.c_lflag;
480         tmode.c_cflag = def.c_cflag;
481         if (NC)
482                 tmode.c_cflag |= CLOCAL;
483         omode = tmode;
484 }
485
486 static void
487 setttymode(int raw)
488 {
489         int off = 0;
490
491         (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */
492         ioctl(STDIN_FILENO, FIONBIO, &off);     /* turn off non-blocking mode */
493         ioctl(STDIN_FILENO, FIOASYNC, &off);    /* ditto for async mode */
494
495         if (IS)
496                 cfsetispeed(&tmode, speed(IS));
497         else if (SP)
498                 cfsetispeed(&tmode, speed(SP));
499         if (OS)
500                 cfsetospeed(&tmode, speed(OS));
501         else if (SP)
502                 cfsetospeed(&tmode, speed(SP));
503         set_flags(0);
504         setchars();
505         if (raw)
506                 cfmakeraw(&tmode);
507         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
508                 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
509                 exit(1);
510         }
511 }
512
513
514 static int
515 getname(void)
516 {
517         int c;
518         char *np;
519         unsigned char cs;
520         int ppp_state = 0;
521         int ppp_connection = 0;
522
523         /*
524          * Interrupt may happen if we use CBREAK mode
525          */
526         if (setjmp(intrupt)) {
527                 signal(SIGINT, SIG_IGN);
528                 return (0);
529         }
530         signal(SIGINT, interrupt);
531         set_flags(1);
532         prompt();
533         oflush();
534         if (PF > 0) {
535                 sleep(PF);
536                 PF = 0;
537         }
538         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
539                 syslog(LOG_ERR, "%s: %m", ttyn);
540                 exit(1);
541         }
542         crmod = digit = lower = upper = 0;
543         np = name;
544         for (;;) {
545                 oflush();
546                 if (read(STDIN_FILENO, &cs, 1) <= 0)
547                         exit(0);
548                 if ((c = cs&0177) == 0)
549                         return (0);
550
551                 /* PPP detection state machine..
552                    Look for sequences:
553                    PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
554                    PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
555                    See RFC1662.
556                    Derived from code from Michael Hancock, <michaelh@cet.co.jp>
557                    and Erik 'PPP' Olson, <eriko@wrq.com>
558                  */
559
560                 if (PP && (cs == PPP_FRAME)) {
561                         ppp_state = 1;
562                 } else if (ppp_state == 1 && cs == PPP_STATION) {
563                         ppp_state = 2;
564                 } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
565                         ppp_state = 3;
566                 } else if ((ppp_state == 2 && cs == PPP_CONTROL)
567                         || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
568                         ppp_state = 4;
569                 } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
570                         ppp_state = 5;
571                 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
572                         ppp_connection = 1;
573                         break;
574                 } else {
575                         ppp_state = 0;
576                 }
577
578                 if (c == EOT || c == CTRL('d'))
579                         exit(0);
580                 if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
581                         putf("\r\n");
582                         break;
583                 }
584                 if (islower(c))
585                         lower = 1;
586                 else if (isupper(c))
587                         upper = 1;
588                 else if (c == ERASE || c == '\b' || c == 0177) {
589                         if (np > name) {
590                                 np--;
591                                 if (cfgetospeed(&tmode) >= 1200)
592                                         puts("\b \b");
593                                 else
594                                         putchr(cs);
595                         }
596                         continue;
597                 } else if (c == KILL || c == CTRL('u')) {
598                         putchr('\r');
599                         if (cfgetospeed(&tmode) < 1200)
600                                 putchr('\n');
601                         /* this is the way they do it down under ... */
602                         else if (np > name)
603                                 puts("                                     \r");
604                         prompt();
605                         digit = lower = upper = 0;
606                         np = name;
607                         continue;
608                 } else if (isdigit(c))
609                         digit = 1;
610                 if (IG && (c <= ' ' || c > 0176))
611                         continue;
612                 *np++ = c;
613                 putchr(cs);
614         }
615         signal(SIGINT, SIG_IGN);
616         *np = 0;
617         if (c == '\r')
618                 crmod = 1;
619         if ((upper && !lower && !LC) || UC)
620                 for (np = name; *np; np++)
621                         if (isupper(*np))
622                                 *np = tolower(*np);
623         return (1 + ppp_connection);
624 }
625
626 static void
627 putpad(const char *s)
628 {
629         int pad = 0;
630         speed_t ospeed = cfgetospeed(&tmode);
631
632         if (isdigit(*s)) {
633                 while (isdigit(*s)) {
634                         pad *= 10;
635                         pad += *s++ - '0';
636                 }
637                 pad *= 10;
638                 if (*s == '.' && isdigit(s[1])) {
639                         pad += s[1] - '0';
640                         s += 2;
641                 }
642         }
643
644         puts(s);
645         /*
646          * If no delay needed, or output speed is
647          * not comprehensible, then don't try to delay.
648          */
649         if (pad == 0 || ospeed <= 0)
650                 return;
651
652         /*
653          * Round up by a half a character frame, and then do the delay.
654          * Too bad there are no user program accessible programmed delays.
655          * Transmitting pad characters slows many terminals down and also
656          * loads the system.
657          */
658         pad = (pad * ospeed + 50000) / 100000;
659         while (pad--)
660                 putchr(*PC);
661 }
662
663 static void
664 puts(const char *s)
665 {
666         while (*s)
667                 putchr(*s++);
668 }
669
670 static char     outbuf[OBUFSIZ];
671 static int      obufcnt = 0;
672
673 static void
674 putchr(int cc)
675 {
676         char c;
677
678         c = cc;
679         if (!NP) {
680                 c |= partab[c&0177] & 0200;
681                 if (OP)
682                         c ^= 0200;
683         }
684         if (!UB) {
685                 outbuf[obufcnt++] = c;
686                 if (obufcnt >= OBUFSIZ)
687                         oflush();
688         } else
689                 write(STDOUT_FILENO, &c, 1);
690 }
691
692 static void
693 oflush(void)
694 {
695         if (obufcnt)
696                 write(STDOUT_FILENO, outbuf, obufcnt);
697         obufcnt = 0;
698 }
699
700 static void
701 prompt(void)
702 {
703
704         putf(LM);
705         if (CO)
706                 putchr('\n');
707 }
708
709
710 static char *
711 get_line(int fd)
712 {
713         size_t i = 0;
714         static char linebuf[512];
715
716         /*
717          * This is certainly slow, but it avoids having to include
718          * stdio.h unnecessarily. Issue files should be small anyway.
719          */
720         while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
721                 if (linebuf[i] == '\n') {
722                         /* Don't rely on newline mode, assume raw */
723                         linebuf[i++] = '\r';
724                         linebuf[i++] = '\n';
725                         linebuf[i] = '\0';
726                         return linebuf;
727                 }
728                 ++i;
729         }
730         linebuf[i] = '\0';
731         return i ? linebuf : 0;
732 }
733
734 static void
735 putf(const char *cp)
736 {
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 }