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
dingdong(int signo __unused)139 dingdong(int signo __unused)
140 {
141 alarm(0);
142 longjmp(timeout, 1);
143 }
144
145 static jmp_buf intrupt;
146
147 static void
interrupt(int signo __unused)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
timeoverrun(int signo __unused)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
main(int argc,char * argv[])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
opentty(const char * tty,int flags)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
defttymode(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
setttymode(int raw)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
getname(void)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
putpad(const char * s)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
puts(const char * s)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
putchr(int cc)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
oflush(void)681 oflush(void)
682 {
683 if (obufcnt)
684 write(STDOUT_FILENO, outbuf, obufcnt);
685 obufcnt = 0;
686 }
687
688 static void
prompt(void)689 prompt(void)
690 {
691
692 putf(LM);
693 if (CO)
694 putchr('\n');
695 }
696
697
698 static char *
get_line(int fd)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
putf(const char * cp)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
dogettytab(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 }
807