main.c revision 116164
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
44static const char rcsid[] =
45  "$FreeBSD: head/libexec/getty/main.c 116164 2003-06-10 18:30:41Z yar $";
46#endif /* not lint */
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 "extern.h"
71#include "gettytab.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
93struct termios tmode, omode;
94
95int crmod, digit, lower, upper;
96
97char	hostname[MAXHOSTNAMELEN];
98char	name[MAXLOGNAME*3];
99char	dev[] = _PATH_DEV;
100char	ttyn[32];
101
102#define	OBUFSIZ		128
103#define	TABBUFSIZ	512
104
105char	defent[TABBUFSIZ];
106char	tabent[TABBUFSIZ];
107
108char	*env[128];
109
110char partab[] = {
111	0001,0201,0201,0001,0201,0001,0001,0201,
112	0202,0004,0003,0205,0005,0206,0201,0001,
113	0201,0001,0001,0201,0001,0201,0201,0001,
114	0001,0201,0201,0001,0201,0001,0001,0201,
115	0200,0000,0000,0200,0000,0200,0200,0000,
116	0000,0200,0200,0000,0200,0000,0000,0200,
117	0000,0200,0200,0000,0200,0000,0000,0200,
118	0200,0000,0000,0200,0000,0200,0200,0000,
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	0000,0200,0200,0000,0200,0000,0000,0200,
124	0200,0000,0000,0200,0000,0200,0200,0000,
125	0200,0000,0000,0200,0000,0200,0200,0000,
126	0000,0200,0200,0000,0200,0000,0000,0201
127};
128
129#define	ERASE	tmode.c_cc[VERASE]
130#define	KILL	tmode.c_cc[VKILL]
131#define	EOT	tmode.c_cc[VEOF]
132
133#define	puts	Gputs
134
135static void	dingdong(int);
136static void	dogettytab(const char *);
137static int	getname(void);
138static void	interrupt(int);
139static void	oflush(void);
140static void	prompt(void);
141static void	putchr(int);
142static void	putf(const char *);
143static void	putpad(const char *);
144static void	puts(const char *);
145static void	timeoverrun(int);
146static char	*getline(int);
147static void	setttymode(int);
148static void	setdefttymode(void);
149static int	opentty(const char *, int);
150
151jmp_buf timeout;
152
153static void
154dingdong(int signo __unused)
155{
156	alarm(0);
157	longjmp(timeout, 1);
158}
159
160jmp_buf	intrupt;
161
162static void
163interrupt(int signo __unused)
164{
165	longjmp(intrupt, 1);
166}
167
168/*
169 * Action to take when getty is running too long.
170 */
171static void
172timeoverrun(int signo __unused)
173{
174
175	syslog(LOG_ERR, "getty exiting due to excessive running time");
176	exit(1);
177}
178
179int
180main(int argc, char *argv[])
181{
182	extern	char **environ;
183	const char *tname;
184	int first_sleep = 1, first_time = 1;
185	struct rlimit limit;
186	int rval;
187
188	signal(SIGINT, SIG_IGN);
189	signal(SIGQUIT, SIG_IGN);
190
191	openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
192	gethostname(hostname, sizeof(hostname) - 1);
193	hostname[sizeof(hostname) - 1] = '\0';
194	if (hostname[0] == '\0')
195		strcpy(hostname, "Amnesiac");
196
197	/*
198	 * Limit running time to deal with broken or dead lines.
199	 */
200	(void)signal(SIGXCPU, timeoverrun);
201	limit.rlim_max = RLIM_INFINITY;
202	limit.rlim_cur = GETTY_TIMEOUT;
203	(void)setrlimit(RLIMIT_CPU, &limit);
204
205	gettable("default", defent);
206	gendefaults();
207	tname = "default";
208	if (argc > 1) {
209		tname = argv[1];
210		dogettytab(tname);
211	}
212
213	/*
214	 * The following is a work around for vhangup interactions
215	 * which cause great problems getting window systems started.
216	 * If the tty line is "-", we do the old style getty presuming
217	 * that the file descriptors are already set up for us.
218	 * J. Gettys - MIT Project Athena.
219	 */
220	if (argc <= 2 || strcmp(argv[2], "-") == 0)
221	    strcpy(ttyn, ttyname(STDIN_FILENO));
222	else {
223	    strcpy(ttyn, dev);
224	    strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
225	    if (strcmp(argv[0], "+") != 0) {
226		chown(ttyn, 0, 0);
227		chmod(ttyn, 0600);
228		revoke(ttyn);
229
230		/* Init modem sequence has been specified
231		 */
232		if (IC) {
233			if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
234				exit(1);
235			setdefttymode();
236			if (getty_chat(IC, CT, DC) > 0) {
237				syslog(LOG_ERR, "modem init problem on %s", ttyn);
238				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
239				exit(1);
240			}
241		}
242
243		if (AC) {
244			int i, rfds;
245			struct timeval to;
246
247			if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
248				exit(1);
249        		setdefttymode();
250        		rfds = 1 << 0;	/* FD_SET */
251        		to.tv_sec = RT;
252        		to.tv_usec = 0;
253        		i = select(32, (fd_set*)&rfds, (fd_set*)NULL,
254        			       (fd_set*)NULL, RT ? &to : NULL);
255        		if (i < 0) {
256				syslog(LOG_ERR, "select %s: %m", ttyn);
257			} else if (i == 0) {
258				syslog(LOG_NOTICE, "recycle tty %s", ttyn);
259				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
260				exit(0);  /* recycle for init */
261			}
262			i = getty_chat(AC, CT, DC);
263			if (i > 0) {
264				syslog(LOG_ERR, "modem answer problem on %s", ttyn);
265				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
266				exit(1);
267			}
268		} else { /* maybe blocking open */
269			if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
270				exit(1);
271		}
272	    }
273	}
274
275	/* Start with default tty settings */
276	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
277		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
278		exit(1);
279	}
280	/*
281	 * Don't rely on the driver too much, and initialize crucial
282	 * things according to <sys/ttydefaults.h>.  Avoid clobbering
283	 * the c_cc[] settings however, the console drivers might wish
284	 * to leave their idea of the preferred VERASE key value
285	 * there.
286	 */
287	tmode.c_iflag = TTYDEF_IFLAG;
288	tmode.c_oflag = TTYDEF_OFLAG;
289	tmode.c_lflag = TTYDEF_LFLAG;
290	tmode.c_cflag = TTYDEF_CFLAG;
291	tmode.c_cflag |= (NC ? CLOCAL : 0);
292	omode = tmode;
293
294	for (;;) {
295
296		/*
297		 * if a delay was specified then sleep for that
298		 * number of seconds before writing the initial prompt
299		 */
300		if (first_sleep && DE) {
301		    sleep(DE);
302		    /* remove any noise */
303		    (void)tcflush(STDIN_FILENO, TCIOFLUSH);
304		}
305		first_sleep = 0;
306
307		setttymode(0);
308		if (AB) {
309			tname = autobaud();
310			dogettytab(tname);
311			continue;
312		}
313		if (PS) {
314			tname = portselector();
315			dogettytab(tname);
316			continue;
317		}
318		if (CL && *CL)
319			putpad(CL);
320		edithost(HE);
321
322		/* if this is the first time through this, and an
323		   issue file has been given, then send it */
324		if (first_time && IF) {
325			int fd;
326
327			if ((fd = open(IF, O_RDONLY)) != -1) {
328				char * cp;
329
330				while ((cp = getline(fd)) != NULL) {
331					  putf(cp);
332				}
333				close(fd);
334			}
335		}
336		first_time = 0;
337
338		if (IM && *IM && !(PL && PP))
339			putf(IM);
340		if (setjmp(timeout)) {
341			cfsetispeed(&tmode, B0);
342			cfsetospeed(&tmode, B0);
343			(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
344			exit(1);
345		}
346		if (TO) {
347			signal(SIGALRM, dingdong);
348			alarm(TO);
349		}
350
351		rval = 0;
352		if (AL) {
353			const char *p = AL;
354			char *q = name;
355
356			while (*p && q < &name[sizeof name - 1]) {
357				if (isupper(*p))
358					upper = 1;
359				else if (islower(*p))
360					lower = 1;
361				else if (isdigit(*p))
362					digit = 1;
363				*q++ = *p++;
364			}
365		} else if (!(PL && PP))
366			rval = getname();
367		if (rval == 2 || (PL && PP)) {
368			oflush();
369			alarm(0);
370			limit.rlim_max = RLIM_INFINITY;
371			limit.rlim_cur = RLIM_INFINITY;
372			(void)setrlimit(RLIMIT_CPU, &limit);
373			execle(PP, "ppplogin", ttyn, (char *) 0, env);
374			syslog(LOG_ERR, "%s: %m", PP);
375			exit(1);
376		} else if (rval || AL) {
377			int i;
378
379			oflush();
380			alarm(0);
381			signal(SIGALRM, SIG_DFL);
382			if (name[0] == '\0')
383				continue;
384			if (name[0] == '-') {
385				puts("user names may not start with '-'.");
386				continue;
387			}
388			if (!(upper || lower || digit)) {
389				if (AL) {
390					syslog(LOG_ERR,
391					    "invalid auto-login name: %s", AL);
392					exit(1);
393				} else
394					continue;
395			}
396			set_flags(2);
397			if (crmod) {
398				tmode.c_iflag |= ICRNL;
399				tmode.c_oflag |= ONLCR;
400			}
401#if REALLY_OLD_TTYS
402			if (upper || UC)
403				tmode.sg_flags |= LCASE;
404			if (lower || LC)
405				tmode.sg_flags &= ~LCASE;
406#endif
407			if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
408				syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
409				exit(1);
410			}
411			signal(SIGINT, SIG_DFL);
412			for (i = 0; environ[i] != (char *)0; i++)
413				env[i] = environ[i];
414			makeenv(&env[i]);
415
416			limit.rlim_max = RLIM_INFINITY;
417			limit.rlim_cur = RLIM_INFINITY;
418			(void)setrlimit(RLIMIT_CPU, &limit);
419			execle(LO, "login", AL ? "-fp" : "-p", name,
420			    (char *) 0, env);
421			syslog(LOG_ERR, "%s: %m", LO);
422			exit(1);
423		}
424		alarm(0);
425		signal(SIGALRM, SIG_DFL);
426		signal(SIGINT, SIG_IGN);
427		if (NX && *NX) {
428			tname = NX;
429			dogettytab(tname);
430		}
431	}
432}
433
434static int
435opentty(const char *tty, int flags)
436{
437	int i, j = 0;
438	int failopenlogged = 0;
439
440	while (j < 10 && (i = open(tty, flags)) == -1)
441	{
442		if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
443			syslog(LOG_ERR, "open %s: %m", tty);
444			failopenlogged = 1;
445		}
446		j++;
447		sleep(60);
448	}
449	if (i == -1) {
450		syslog(LOG_ERR, "open %s: %m", tty);
451		return 0;
452	}
453	else {
454		if (login_tty(i) < 0) {
455			if (daemon(0,0) < 0) {
456				syslog(LOG_ERR,"daemon: %m");
457				close(i);
458				return 0;
459			}
460			if (login_tty(i) < 0) {
461				syslog(LOG_ERR, "login_tty %s: %m", tty);
462				close(i);
463				return 0;
464			}
465		}
466		return 1;
467	}
468}
469
470static void
471setdefttymode(void)
472{
473	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
474		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
475		exit(1);
476	}
477	tmode.c_iflag = TTYDEF_IFLAG;
478        tmode.c_oflag = TTYDEF_OFLAG;
479        tmode.c_lflag = TTYDEF_LFLAG;
480        tmode.c_cflag = TTYDEF_CFLAG;
481        omode = tmode;
482	setttymode(1);
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(const char *tname)
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