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