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
35static 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
42static 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> */
94struct termios omode;
95/* current mode */
96struct termios tmode;
97
98int crmod, digit, lower, upper;
99
100char	hostname[MAXHOSTNAMELEN];
101char	name[MAXLOGNAME*3];
102char	dev[] = _PATH_DEV;
103char	ttyn[32];
104
105#define	OBUFSIZ		128
106#define	TABBUFSIZ	512
107
108char	defent[TABBUFSIZ];
109char	tabent[TABBUFSIZ];
110const	char *tname;
111
112char	*env[128];
113
114char 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
139static void	defttymode(void);
140static void	dingdong(int);
141static void	dogettytab(void);
142static int	getname(void);
143static void	interrupt(int);
144static void	oflush(void);
145static void	prompt(void);
146static void	putchr(int);
147static void	putf(const char *);
148static void	putpad(const char *);
149static void	puts(const char *);
150static void	timeoverrun(int);
151static char	*getline(int);
152static void	setttymode(int);
153static int	opentty(const char *, int);
154
155jmp_buf timeout;
156
157static void
158dingdong(int signo __unused)
159{
160	alarm(0);
161	longjmp(timeout, 1);
162}
163
164jmp_buf	intrupt;
165
166static void
167interrupt(int signo __unused)
168{
169	longjmp(intrupt, 1);
170}
171
172/*
173 * Action to take when getty is running too long.
174 */
175static void
176timeoverrun(int signo __unused)
177{
178
179	syslog(LOG_ERR, "getty exiting due to excessive running time");
180	exit(1);
181}
182
183int
184main(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
427static int
428opentty(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
456static void
457defttymode(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
485static void
486setttymode(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
513static int
514getname(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
625static void
626putpad(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
662static void
663puts(const char *s)
664{
665	while (*s)
666		putchr(*s++);
667}
668
669char	outbuf[OBUFSIZ];
670int	obufcnt = 0;
671
672static void
673putchr(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
691static void
692oflush(void)
693{
694	if (obufcnt)
695		write(STDOUT_FILENO, outbuf, obufcnt);
696	obufcnt = 0;
697}
698
699static void
700prompt(void)
701{
702
703	putf(LM);
704	if (CO)
705		putchr('\n');
706}
707
708
709static char *
710getline(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
733static void
734putf(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 */
801static void
802dogettytab(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}
819