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