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