main.c revision 77874
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 77874 2001-06-07 13:53:23Z yar $";
46#endif /* not lint */
47
48#include <sys/param.h>
49#include <sys/stat.h>
50#include <sys/ioctl.h>
51#include <sys/resource.h>
52#include <sys/ttydefaults.h>
53#include <sys/utsname.h>
54
55#include <ctype.h>
56#include <errno.h>
57#include <fcntl.h>
58#include <locale.h>
59#include <libutil.h>
60#include <signal.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 "pathnames.h"
72#include "extern.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 __P((int));
136static int	getname __P((void));
137static void	interrupt __P((int));
138static void	oflush __P((void));
139static void	prompt __P((void));
140static void	putchr __P((int));
141static void	putf __P((const char *));
142static void	putpad __P((const char *));
143static void	puts __P((const char *));
144static void	timeoverrun __P((int));
145static char	*getline __P((int));
146static void	setttymode __P((const char *, int));
147static void	setdefttymode __P((const char *));
148static int	opentty __P((const char *, int));
149
150int		main __P((int, char **));
151
152jmp_buf timeout;
153
154static void
155dingdong(signo)
156	int signo;
157{
158	alarm(0);
159	longjmp(timeout, 1);
160}
161
162jmp_buf	intrupt;
163
164static void
165interrupt(signo)
166	int signo;
167{
168	longjmp(intrupt, 1);
169}
170
171/*
172 * Action to take when getty is running too long.
173 */
174static void
175timeoverrun(signo)
176	int signo;
177{
178
179	syslog(LOG_ERR, "getty exiting due to excessive running time");
180	exit(1);
181}
182
183int
184main(argc, argv)
185	int argc;
186	char **argv;
187{
188	extern	char **environ;
189	const char *tname;
190	int first_sleep = 1, first_time = 1;
191	struct rlimit limit;
192	int rval;
193
194	signal(SIGINT, SIG_IGN);
195	signal(SIGQUIT, SIG_IGN);
196
197	openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
198	gethostname(hostname, sizeof(hostname) - 1);
199	hostname[sizeof(hostname) - 1] = '\0';
200	if (hostname[0] == '\0')
201		strcpy(hostname, "Amnesiac");
202
203	/*
204	 * Limit running time to deal with broken or dead lines.
205	 */
206	(void)signal(SIGXCPU, timeoverrun);
207	limit.rlim_max = RLIM_INFINITY;
208	limit.rlim_cur = GETTY_TIMEOUT;
209	(void)setrlimit(RLIMIT_CPU, &limit);
210
211	gettable("default", defent);
212	gendefaults();
213	tname = "default";
214	if (argc > 1)
215		tname = argv[1];
216
217	/*
218	 * The following is a work around for vhangup interactions
219	 * which cause great problems getting window systems started.
220	 * If the tty line is "-", we do the old style getty presuming
221	 * that the file descriptors are already set up for us.
222	 * J. Gettys - MIT Project Athena.
223	 */
224	if (argc <= 2 || strcmp(argv[2], "-") == 0)
225	    strcpy(ttyn, ttyname(STDIN_FILENO));
226	else {
227	    strcpy(ttyn, dev);
228	    strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
229	    if (strcmp(argv[0], "+") != 0) {
230		chown(ttyn, 0, 0);
231		chmod(ttyn, 0600);
232		revoke(ttyn);
233
234		gettable(tname, tabent);
235
236		/* Init modem sequence has been specified
237		 */
238		if (IC) {
239			if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
240				exit(1);
241			setdefttymode(tname);
242			if (getty_chat(IC, CT, DC) > 0) {
243				syslog(LOG_ERR, "modem init problem on %s", ttyn);
244				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
245				exit(1);
246			}
247		}
248
249		if (AC) {
250			int i, rfds;
251			struct timeval timeout;
252
253			if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
254				exit(1);
255        		setdefttymode(tname);
256        		rfds = 1 << 0;	/* FD_SET */
257        		timeout.tv_sec = RT;
258        		timeout.tv_usec = 0;
259        		i = select(32, (fd_set*)&rfds, (fd_set*)NULL,
260        			       (fd_set*)NULL, RT ? &timeout : NULL);
261        		if (i < 0) {
262				syslog(LOG_ERR, "select %s: %m", ttyn);
263			} else if (i == 0) {
264				syslog(LOG_NOTICE, "recycle tty %s", ttyn);
265				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
266				exit(0);  /* recycle for init */
267			}
268			i = getty_chat(AC, CT, DC);
269			if (i > 0) {
270				syslog(LOG_ERR, "modem answer problem on %s", ttyn);
271				(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
272				exit(1);
273			}
274		} else { /* maybe blocking open */
275			if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
276				exit(1);
277		}
278	    }
279	}
280
281	/* Start with default tty settings */
282	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
283		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
284		exit(1);
285	}
286	/*
287	 * Don't rely on the driver too much, and initialize crucial
288	 * things according to <sys/ttydefaults.h>.  Avoid clobbering
289	 * the c_cc[] settings however, the console drivers might wish
290	 * to leave their idea of the preferred VERASE key value
291	 * there.
292	 */
293	tmode.c_iflag = TTYDEF_IFLAG;
294	tmode.c_oflag = TTYDEF_OFLAG;
295	tmode.c_lflag = TTYDEF_LFLAG;
296	tmode.c_cflag = TTYDEF_CFLAG;
297	tmode.c_cflag |= (NC ? CLOCAL : 0);
298	omode = tmode;
299
300	for (;;) {
301
302		/*
303		 * if a delay was specified then sleep for that
304		 * number of seconds before writing the initial prompt
305		 */
306		if (first_sleep && DE) {
307		    sleep(DE);
308		    /* remove any noise */
309		    (void)tcflush(STDIN_FILENO, TCIOFLUSH);
310		}
311		first_sleep = 0;
312
313		setttymode(tname, 0);
314		if (AB) {
315			tname = autobaud();
316			continue;
317		}
318		if (PS) {
319			tname = portselector();
320			continue;
321		}
322		if (CL && *CL)
323			putpad(CL);
324		edithost(HE);
325
326		/* if this is the first time through this, and an
327		   issue file has been given, then send it */
328		if (first_time && IF) {
329			int fd;
330
331			if ((fd = open(IF, O_RDONLY)) != -1) {
332				char * cp;
333
334				while ((cp = getline(fd)) != NULL) {
335					  putf(cp);
336				}
337				close(fd);
338			}
339		}
340		first_time = 0;
341
342		if (IM && *IM)
343			putf(IM);
344		if (setjmp(timeout)) {
345			cfsetispeed(&tmode, B0);
346			cfsetospeed(&tmode, B0);
347			(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
348			exit(1);
349		}
350		if (TO) {
351			signal(SIGALRM, dingdong);
352			alarm(TO);
353		}
354		if (AL) {
355			const char *p = AL;
356			char *q = name;
357			int n = sizeof name;
358
359			while (*p && q < &name[sizeof name - 1]) {
360				if (isupper(*p))
361					upper = 1;
362				else if (islower(*p))
363					lower = 1;
364				else if (isdigit(*p))
365					digit++;
366				*q++ = *p++;
367			}
368		} else
369			rval = getname();
370		if (rval == 2) {
371			oflush();
372			alarm(0);
373			limit.rlim_max = RLIM_INFINITY;
374			limit.rlim_cur = RLIM_INFINITY;
375			(void)setrlimit(RLIMIT_CPU, &limit);
376			execle(PP, "ppplogin", ttyn, (char *) 0, env);
377			syslog(LOG_ERR, "%s: %m", PP);
378			exit(1);
379		} else if (rval || AL) {
380			register int i;
381
382			oflush();
383			alarm(0);
384			signal(SIGALRM, SIG_DFL);
385			if (name[0] == '-') {
386				puts("user names may not start with '-'.");
387				continue;
388			}
389			if (!(upper || lower || digit))
390				continue;
391			set_flags(2);
392			if (crmod) {
393				tmode.c_iflag |= ICRNL;
394				tmode.c_oflag |= ONLCR;
395			}
396#if REALLY_OLD_TTYS
397			if (upper || UC)
398				tmode.sg_flags |= LCASE;
399			if (lower || LC)
400				tmode.sg_flags &= ~LCASE;
401#endif
402			if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
403				syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
404				exit(1);
405			}
406			signal(SIGINT, SIG_DFL);
407			for (i = 0; environ[i] != (char *)0; i++)
408				env[i] = environ[i];
409			makeenv(&env[i]);
410
411			limit.rlim_max = RLIM_INFINITY;
412			limit.rlim_cur = RLIM_INFINITY;
413			(void)setrlimit(RLIMIT_CPU, &limit);
414			execle(LO, "login", AL ? "-fp" : "-p", name,
415			    (char *) 0, env);
416			syslog(LOG_ERR, "%s: %m", LO);
417			exit(1);
418		}
419		alarm(0);
420		signal(SIGALRM, SIG_DFL);
421		signal(SIGINT, SIG_IGN);
422		if (NX && *NX)
423			tname = NX;
424	}
425}
426
427static int
428opentty(const char *ttyn, int flags)
429{
430	int i, j = 0;
431	int failopenlogged = 0;
432
433	while (j < 10 && (i = open(ttyn, flags)) == -1)
434	{
435		if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
436			syslog(LOG_ERR, "open %s: %m", ttyn);
437			failopenlogged = 1;
438		}
439		j++;
440		sleep(60);
441	}
442	if (i == -1) {
443		syslog(LOG_ERR, "open %s: %m", ttyn);
444		return 0;
445	}
446	else {
447		if (login_tty(i) < 0) {
448			if (daemon(0,0) < 0) {
449				syslog(LOG_ERR,"daemon: %m");
450				close(i);
451				return 0;
452			}
453			if (login_tty(i) < 0) {
454				syslog(LOG_ERR, "login_tty %s: %m", ttyn);
455				close(i);
456				return 0;
457			}
458		}
459		return 1;
460	}
461}
462
463static void
464setdefttymode(tname)
465	const char * tname;
466{
467	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
468		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
469		exit(1);
470	}
471	tmode.c_iflag = TTYDEF_IFLAG;
472        tmode.c_oflag = TTYDEF_OFLAG;
473        tmode.c_lflag = TTYDEF_LFLAG;
474        tmode.c_cflag = TTYDEF_CFLAG;
475        omode = tmode;
476	setttymode(tname, 1);
477}
478
479static void
480setttymode(tname, raw)
481	const char * tname;
482	int raw;
483{
484	int off = 0;
485
486	gettable(tname, tabent);
487	if (OPset || EPset || APset)
488		APset++, OPset++, EPset++;
489	setdefaults();
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()
515{
516	register int c;
517	register 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(1);
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			np = name;
605			continue;
606		} else if (isdigit(c))
607			digit++;
608		if (IG && (c <= ' ' || c > 0176))
609			continue;
610		*np++ = c;
611		putchr(cs);
612	}
613	signal(SIGINT, SIG_IGN);
614	*np = 0;
615	if (c == '\r')
616		crmod = 1;
617	if ((upper && !lower && !LC) || UC)
618		for (np = name; *np; np++)
619			if (isupper(*np))
620				*np = tolower(*np);
621	return (1 + ppp_connection);
622}
623
624static void
625putpad(s)
626	register const char *s;
627{
628	register 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(s)
664	register const char *s;
665{
666	while (*s)
667		putchr(*s++);
668}
669
670char	outbuf[OBUFSIZ];
671int	obufcnt = 0;
672
673static void
674putchr(cc)
675	int cc;
676{
677	char c;
678
679	c = cc;
680	if (!NP) {
681		c |= partab[c&0177] & 0200;
682		if (OP)
683			c ^= 0200;
684	}
685	if (!UB) {
686		outbuf[obufcnt++] = c;
687		if (obufcnt >= OBUFSIZ)
688			oflush();
689	} else
690		write(STDOUT_FILENO, &c, 1);
691}
692
693static void
694oflush()
695{
696	if (obufcnt)
697		write(STDOUT_FILENO, outbuf, obufcnt);
698	obufcnt = 0;
699}
700
701static void
702prompt()
703{
704
705	putf(LM);
706	if (CO)
707		putchr('\n');
708}
709
710
711static char *
712getline(fd)
713	int fd;
714{
715	int i = 0;
716	static char linebuf[512];
717
718	/*
719	 * This is certainly slow, but it avoids having to include
720	 * stdio.h unnecessarily. Issue files should be small anyway.
721	 */
722	while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
723		if (linebuf[i] == '\n') {
724			/* Don't rely on newline mode, assume raw */
725			linebuf[i++] = '\r';
726			linebuf[i++] = '\n';
727			linebuf[i] = '\0';
728			return linebuf;
729		}
730		++i;
731	}
732	linebuf[i] = '\0';
733	return i ? linebuf : 0;
734}
735
736static void
737putf(cp)
738	register const char *cp;
739{
740	extern char editedhost[];
741	time_t t;
742	char *slash, db[100];
743
744	static struct utsname kerninfo;
745
746	if (!*kerninfo.sysname)
747		uname(&kerninfo);
748
749	while (*cp) {
750		if (*cp != '%') {
751			putchr(*cp++);
752			continue;
753		}
754		switch (*++cp) {
755
756		case 't':
757			slash = strrchr(ttyn, '/');
758			if (slash == (char *) 0)
759				puts(ttyn);
760			else
761				puts(&slash[1]);
762			break;
763
764		case 'h':
765			puts(editedhost);
766			break;
767
768		case 'd': {
769			t = (time_t)0;
770			(void)time(&t);
771			if (Lo)
772				(void)setlocale(LC_TIME, Lo);
773			(void)strftime(db, sizeof(db), DF, localtime(&t));
774			puts(db);
775			break;
776
777		case 's':
778			puts(kerninfo.sysname);
779			break;
780
781		case 'm':
782			puts(kerninfo.machine);
783			break;
784
785		case 'r':
786			puts(kerninfo.release);
787			break;
788
789		case 'v':
790			puts(kerninfo.version);
791			break;
792		}
793
794		case '%':
795			putchr('%');
796			break;
797		}
798		cp++;
799	}
800}
801