Home | History | Annotate | Line # | Download | only in getty
main.c revision 1.53
      1 /*	$NetBSD: main.c,v 1.53 2007/12/03 09:54:24 isaki Exp $	*/
      2 
      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/cdefs.h>
     33 
     34 #ifndef lint
     35 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\n\
     36 	The Regents of the University of California.  All rights reserved.\n");
     37 #endif /* not lint */
     38 
     39 #ifndef lint
     40 #if 0
     41 static char sccsid[] = "from: @(#)main.c	8.1 (Berkeley) 6/20/93";
     42 #else
     43 __RCSID("$NetBSD: main.c,v 1.53 2007/12/03 09:54:24 isaki Exp $");
     44 #endif
     45 #endif /* not lint */
     46 
     47 #include <sys/param.h>
     48 #include <sys/stat.h>
     49 #include <termios.h>
     50 #include <sys/ioctl.h>
     51 #include <sys/resource.h>
     52 #include <sys/utsname.h>
     53 
     54 #include <errno.h>
     55 #include <fcntl.h>
     56 #include <time.h>
     57 #include <ctype.h>
     58 #include <pwd.h>
     59 #include <setjmp.h>
     60 #include <signal.h>
     61 #include <stdlib.h>
     62 #include <string.h>
     63 #include <syslog.h>
     64 #include <time.h>
     65 #include <unistd.h>
     66 #include <util.h>
     67 #include <limits.h>
     68 #include <ttyent.h>
     69 #include <termcap.h>
     70 
     71 #include "gettytab.h"
     72 #include "pathnames.h"
     73 #include "extern.h"
     74 
     75 extern char editedhost[];
     76 
     77 /*
     78  * Set the amount of running time that getty should accumulate
     79  * before deciding that something is wrong and exit.
     80  */
     81 #define GETTY_TIMEOUT	60 /* seconds */
     82 
     83 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
     84 
     85 #define PPP_FRAME           0x7e  /* PPP Framing character */
     86 #define PPP_STATION         0xff  /* "All Station" character */
     87 #define PPP_ESCAPE          0x7d  /* Escape Character */
     88 #define PPP_CONTROL         0x03  /* PPP Control Field */
     89 #define PPP_CONTROL_ESCAPED 0x23  /* PPP Control Field, escaped */
     90 #define PPP_LCP_HI          0xc0  /* LCP protocol - high byte */
     91 #define PPP_LCP_LOW         0x21  /* LCP protocol - low byte */
     92 
     93 struct termios tmode, omode;
     94 
     95 int crmod, digit, lower, upper;
     96 
     97 char	hostname[MAXHOSTNAMELEN + 1];
     98 struct	utsname kerninfo;
     99 char	name[LOGIN_NAME_MAX];
    100 char	dev[] = _PATH_DEV;
    101 char	ttyn[32];
    102 char	lockfile[512];
    103 uid_t	ttyowner;
    104 char	*rawttyn;
    105 
    106 #define	OBUFSIZ		128
    107 #define	TABBUFSIZ	512
    108 
    109 char	defent[TABBUFSIZ];
    110 char	tabent[TABBUFSIZ];
    111 
    112 char	*env[128];
    113 
    114 const unsigned char partab[] = {
    115 	0001,0201,0201,0001,0201,0001,0001,0201,
    116 	0202,0004,0003,0205,0005,0206,0201,0001,
    117 	0201,0001,0001,0201,0001,0201,0201,0001,
    118 	0001,0201,0201,0001,0201,0001,0001,0201,
    119 	0200,0000,0000,0200,0000,0200,0200,0000,
    120 	0000,0200,0200,0000,0200,0000,0000,0200,
    121 	0000,0200,0200,0000,0200,0000,0000,0200,
    122 	0200,0000,0000,0200,0000,0200,0200,0000,
    123 	0200,0000,0000,0200,0000,0200,0200,0000,
    124 	0000,0200,0200,0000,0200,0000,0000,0200,
    125 	0000,0200,0200,0000,0200,0000,0000,0200,
    126 	0200,0000,0000,0200,0000,0200,0200,0000,
    127 	0000,0200,0200,0000,0200,0000,0000,0200,
    128 	0200,0000,0000,0200,0000,0200,0200,0000,
    129 	0200,0000,0000,0200,0000,0200,0200,0000,
    130 	0000,0200,0200,0000,0200,0000,0000,0201
    131 };
    132 
    133 #define	ERASE	tmode.c_cc[VERASE]
    134 #define	KILL	tmode.c_cc[VKILL]
    135 #define	EOT	tmode.c_cc[VEOF]
    136 
    137 static void	clearscreen(void);
    138 
    139 jmp_buf timeout;
    140 
    141 static void
    142 /*ARGSUSED*/
    143 dingdong(int signo)
    144 {
    145 
    146 	(void)alarm(0);
    147 	(void)signal(SIGALRM, SIG_DFL);
    148 	longjmp(timeout, 1);
    149 }
    150 
    151 jmp_buf	intrupt;
    152 
    153 static void
    154 /*ARGSUSED*/
    155 interrupt(int signo)
    156 {
    157 
    158 	(void)signal(SIGINT, interrupt);
    159 	longjmp(intrupt, 1);
    160 }
    161 
    162 /*
    163  * Action to take when getty is running too long.
    164  */
    165 static void
    166 /*ARGSUSED*/
    167 timeoverrun(int signo)
    168 {
    169 
    170 	syslog(LOG_ERR, "getty exiting due to excessive running time");
    171 	exit(1);
    172 }
    173 
    174 static int	getname(void);
    175 static void	oflush(void);
    176 static void	prompt(void);
    177 static void	putchr(int);
    178 static void	putf(const char *);
    179 static void	putpad(const char *);
    180 static void	xputs(const char *);
    181 
    182 int
    183 main(int argc, char *argv[], char *envp[])
    184 {
    185 	const char *progname;
    186 	char *tname;
    187 	int repcnt = 0, failopenlogged = 0, uugetty = 0, first_time = 1;
    188 	struct rlimit limit;
    189 	struct passwd *pw;
    190 	int rval;
    191 
    192 	(void)signal(SIGINT, SIG_IGN);
    193 	openlog("getty", LOG_PID, LOG_AUTH);
    194 	(void)gethostname(hostname, sizeof(hostname));
    195 	hostname[sizeof(hostname) - 1] = '\0';
    196 	if (hostname[0] == '\0')
    197 		(void)strlcpy(hostname, "Amnesiac", sizeof(hostname));
    198 	(void)uname(&kerninfo);
    199 
    200 	progname = getprogname();
    201 	if (progname[0] == 'u' && progname[1] == 'u')
    202 		uugetty = 1;
    203 
    204 	/*
    205 	 * Find id of uucp login (if present) so we can chown tty properly.
    206 	 */
    207 	if (uugetty && (pw = getpwnam("uucp")))
    208 		ttyowner = pw->pw_uid;
    209 	else
    210 		ttyowner = 0;
    211 
    212 	/*
    213 	 * Limit running time to deal with broken or dead lines.
    214 	 */
    215 	(void)signal(SIGXCPU, timeoverrun);
    216 	limit.rlim_max = RLIM_INFINITY;
    217 	limit.rlim_cur = GETTY_TIMEOUT;
    218 	(void)setrlimit(RLIMIT_CPU, &limit);
    219 
    220 	/*
    221 	 * The following is a work around for vhangup interactions
    222 	 * which cause great problems getting window systems started.
    223 	 * If the tty line is "-", we do the old style getty presuming
    224 	 * that the file descriptors are already set up for us.
    225 	 * J. Gettys - MIT Project Athena.
    226 	 */
    227 	if (argc <= 2 || strcmp(argv[2], "-") == 0) {
    228 		(void)strlcpy(ttyn, ttyname(0), sizeof(ttyn));
    229 	}
    230 	else {
    231 		int i;
    232 
    233 		rawttyn = argv[2];
    234 		(void)strlcpy(ttyn, dev, sizeof(ttyn));
    235 		(void)strlcat(ttyn, argv[2], sizeof(ttyn));
    236 		if (uugetty)  {
    237 			(void)chown(ttyn, ttyowner, 0);
    238 			(void)strlcpy(lockfile, _PATH_LOCK,
    239 				sizeof(lockfile));
    240 			(void)strlcat(lockfile, argv[2],
    241 				sizeof(lockfile));
    242 			/*
    243 			 * wait for lockfiles to go away before we try
    244 			 * to open
    245 			 */
    246 			if (pidlock(lockfile, 0, 0, 0) != 0)  {
    247 				syslog(LOG_ERR,
    248 					"%s: can't create lockfile", ttyn);
    249 				exit(1);
    250 			}
    251 			(void)unlink(lockfile);
    252 		}
    253 		if (strcmp(argv[0], "+") != 0) {
    254 			(void)chown(ttyn, ttyowner, 0);
    255 			(void)chmod(ttyn, 0600);
    256 			(void)revoke(ttyn);
    257 			if (ttyaction(ttyn, "getty", "root"))
    258 				syslog(LOG_WARNING, "%s: ttyaction failed",
    259 					ttyn);
    260 			/*
    261 			 * Delay the open so DTR stays down long enough
    262 			 * to be detected.
    263 			 */
    264 			(void)sleep(2);
    265 			while ((i = open(ttyn, O_RDWR)) == -1) {
    266 				if ((repcnt % 10 == 0) &&
    267 				    (errno != ENXIO || !failopenlogged)) {
    268 					syslog(LOG_WARNING, "%s: %m", ttyn);
    269 					closelog();
    270 					failopenlogged = 1;
    271 				}
    272 				repcnt++;
    273 				(void)sleep(60);
    274 			}
    275 			if (uugetty && pidlock(lockfile, 0, 0, 0) != 0)  {
    276 				syslog(LOG_ERR, "%s: can't create lockfile",
    277 					ttyn);
    278 				exit(1);
    279 			}
    280 			if (uugetty)
    281 				(void)chown(lockfile, ttyowner, 0);
    282 			(void)login_tty(i);
    283 		}
    284 	}
    285 
    286 	/* Start with default tty settings */
    287 	if (tcgetattr(0, &tmode) < 0) {
    288 		syslog(LOG_ERR, "%s: %m", ttyn);
    289 		exit(1);
    290 	}
    291 	omode = tmode;
    292 
    293 	gettable("default", defent);
    294 	gendefaults();
    295 	tname = "default";
    296 	if (argc > 1)
    297 		tname = argv[1];
    298 	for (;;) {
    299 		int off;
    300 
    301 		rval = 0;
    302 		gettable(tname, tabent);
    303 		if (OPset || EPset || APset)
    304 			APset++, OPset++, EPset++;
    305 		setdefaults();
    306 		off = 0;
    307 		(void)tcflush(0, TCIOFLUSH);	/* clear out the crap */
    308 		(void)ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
    309 		(void)ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
    310 
    311 		if (IS)
    312 			(void)cfsetispeed(&tmode, (speed_t)IS);
    313 		else if (SP)
    314 			(void)cfsetispeed(&tmode, (speed_t)SP);
    315 		if (OS)
    316 			(void)cfsetospeed(&tmode, (speed_t)OS);
    317 		else if (SP)
    318 			(void)cfsetospeed(&tmode, (speed_t)SP);
    319 		setflags(0);
    320 		setchars();
    321 		if (tcsetattr(0, TCSANOW, &tmode) < 0) {
    322 			syslog(LOG_ERR, "%s: %m", ttyn);
    323 			exit(1);
    324 		}
    325 		if (AB) {
    326 			tname = autobaud();
    327 			continue;
    328 		}
    329 		if (PS) {
    330 			tname = portselector();
    331 			continue;
    332 		}
    333 		if (CS)
    334 			clearscreen();
    335 		if (CL && *CL)
    336 			putpad(CL);
    337 		edithost(HE);
    338 
    339 		/*
    340 		 * If this is the first time through this, and an
    341 		 * issue file has been given, then send it.
    342 		 */
    343 		if (first_time != 0 && IF != NULL) {
    344 			char buf[_POSIX2_LINE_MAX];
    345 			FILE *fp;
    346 
    347 			if ((fp = fopen(IF, "r")) != NULL) {
    348 				while (fgets(buf, sizeof(buf) - 1, fp) != NULL)
    349 					putf(buf);
    350 				(void)fclose(fp);
    351 			}
    352 		}
    353 		first_time = 0;
    354 
    355 		if (IM && *IM)
    356 			putf(IM);
    357 		oflush();
    358 		if (setjmp(timeout)) {
    359 			tmode.c_ispeed = tmode.c_ospeed = 0;
    360 			(void)tcsetattr(0, TCSANOW, &tmode);
    361 			exit(1);
    362 		}
    363 		if (TO) {
    364 			(void)signal(SIGALRM, dingdong);
    365 			(void)alarm((unsigned int)TO);
    366 		}
    367 		if (NN) {
    368 			name[0] = '\0';
    369 			lower = 1;
    370 			upper = digit = 0;
    371 		} else if (AL) {
    372 			const char *p = AL;
    373 			char *q = name;
    374 
    375 			while (*p && q < &name[sizeof name - 1]) {
    376 				if (isupper((unsigned char)*p))
    377 					upper = 1;
    378 				else if (islower((unsigned char)*p))
    379 					lower = 1;
    380 				else if (isdigit((unsigned char)*p))
    381 					digit++;
    382 				*q++ = *p++;
    383 			}
    384 		} else if ((rval = getname()) == 2) {
    385 			setflags(2);
    386 			(void)execle(PP, "ppplogin", ttyn, (char *) 0, env);
    387 			syslog(LOG_ERR, "%s: %m", PP);
    388 			exit(1);
    389 		}
    390 
    391 		if (rval || AL || NN) {
    392 			int i;
    393 
    394 			oflush();
    395 			(void)alarm(0);
    396 			(void)signal(SIGALRM, SIG_DFL);
    397 			if (name[0] == '-') {
    398 				xputs("user names may not start with '-'.");
    399 				continue;
    400 			}
    401 			if (!(upper || lower || digit))
    402 				continue;
    403 			setflags(2);
    404 			if (crmod) {
    405 				tmode.c_iflag |= ICRNL;
    406 				tmode.c_oflag |= ONLCR;
    407 			}
    408 #if XXX
    409 			if (upper || UC)
    410 				tmode.sg_flags |= LCASE;
    411 			if (lower || LC)
    412 				tmode.sg_flags &= ~LCASE;
    413 #endif
    414 			if (tcsetattr(0, TCSANOW, &tmode) < 0) {
    415 				syslog(LOG_ERR, "%s: %m", ttyn);
    416 				exit(1);
    417 			}
    418 			(void)signal(SIGINT, SIG_DFL);
    419 			for (i = 0; envp[i] != NULL; i++)
    420 				env[i] = envp[i];
    421 			makeenv(&env[i]);
    422 
    423 			limit.rlim_max = RLIM_INFINITY;
    424 			limit.rlim_cur = RLIM_INFINITY;
    425 			(void)setrlimit(RLIMIT_CPU, &limit);
    426 			if (NN)
    427 				(void)execle(LO, "login", AL ? "-fp" : "-p",
    428 				    NULL, env);
    429 			else
    430 				(void)execle(LO, "login", AL ? "-fp" : "-p",
    431 				    "--", name, NULL, env);
    432 			syslog(LOG_ERR, "%s: %m", LO);
    433 			exit(1);
    434 		}
    435 		(void)alarm(0);
    436 		(void)signal(SIGALRM, SIG_DFL);
    437 		(void)signal(SIGINT, SIG_IGN);
    438 		if (NX && *NX)
    439 			tname = NX;
    440 		if (uugetty)
    441 			(void)unlink(lockfile);
    442 	}
    443 }
    444 
    445 static int
    446 getname(void)
    447 {
    448 	int c;
    449 	char *np;
    450 	unsigned char cs;
    451 	int ppp_state, ppp_connection;
    452 
    453 	/*
    454 	 * Interrupt may happen if we use CBREAK mode
    455 	 */
    456 	if (setjmp(intrupt)) {
    457 		(void)signal(SIGINT, SIG_IGN);
    458 		return (0);
    459 	}
    460 	(void)signal(SIGINT, interrupt);
    461 	setflags(1);
    462 	prompt();
    463 	if (PF > 0) {
    464 		oflush();
    465 		(void)sleep((unsigned int)PF);
    466 		PF = 0;
    467 	}
    468 	if (tcsetattr(0, TCSANOW, &tmode) < 0) {
    469 		syslog(LOG_ERR, "%s: %m", ttyn);
    470 		exit(1);
    471 	}
    472 	crmod = digit = lower = upper = 0;
    473 	ppp_state = ppp_connection = 0;
    474 	np = name;
    475 	for (;;) {
    476 		oflush();
    477 		if (read(STDIN_FILENO, &cs, 1) <= 0)
    478 			exit(0);
    479 		if ((c = cs&0177) == 0)
    480 			return (0);
    481 
    482 		/*
    483 		 * PPP detection state machine..
    484 		 * Look for sequences:
    485 		 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
    486 		 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
    487 		 * See RFC1662.
    488 		 * Derived from code from Michael Hancock <michaelh (at) cet.co.jp>
    489 		 * and Erik 'PPP' Olson <eriko (at) wrq.com>
    490 		 */
    491 		if (PP && cs == PPP_FRAME) {
    492 			ppp_state = 1;
    493 		} else if (ppp_state == 1 && cs == PPP_STATION) {
    494 			ppp_state = 2;
    495 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
    496 			ppp_state = 3;
    497 		} else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
    498 		    (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
    499 			ppp_state = 4;
    500 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
    501 			ppp_state = 5;
    502 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
    503 			ppp_connection = 1;
    504 			break;
    505 		} else {
    506 			ppp_state = 0;
    507 		}
    508 
    509 		if (c == EOT)
    510 			exit(1);
    511 		if (c == '\r' || c == '\n' ||
    512 		    np >= &name[LOGIN_NAME_MAX - 1]) {
    513 			*np = '\0';
    514 			putf("\r\n");
    515 			break;
    516 		}
    517 		if (islower(c))
    518 			lower = 1;
    519 		else if (isupper(c))
    520 			upper = 1;
    521 		else if (c == ERASE || c == '#' || c == '\b') {
    522 			if (np > name) {
    523 				np--;
    524 				if (cfgetospeed(&tmode) >= 1200)
    525 					xputs("\b \b");
    526 				else
    527 					putchr(cs);
    528 			}
    529 			continue;
    530 		} else if (c == KILL || c == '@') {
    531 			putchr(cs);
    532 			putchr('\r');
    533 			if (cfgetospeed(&tmode) < 1200)
    534 				putchr('\n');
    535 			/* this is the way they do it down under ... */
    536 			else if (np > name)
    537 				xputs(
    538 				    "                                     \r");
    539 			prompt();
    540 			np = name;
    541 			continue;
    542 		} else if (isdigit(c))
    543 			digit++;
    544 		if (IG && (c <= ' ' || c > 0176))
    545 			continue;
    546 		*np++ = c;
    547 		putchr(cs);
    548 
    549 		/*
    550 		 * An MS-Windows direct connect PPP "client" won't send its
    551 		 * first PPP packet until we respond to its "CLIENT" poll
    552 		 * with a CRLF sequence.  We cater to yet another broken
    553 		 * implementation of a previously-standard protocol...
    554 		 */
    555 		*np = '\0';
    556 		if (strstr(name, "CLIENT"))
    557 			putf("\r\n");
    558 	}
    559 	(void)signal(SIGINT, SIG_IGN);
    560 	*np = 0;
    561 	if (c == '\r')
    562 		crmod = 1;
    563 	if ((upper && !lower && !LC) || UC)
    564 		for (np = name; *np; np++)
    565 			*np = tolower((unsigned char)*np);
    566 	return (1 + ppp_connection);
    567 }
    568 
    569 static void
    570 putpad(const char *s)
    571 {
    572 	int pad = 0;
    573 	speed_t ospd = cfgetospeed(&tmode);
    574 
    575 	if (isdigit((unsigned char)*s)) {
    576 		while (isdigit((unsigned char)*s)) {
    577 			pad *= 10;
    578 			pad += *s++ - '0';
    579 		}
    580 		pad *= 10;
    581 		if (*s == '.' && isdigit((unsigned char)s[1])) {
    582 			pad += s[1] - '0';
    583 			s += 2;
    584 		}
    585 	}
    586 
    587 	xputs(s);
    588 	/*
    589 	 * If no delay needed, or output speed is
    590 	 * not comprehensible, then don't try to delay.
    591 	 */
    592 	if (pad == 0)
    593 		return;
    594 
    595 	/*
    596 	 * Round up by a half a character frame, and then do the delay.
    597 	 * Too bad there are no user program accessible programmed delays.
    598 	 * Transmitting pad characters slows many terminals down and also
    599 	 * loads the system.
    600 	 */
    601 	pad = (pad * ospd + 50000) / 100000;
    602 	while (pad--)
    603 		putchr(*PC);
    604 }
    605 
    606 static void
    607 xputs(const char *s)
    608 {
    609 	while (*s)
    610 		putchr(*s++);
    611 }
    612 
    613 char	outbuf[OBUFSIZ];
    614 size_t	obufcnt = 0;
    615 
    616 static void
    617 putchr(int cc)
    618 {
    619 	unsigned char c;
    620 
    621 	c = cc;
    622 	if (!NP) {
    623 		c |= partab[c&0177] & 0200;
    624 		if (OP)
    625 			c ^= 0200;
    626 	}
    627 	if (!UB) {
    628 		outbuf[obufcnt++] = c;
    629 		if (obufcnt >= OBUFSIZ)
    630 			oflush();
    631 	} else
    632 		(void)write(STDOUT_FILENO, &c, 1);
    633 }
    634 
    635 static void
    636 oflush(void)
    637 {
    638 	if (obufcnt)
    639 		(void)write(STDOUT_FILENO, outbuf, obufcnt);
    640 	obufcnt = 0;
    641 }
    642 
    643 static void
    644 prompt(void)
    645 {
    646 
    647 	putf(LM);
    648 	if (CO)
    649 		putchr('\n');
    650 }
    651 
    652 static void
    653 putf(const char *cp)
    654 {
    655 	time_t t;
    656 	char *slash, db[100];
    657 
    658 	while (*cp) {
    659 		if (*cp != '%') {
    660 			putchr(*cp++);
    661 			continue;
    662 		}
    663 		switch (*++cp) {
    664 
    665 		case 't':
    666 			if ((slash = strstr(ttyn, "/pts/")) == NULL)
    667 				slash = strrchr(ttyn, '/');
    668 			if (slash == NULL)
    669 				xputs(ttyn);
    670 			else
    671 				xputs(&slash[1]);
    672 			break;
    673 
    674 		case 'h':
    675 			xputs(editedhost);
    676 			break;
    677 
    678 		case 'd':
    679 			(void)time(&t);
    680 			(void)strftime(db, sizeof(db),
    681 			    /* SCCS eats %M% */
    682 			    "%l:%M" "%p on %A, %d %B %Y", localtime(&t));
    683 			xputs(db);
    684 			break;
    685 
    686 		case 's':
    687 			xputs(kerninfo.sysname);
    688 			break;
    689 
    690 		case 'm':
    691 			xputs(kerninfo.machine);
    692 			break;
    693 
    694 		case 'r':
    695 			xputs(kerninfo.release);
    696 			break;
    697 
    698 		case 'v':
    699 			xputs(kerninfo.version);
    700 			break;
    701 
    702 		case '%':
    703 			putchr('%');
    704 			break;
    705 		}
    706 		if (*cp)
    707 			cp++;
    708 	}
    709 }
    710 
    711 static void
    712 clearscreen(void)
    713 {
    714 	struct ttyent *typ;
    715 	struct tinfo *tinfo;
    716 	char *cs;
    717 
    718 	if (rawttyn == NULL)
    719 		return;
    720 
    721 	typ = getttynam(rawttyn);
    722 
    723 	if ((typ == NULL) || (typ->ty_type == NULL) ||
    724 	    (typ->ty_type[0] == 0))
    725 		return;
    726 
    727 	if (t_getent(&tinfo, typ->ty_type) <= 0)
    728 		return;
    729 
    730 	cs = t_agetstr(tinfo, "cl");
    731 	if (cs == NULL)
    732 		return;
    733 
    734 	putpad(cs);
    735 }
    736