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