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