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