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