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