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