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