xref: /freebsd/libexec/getty/main.c (revision 3e0f6b97b257a96f7275e4442204263e44b16686)
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[] = "$FreeBSD$";
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 repcnt = 0, failopenlogged = 0;
187 	int first_sleep = 1, first_time = 1;
188 	struct rlimit limit;
189 	int rval;
190 
191 	signal(SIGINT, SIG_IGN);
192 	signal(SIGQUIT, SIG_IGN);
193 
194 	openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
195 	gethostname(hostname, sizeof(hostname));
196 	if (hostname[0] == '\0')
197 		strcpy(hostname, "Amnesiac");
198 
199 	/*
200 	 * Limit running time to deal with broken or dead lines.
201 	 */
202 	(void)signal(SIGXCPU, timeoverrun);
203 	limit.rlim_max = RLIM_INFINITY;
204 	limit.rlim_cur = GETTY_TIMEOUT;
205 	(void)setrlimit(RLIMIT_CPU, &limit);
206 
207 	gettable("default", defent);
208 	gendefaults();
209 	tname = "default";
210 	if (argc > 1)
211 		tname = argv[1];
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 	    int i;
224 
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 			execle(PP, "ppplogin", ttyn, (char *) 0, env);
353 			syslog(LOG_ERR, "%s: %m", PP);
354 			exit(1);
355 		} else if (rval) {
356 			register int i;
357 
358 			oflush();
359 			alarm(0);
360 			signal(SIGALRM, SIG_DFL);
361 			if (name[0] == '-') {
362 				puts("user names may not start with '-'.");
363 				continue;
364 			}
365 			if (!(upper || lower || digit))
366 				continue;
367 			setflags(2);
368 			if (crmod) {
369 				tmode.c_iflag |= ICRNL;
370 				tmode.c_oflag |= ONLCR;
371 			}
372 #if REALLY_OLD_TTYS
373 			if (upper || UC)
374 				tmode.sg_flags |= LCASE;
375 			if (lower || LC)
376 				tmode.sg_flags &= ~LCASE;
377 #endif
378 			if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
379 				syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
380 				exit(1);
381 			}
382 			signal(SIGINT, SIG_DFL);
383 			for (i = 0; environ[i] != (char *)0; i++)
384 				env[i] = environ[i];
385 			makeenv(&env[i]);
386 
387 			limit.rlim_max = RLIM_INFINITY;
388 			limit.rlim_cur = RLIM_INFINITY;
389 			(void)setrlimit(RLIMIT_CPU, &limit);
390 			execle(LO, "login", "-p", name, (char *) 0, env);
391 			syslog(LOG_ERR, "%s: %m", LO);
392 			exit(1);
393 		}
394 		alarm(0);
395 		signal(SIGALRM, SIG_DFL);
396 		signal(SIGINT, SIG_IGN);
397 		if (NX && *NX)
398 			tname = NX;
399 	}
400 }
401 
402 static int
403 opentty(const char *ttyn, int flags)
404 {
405 	int i, j = 0;
406 	int failopenlogged = 0;
407 
408 	while (j < 10 && (i = open(ttyn, flags)) == -1)
409 	{
410 		if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
411 			syslog(LOG_ERR, "open %s: %m", ttyn);
412 			failopenlogged = 1;
413 		}
414 		j++;
415 		sleep(60);
416 	}
417 	if (i == -1) {
418 		syslog(LOG_ERR, "open %s: %m", ttyn);
419 		return 0;
420 	}
421 	else {
422 		login_tty(i);
423 		return 1;
424 	}
425 }
426 
427 static void
428 setdefttymode(tname)
429 	const char * tname;
430 {
431 	if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
432 		syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
433 		exit(1);
434 	}
435 	tmode.c_iflag = TTYDEF_IFLAG;
436         tmode.c_oflag = TTYDEF_OFLAG;
437         tmode.c_lflag = TTYDEF_LFLAG;
438         tmode.c_cflag = TTYDEF_CFLAG;
439         omode = tmode;
440 	setttymode(tname, 1);
441 }
442 
443 static void
444 setttymode(tname, raw)
445 	const char * tname;
446 	int raw;
447 {
448 	int off = 0;
449 
450 	gettable(tname, tabent);
451 	if (OPset || EPset || APset)
452 		APset++, OPset++, EPset++;
453 	setdefaults();
454 	(void)tcflush(STDIN_FILENO, TCIOFLUSH);	/* clear out the crap */
455 	ioctl(STDIN_FILENO, FIONBIO, &off);	/* turn off non-blocking mode */
456 	ioctl(STDIN_FILENO, FIOASYNC, &off);	/* ditto for async mode */
457 
458 	if (IS)
459 		cfsetispeed(&tmode, speed(IS));
460 	else if (SP)
461 		cfsetispeed(&tmode, speed(SP));
462 	if (OS)
463 		cfsetospeed(&tmode, speed(OS));
464 	else if (SP)
465 		cfsetospeed(&tmode, speed(SP));
466 	setflags(0);
467 	setchars();
468 	if (raw)
469 		cfmakeraw(&tmode);
470 	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
471 		syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
472 		exit(1);
473 	}
474 }
475 
476 
477 static int
478 getname()
479 {
480 	register int c;
481 	register char *np;
482 	unsigned char cs;
483 	int ppp_state = 0;
484 	int ppp_connection = 0;
485 
486 	/*
487 	 * Interrupt may happen if we use CBREAK mode
488 	 */
489 	if (setjmp(intrupt)) {
490 		signal(SIGINT, SIG_IGN);
491 		return (0);
492 	}
493 	signal(SIGINT, interrupt);
494 	setflags(1);
495 	prompt();
496 	oflush();
497 	if (PF > 0) {
498 		sleep(PF);
499 		PF = 0;
500 	}
501 	if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
502 		syslog(LOG_ERR, "%s: %m", ttyn);
503 		exit(1);
504 	}
505 	crmod = digit = lower = upper = 0;
506 	np = name;
507 	for (;;) {
508 		oflush();
509 		if (read(STDIN_FILENO, &cs, 1) <= 0)
510 			exit(0);
511 		if ((c = cs&0177) == 0)
512 			return (0);
513 
514 		/* PPP detection state machine..
515 		   Look for sequences:
516 		   PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
517 		   PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
518 		   See RFC1662.
519 		   Derived from code from Michael Hancock, <michaelh@cet.co.jp>
520 		   and Erik 'PPP' Olson, <eriko@wrq.com>
521 		 */
522 
523 		if (PP && (cs == PPP_FRAME)) {
524 			ppp_state = 1;
525 		} else if (ppp_state == 1 && cs == PPP_STATION) {
526 			ppp_state = 2;
527 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
528 			ppp_state = 3;
529 		} else if ((ppp_state == 2 && cs == PPP_CONTROL)
530 			|| (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
531 			ppp_state = 4;
532 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
533 			ppp_state = 5;
534 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
535 			ppp_connection = 1;
536 			break;
537 		} else {
538 			ppp_state = 0;
539 		}
540 
541 		if (c == EOT || c == CTRL('d'))
542 			exit(1);
543 		if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
544 			putf("\r\n");
545 			break;
546 		}
547 		if (islower(c))
548 			lower = 1;
549 		else if (isupper(c))
550 			upper = 1;
551 		else if (c == ERASE || c == '\b' || c == 0177) {
552 			if (np > name) {
553 				np--;
554 				if (cfgetospeed(&tmode) >= 1200)
555 					puts("\b \b");
556 				else
557 					putchr(cs);
558 			}
559 			continue;
560 		} else if (c == KILL || c == CTRL('u')) {
561 			putchr('\r');
562 			if (cfgetospeed(&tmode) < 1200)
563 				putchr('\n');
564 			/* this is the way they do it down under ... */
565 			else if (np > name)
566 				puts("                                     \r");
567 			prompt();
568 			np = name;
569 			continue;
570 		} else if (isdigit(c))
571 			digit++;
572 		if (IG && (c <= ' ' || c > 0176))
573 			continue;
574 		*np++ = c;
575 		putchr(cs);
576 	}
577 	signal(SIGINT, SIG_IGN);
578 	*np = 0;
579 	if (c == '\r')
580 		crmod = 1;
581 	if ((upper && !lower && !LC) || UC)
582 		for (np = name; *np; np++)
583 			if (isupper(*np))
584 				*np = tolower(*np);
585 	return (1 + ppp_connection);
586 }
587 
588 static void
589 putpad(s)
590 	register const char *s;
591 {
592 	register pad = 0;
593 	speed_t ospeed = cfgetospeed(&tmode);
594 
595 	if (isdigit(*s)) {
596 		while (isdigit(*s)) {
597 			pad *= 10;
598 			pad += *s++ - '0';
599 		}
600 		pad *= 10;
601 		if (*s == '.' && isdigit(s[1])) {
602 			pad += s[1] - '0';
603 			s += 2;
604 		}
605 	}
606 
607 	puts(s);
608 	/*
609 	 * If no delay needed, or output speed is
610 	 * not comprehensible, then don't try to delay.
611 	 */
612 	if (pad == 0 || ospeed <= 0)
613 		return;
614 
615 	/*
616 	 * Round up by a half a character frame, and then do the delay.
617 	 * Too bad there are no user program accessible programmed delays.
618 	 * Transmitting pad characters slows many terminals down and also
619 	 * loads the system.
620 	 */
621 	pad = (pad * ospeed + 50000) / 100000;
622 	while (pad--)
623 		putchr(*PC);
624 }
625 
626 static void
627 puts(s)
628 	register const char *s;
629 {
630 	while (*s)
631 		putchr(*s++);
632 }
633 
634 char	outbuf[OBUFSIZ];
635 int	obufcnt = 0;
636 
637 static void
638 putchr(cc)
639 	int cc;
640 {
641 	char c;
642 
643 	c = cc;
644 	if (!NP) {
645 		c |= partab[c&0177] & 0200;
646 		if (OP)
647 			c ^= 0200;
648 	}
649 	if (!UB) {
650 		outbuf[obufcnt++] = c;
651 		if (obufcnt >= OBUFSIZ)
652 			oflush();
653 	} else
654 		write(STDOUT_FILENO, &c, 1);
655 }
656 
657 static void
658 oflush()
659 {
660 	if (obufcnt)
661 		write(STDOUT_FILENO, outbuf, obufcnt);
662 	obufcnt = 0;
663 }
664 
665 static void
666 prompt()
667 {
668 
669 	putf(LM);
670 	if (CO)
671 		putchr('\n');
672 }
673 
674 
675 static char *
676 getline(fd)
677 	int fd;
678 {
679 	int i = 0;
680 	static char linebuf[512];
681 
682 	/*
683 	 * This is certainly slow, but it avoids having to include
684 	 * stdio.h unnecessarily. Issue files should be small anyway.
685 	 */
686 	while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
687 		if (linebuf[i] == '\n') {
688 			/* Don't rely on newline mode, assume raw */
689 			linebuf[i++] = '\r';
690 			linebuf[i++] = '\n';
691 			linebuf[i] = '\0';
692 			return linebuf;
693 		}
694 		++i;
695 	}
696 	linebuf[i] = '\0';
697 	return i ? linebuf : 0;
698 }
699 
700 static void
701 putf(cp)
702 	register const char *cp;
703 {
704 	extern char editedhost[];
705 	time_t t;
706 	char *slash, db[100];
707 
708 	static struct utsname kerninfo;
709 
710 	if (!*kerninfo.sysname)
711 		uname(&kerninfo);
712 
713 	while (*cp) {
714 		if (*cp != '%') {
715 			putchr(*cp++);
716 			continue;
717 		}
718 		switch (*++cp) {
719 
720 		case 't':
721 			slash = strrchr(ttyn, '/');
722 			if (slash == (char *) 0)
723 				puts(ttyn);
724 			else
725 				puts(&slash[1]);
726 			break;
727 
728 		case 'h':
729 			puts(editedhost);
730 			break;
731 
732 		case 'd': {
733 			t = (time_t)0;
734 			(void)time(&t);
735 			if (Lo)
736 				(void)setlocale(LC_TIME, Lo);
737 			(void)strftime(db, sizeof(db), "%+", localtime(&t));
738 			puts(db);
739 			break;
740 
741 		case 's':
742 			puts(kerninfo.sysname);
743 			break;
744 
745 		case 'm':
746 			puts(kerninfo.machine);
747 			break;
748 
749 		case 'r':
750 			puts(kerninfo.release);
751 			break;
752 
753 		case 'v':
754 			puts(kerninfo.version);
755 			break;
756 		}
757 
758 		case '%':
759 			putchr('%');
760 			break;
761 		}
762 		cp++;
763 	}
764 }
765