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