Home | History | Annotate | Line # | Download | only in getty
main.c revision 1.1.1.2
      1      1.1  cgd /*-
      2  1.1.1.2   pk  * Copyright (c) 1980, 1993
      3  1.1.1.2   pk  *	The Regents of the University of California.  All rights reserved.
      4      1.1  cgd  *
      5      1.1  cgd  * Redistribution and use in source and binary forms, with or without
      6      1.1  cgd  * modification, are permitted provided that the following conditions
      7      1.1  cgd  * are met:
      8      1.1  cgd  * 1. Redistributions of source code must retain the above copyright
      9      1.1  cgd  *    notice, this list of conditions and the following disclaimer.
     10      1.1  cgd  * 2. Redistributions in binary form must reproduce the above copyright
     11      1.1  cgd  *    notice, this list of conditions and the following disclaimer in the
     12      1.1  cgd  *    documentation and/or other materials provided with the distribution.
     13      1.1  cgd  * 3. All advertising materials mentioning features or use of this software
     14      1.1  cgd  *    must display the following acknowledgement:
     15      1.1  cgd  *	This product includes software developed by the University of
     16      1.1  cgd  *	California, Berkeley and its contributors.
     17      1.1  cgd  * 4. Neither the name of the University nor the names of its contributors
     18      1.1  cgd  *    may be used to endorse or promote products derived from this software
     19      1.1  cgd  *    without specific prior written permission.
     20      1.1  cgd  *
     21      1.1  cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     22      1.1  cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23      1.1  cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24      1.1  cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     25      1.1  cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26      1.1  cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27      1.1  cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28      1.1  cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29      1.1  cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30      1.1  cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31      1.1  cgd  * SUCH DAMAGE.
     32      1.1  cgd  */
     33      1.1  cgd 
     34      1.1  cgd #ifndef lint
     35  1.1.1.2   pk static char copyright[] =
     36  1.1.1.2   pk "@(#) Copyright (c) 1980, 1993\n\
     37  1.1.1.2   pk 	The Regents of the University of California.  All rights reserved.\n";
     38      1.1  cgd #endif /* not lint */
     39      1.1  cgd 
     40      1.1  cgd #ifndef lint
     41  1.1.1.2   pk static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/20/93";
     42      1.1  cgd #endif /* not lint */
     43      1.1  cgd 
     44      1.1  cgd #define USE_OLD_TTY
     45      1.1  cgd 
     46      1.1  cgd #include <sys/param.h>
     47      1.1  cgd #include <sys/stat.h>
     48  1.1.1.2   pk #include <sys/resource.h>
     49  1.1.1.2   pk 
     50      1.1  cgd #include <ctype.h>
     51      1.1  cgd #include <ctype.h>
     52  1.1.1.2   pk #include <fcntl.h>
     53  1.1.1.2   pk #include <setjmp.h>
     54  1.1.1.2   pk #include <sgtty.h>
     55  1.1.1.2   pk #include <signal.h>
     56      1.1  cgd #include <stdlib.h>
     57      1.1  cgd #include <string.h>
     58  1.1.1.2   pk #include <syslog.h>
     59  1.1.1.2   pk #include <time.h>
     60  1.1.1.2   pk #include <unistd.h>
     61  1.1.1.2   pk 
     62      1.1  cgd #include "gettytab.h"
     63      1.1  cgd #include "pathnames.h"
     64  1.1.1.2   pk #include "extern.h"
     65  1.1.1.2   pk 
     66  1.1.1.2   pk /*
     67  1.1.1.2   pk  * Set the amount of running time that getty should accumulate
     68  1.1.1.2   pk  * before deciding that something is wrong and exit.
     69  1.1.1.2   pk  */
     70  1.1.1.2   pk #define GETTY_TIMEOUT	60 /* seconds */
     71      1.1  cgd 
     72      1.1  cgd struct	sgttyb tmode = {
     73      1.1  cgd 	0, 0, CERASE, CKILL, 0
     74      1.1  cgd };
     75      1.1  cgd struct	tchars tc = {
     76      1.1  cgd 	CINTR, CQUIT, CSTART,
     77      1.1  cgd 	CSTOP, CEOF, CBRK,
     78      1.1  cgd };
     79      1.1  cgd struct	ltchars ltc = {
     80      1.1  cgd 	CSUSP, CDSUSP, CRPRNT,
     81      1.1  cgd 	CFLUSH, CWERASE, CLNEXT
     82      1.1  cgd };
     83      1.1  cgd 
     84      1.1  cgd int crmod, digit, lower, upper;
     85      1.1  cgd 
     86      1.1  cgd char	hostname[MAXHOSTNAMELEN];
     87      1.1  cgd char	name[16];
     88      1.1  cgd char	dev[] = _PATH_DEV;
     89      1.1  cgd char	ttyn[32];
     90      1.1  cgd char	*portselector();
     91      1.1  cgd char	*ttyname();
     92      1.1  cgd 
     93      1.1  cgd #define	OBUFSIZ		128
     94      1.1  cgd #define	TABBUFSIZ	512
     95      1.1  cgd 
     96      1.1  cgd char	defent[TABBUFSIZ];
     97      1.1  cgd char	tabent[TABBUFSIZ];
     98      1.1  cgd 
     99      1.1  cgd char	*env[128];
    100      1.1  cgd 
    101      1.1  cgd char partab[] = {
    102      1.1  cgd 	0001,0201,0201,0001,0201,0001,0001,0201,
    103      1.1  cgd 	0202,0004,0003,0205,0005,0206,0201,0001,
    104      1.1  cgd 	0201,0001,0001,0201,0001,0201,0201,0001,
    105      1.1  cgd 	0001,0201,0201,0001,0201,0001,0001,0201,
    106      1.1  cgd 	0200,0000,0000,0200,0000,0200,0200,0000,
    107      1.1  cgd 	0000,0200,0200,0000,0200,0000,0000,0200,
    108      1.1  cgd 	0000,0200,0200,0000,0200,0000,0000,0200,
    109      1.1  cgd 	0200,0000,0000,0200,0000,0200,0200,0000,
    110      1.1  cgd 	0200,0000,0000,0200,0000,0200,0200,0000,
    111      1.1  cgd 	0000,0200,0200,0000,0200,0000,0000,0200,
    112      1.1  cgd 	0000,0200,0200,0000,0200,0000,0000,0200,
    113      1.1  cgd 	0200,0000,0000,0200,0000,0200,0200,0000,
    114      1.1  cgd 	0000,0200,0200,0000,0200,0000,0000,0200,
    115      1.1  cgd 	0200,0000,0000,0200,0000,0200,0200,0000,
    116      1.1  cgd 	0200,0000,0000,0200,0000,0200,0200,0000,
    117      1.1  cgd 	0000,0200,0200,0000,0200,0000,0000,0201
    118      1.1  cgd };
    119      1.1  cgd 
    120      1.1  cgd #define	ERASE	tmode.sg_erase
    121      1.1  cgd #define	KILL	tmode.sg_kill
    122      1.1  cgd #define	EOT	tc.t_eofc
    123      1.1  cgd 
    124      1.1  cgd jmp_buf timeout;
    125      1.1  cgd 
    126      1.1  cgd static void
    127      1.1  cgd dingdong()
    128      1.1  cgd {
    129      1.1  cgd 
    130      1.1  cgd 	alarm(0);
    131      1.1  cgd 	signal(SIGALRM, SIG_DFL);
    132      1.1  cgd 	longjmp(timeout, 1);
    133      1.1  cgd }
    134      1.1  cgd 
    135      1.1  cgd jmp_buf	intrupt;
    136      1.1  cgd 
    137      1.1  cgd static void
    138      1.1  cgd interrupt()
    139      1.1  cgd {
    140      1.1  cgd 
    141      1.1  cgd 	signal(SIGINT, interrupt);
    142      1.1  cgd 	longjmp(intrupt, 1);
    143      1.1  cgd }
    144      1.1  cgd 
    145  1.1.1.2   pk /*
    146  1.1.1.2   pk  * Action to take when getty is running too long.
    147  1.1.1.2   pk  */
    148  1.1.1.2   pk void
    149  1.1.1.2   pk timeoverrun(signo)
    150  1.1.1.2   pk 	int signo;
    151  1.1.1.2   pk {
    152  1.1.1.2   pk 
    153  1.1.1.2   pk 	syslog(LOG_ERR, "getty exiting due to excessive running time\n");
    154  1.1.1.2   pk 	exit(1);
    155  1.1.1.2   pk }
    156  1.1.1.2   pk 
    157  1.1.1.2   pk static int	getname __P((void));
    158  1.1.1.2   pk static void	oflush __P((void));
    159  1.1.1.2   pk static void	prompt __P((void));
    160  1.1.1.2   pk static void	putchr __P((int));
    161  1.1.1.2   pk static void	putf __P((char *));
    162  1.1.1.2   pk static void	putpad __P((char *));
    163  1.1.1.2   pk static void	puts __P((char *));
    164  1.1.1.2   pk 
    165  1.1.1.2   pk int
    166      1.1  cgd main(argc, argv)
    167      1.1  cgd 	int argc;
    168  1.1.1.2   pk 	char *argv[];
    169      1.1  cgd {
    170  1.1.1.2   pk 	extern char **environ;
    171      1.1  cgd 	char *tname;
    172      1.1  cgd 	long allflags;
    173      1.1  cgd 	int repcnt = 0;
    174  1.1.1.2   pk 	struct rlimit limit;
    175      1.1  cgd 
    176      1.1  cgd 	signal(SIGINT, SIG_IGN);
    177      1.1  cgd /*
    178      1.1  cgd 	signal(SIGQUIT, SIG_DFL);
    179      1.1  cgd */
    180      1.1  cgd 	openlog("getty", LOG_ODELAY|LOG_CONS, LOG_AUTH);
    181      1.1  cgd 	gethostname(hostname, sizeof(hostname));
    182      1.1  cgd 	if (hostname[0] == '\0')
    183      1.1  cgd 		strcpy(hostname, "Amnesiac");
    184  1.1.1.2   pk 
    185  1.1.1.2   pk 	/*
    186  1.1.1.2   pk 	 * Limit running time to deal with broken or dead lines.
    187  1.1.1.2   pk 	 */
    188  1.1.1.2   pk 	(void)signal(SIGXCPU, timeoverrun);
    189  1.1.1.2   pk 	limit.rlim_max = RLIM_INFINITY;
    190  1.1.1.2   pk 	limit.rlim_cur = GETTY_TIMEOUT;
    191  1.1.1.2   pk 	(void)setrlimit(RLIMIT_CPU, &limit);
    192  1.1.1.2   pk 
    193      1.1  cgd 	/*
    194      1.1  cgd 	 * The following is a work around for vhangup interactions
    195      1.1  cgd 	 * which cause great problems getting window systems started.
    196      1.1  cgd 	 * If the tty line is "-", we do the old style getty presuming
    197      1.1  cgd 	 * that the file descriptors are already set up for us.
    198      1.1  cgd 	 * J. Gettys - MIT Project Athena.
    199      1.1  cgd 	 */
    200      1.1  cgd 	if (argc <= 2 || strcmp(argv[2], "-") == 0)
    201      1.1  cgd 	    strcpy(ttyn, ttyname(0));
    202      1.1  cgd 	else {
    203      1.1  cgd 	    int i;
    204      1.1  cgd 
    205      1.1  cgd 	    strcpy(ttyn, dev);
    206      1.1  cgd 	    strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
    207      1.1  cgd 	    if (strcmp(argv[0], "+") != 0) {
    208      1.1  cgd 		chown(ttyn, 0, 0);
    209      1.1  cgd 		chmod(ttyn, 0600);
    210      1.1  cgd 		revoke(ttyn);
    211      1.1  cgd 		/*
    212      1.1  cgd 		 * Delay the open so DTR stays down long enough to be detected.
    213      1.1  cgd 		 */
    214      1.1  cgd 		sleep(2);
    215      1.1  cgd 		while ((i = open(ttyn, O_RDWR)) == -1) {
    216      1.1  cgd 			if (repcnt % 10 == 0) {
    217      1.1  cgd 				syslog(LOG_ERR, "%s: %m", ttyn);
    218      1.1  cgd 				closelog();
    219      1.1  cgd 			}
    220      1.1  cgd 			repcnt++;
    221      1.1  cgd 			sleep(60);
    222      1.1  cgd 		}
    223      1.1  cgd 		login_tty(i);
    224      1.1  cgd 	    }
    225      1.1  cgd 	}
    226      1.1  cgd 
    227  1.1.1.2   pk 	gettable("default", defent);
    228      1.1  cgd 	gendefaults();
    229      1.1  cgd 	tname = "default";
    230      1.1  cgd 	if (argc > 1)
    231      1.1  cgd 		tname = argv[1];
    232      1.1  cgd 	for (;;) {
    233  1.1.1.2   pk 		int off;
    234      1.1  cgd 
    235  1.1.1.2   pk 		gettable(tname, tabent);
    236      1.1  cgd 		if (OPset || EPset || APset)
    237      1.1  cgd 			APset++, OPset++, EPset++;
    238      1.1  cgd 		setdefaults();
    239  1.1.1.2   pk 		off = 0;
    240  1.1.1.2   pk 		ioctl(0, TIOCFLUSH, &off);	/* clear out the crap */
    241      1.1  cgd 		ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
    242      1.1  cgd 		ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
    243      1.1  cgd 		if (IS)
    244      1.1  cgd 			tmode.sg_ispeed = speed(IS);
    245      1.1  cgd 		else if (SP)
    246      1.1  cgd 			tmode.sg_ispeed = speed(SP);
    247      1.1  cgd 		if (OS)
    248      1.1  cgd 			tmode.sg_ospeed = speed(OS);
    249      1.1  cgd 		else if (SP)
    250      1.1  cgd 			tmode.sg_ospeed = speed(SP);
    251      1.1  cgd 		tmode.sg_flags = setflags(0);
    252      1.1  cgd 		ioctl(0, TIOCSETP, &tmode);
    253      1.1  cgd 		setchars();
    254      1.1  cgd 		ioctl(0, TIOCSETC, &tc);
    255      1.1  cgd 		if (HC)
    256      1.1  cgd 			ioctl(0, TIOCHPCL, 0);
    257      1.1  cgd 		if (AB) {
    258      1.1  cgd 			extern char *autobaud();
    259      1.1  cgd 
    260      1.1  cgd 			tname = autobaud();
    261      1.1  cgd 			continue;
    262      1.1  cgd 		}
    263      1.1  cgd 		if (PS) {
    264      1.1  cgd 			tname = portselector();
    265      1.1  cgd 			continue;
    266      1.1  cgd 		}
    267      1.1  cgd 		if (CL && *CL)
    268      1.1  cgd 			putpad(CL);
    269      1.1  cgd 		edithost(HE);
    270      1.1  cgd 		if (IM && *IM)
    271      1.1  cgd 			putf(IM);
    272      1.1  cgd 		if (setjmp(timeout)) {
    273      1.1  cgd 			tmode.sg_ispeed = tmode.sg_ospeed = 0;
    274      1.1  cgd 			ioctl(0, TIOCSETP, &tmode);
    275      1.1  cgd 			exit(1);
    276      1.1  cgd 		}
    277      1.1  cgd 		if (TO) {
    278      1.1  cgd 			signal(SIGALRM, dingdong);
    279      1.1  cgd 			alarm(TO);
    280      1.1  cgd 		}
    281      1.1  cgd 		if (getname()) {
    282      1.1  cgd 			register int i;
    283      1.1  cgd 
    284      1.1  cgd 			oflush();
    285      1.1  cgd 			alarm(0);
    286      1.1  cgd 			signal(SIGALRM, SIG_DFL);
    287      1.1  cgd 			if (name[0] == '-') {
    288      1.1  cgd 				puts("user names may not start with '-'.");
    289      1.1  cgd 				continue;
    290      1.1  cgd 			}
    291      1.1  cgd 			if (!(upper || lower || digit))
    292      1.1  cgd 				continue;
    293      1.1  cgd 			allflags = setflags(2);
    294      1.1  cgd 			tmode.sg_flags = allflags & 0xffff;
    295      1.1  cgd 			allflags >>= 16;
    296      1.1  cgd 			if (crmod || NL)
    297      1.1  cgd 				tmode.sg_flags |= CRMOD;
    298      1.1  cgd 			if (upper || UC)
    299      1.1  cgd 				tmode.sg_flags |= LCASE;
    300      1.1  cgd 			if (lower || LC)
    301      1.1  cgd 				tmode.sg_flags &= ~LCASE;
    302      1.1  cgd 			ioctl(0, TIOCSETP, &tmode);
    303      1.1  cgd 			ioctl(0, TIOCSLTC, &ltc);
    304      1.1  cgd 			ioctl(0, TIOCLSET, &allflags);
    305      1.1  cgd 			signal(SIGINT, SIG_DFL);
    306      1.1  cgd 			for (i = 0; environ[i] != (char *)0; i++)
    307      1.1  cgd 				env[i] = environ[i];
    308      1.1  cgd 			makeenv(&env[i]);
    309      1.1  cgd 
    310      1.1  cgd 			/*
    311      1.1  cgd 			 * this is what login was doing anyway.
    312      1.1  cgd 			 * soon we rewrite getty completely.
    313      1.1  cgd 			 */
    314      1.1  cgd 			set_ttydefaults(0);
    315  1.1.1.2   pk 			limit.rlim_max = RLIM_INFINITY;
    316  1.1.1.2   pk 			limit.rlim_cur = RLIM_INFINITY;
    317  1.1.1.2   pk 			(void)setrlimit(RLIMIT_CPU, &limit);
    318      1.1  cgd 			execle(LO, "login", "-p", name, (char *) 0, env);
    319      1.1  cgd 			syslog(LOG_ERR, "%s: %m", LO);
    320      1.1  cgd 			exit(1);
    321      1.1  cgd 		}
    322      1.1  cgd 		alarm(0);
    323      1.1  cgd 		signal(SIGALRM, SIG_DFL);
    324      1.1  cgd 		signal(SIGINT, SIG_IGN);
    325      1.1  cgd 		if (NX && *NX)
    326      1.1  cgd 			tname = NX;
    327      1.1  cgd 	}
    328      1.1  cgd }
    329      1.1  cgd 
    330  1.1.1.2   pk static int
    331      1.1  cgd getname()
    332      1.1  cgd {
    333      1.1  cgd 	register int c;
    334      1.1  cgd 	register char *np;
    335      1.1  cgd 	char cs;
    336      1.1  cgd 
    337      1.1  cgd 	/*
    338      1.1  cgd 	 * Interrupt may happen if we use CBREAK mode
    339      1.1  cgd 	 */
    340      1.1  cgd 	if (setjmp(intrupt)) {
    341      1.1  cgd 		signal(SIGINT, SIG_IGN);
    342      1.1  cgd 		return (0);
    343      1.1  cgd 	}
    344      1.1  cgd 	signal(SIGINT, interrupt);
    345      1.1  cgd 	tmode.sg_flags = setflags(0);
    346      1.1  cgd 	ioctl(0, TIOCSETP, &tmode);
    347      1.1  cgd 	tmode.sg_flags = setflags(1);
    348      1.1  cgd 	prompt();
    349      1.1  cgd 	if (PF > 0) {
    350      1.1  cgd 		oflush();
    351      1.1  cgd 		sleep(PF);
    352      1.1  cgd 		PF = 0;
    353      1.1  cgd 	}
    354      1.1  cgd 	ioctl(0, TIOCSETP, &tmode);
    355      1.1  cgd 	crmod = digit = lower = upper = 0;
    356      1.1  cgd 	np = name;
    357      1.1  cgd 	for (;;) {
    358      1.1  cgd 		oflush();
    359      1.1  cgd 		if (read(STDIN_FILENO, &cs, 1) <= 0)
    360      1.1  cgd 			exit(0);
    361      1.1  cgd 		if ((c = cs&0177) == 0)
    362      1.1  cgd 			return (0);
    363      1.1  cgd 		if (c == EOT)
    364      1.1  cgd 			exit(1);
    365      1.1  cgd 		if (c == '\r' || c == '\n' || np >= &name[sizeof name]) {
    366      1.1  cgd 			putf("\r\n");
    367      1.1  cgd 			break;
    368      1.1  cgd 		}
    369      1.1  cgd 		if (islower(c))
    370      1.1  cgd 			lower = 1;
    371      1.1  cgd 		else if (isupper(c))
    372      1.1  cgd 			upper = 1;
    373      1.1  cgd 		else if (c == ERASE || c == '#' || c == '\b') {
    374      1.1  cgd 			if (np > name) {
    375      1.1  cgd 				np--;
    376      1.1  cgd 				if (tmode.sg_ospeed >= B1200)
    377      1.1  cgd 					puts("\b \b");
    378      1.1  cgd 				else
    379      1.1  cgd 					putchr(cs);
    380      1.1  cgd 			}
    381      1.1  cgd 			continue;
    382      1.1  cgd 		} else if (c == KILL || c == '@') {
    383      1.1  cgd 			putchr(cs);
    384      1.1  cgd 			putchr('\r');
    385      1.1  cgd 			if (tmode.sg_ospeed < B1200)
    386      1.1  cgd 				putchr('\n');
    387      1.1  cgd 			/* this is the way they do it down under ... */
    388      1.1  cgd 			else if (np > name)
    389      1.1  cgd 				puts("                                     \r");
    390      1.1  cgd 			prompt();
    391      1.1  cgd 			np = name;
    392      1.1  cgd 			continue;
    393      1.1  cgd 		} else if (isdigit(c))
    394      1.1  cgd 			digit++;
    395      1.1  cgd 		if (IG && (c <= ' ' || c > 0176))
    396      1.1  cgd 			continue;
    397      1.1  cgd 		*np++ = c;
    398      1.1  cgd 		putchr(cs);
    399      1.1  cgd 	}
    400      1.1  cgd 	signal(SIGINT, SIG_IGN);
    401      1.1  cgd 	*np = 0;
    402      1.1  cgd 	if (c == '\r')
    403      1.1  cgd 		crmod = 1;
    404      1.1  cgd 	if (upper && !lower && !LC || UC)
    405      1.1  cgd 		for (np = name; *np; np++)
    406      1.1  cgd 			if (isupper(*np))
    407      1.1  cgd 				*np = tolower(*np);
    408      1.1  cgd 	return (1);
    409      1.1  cgd }
    410      1.1  cgd 
    411      1.1  cgd static
    412      1.1  cgd short	tmspc10[] = {
    413      1.1  cgd 	0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5, 15
    414      1.1  cgd };
    415      1.1  cgd 
    416  1.1.1.2   pk static void
    417      1.1  cgd putpad(s)
    418      1.1  cgd 	register char *s;
    419      1.1  cgd {
    420      1.1  cgd 	register pad = 0;
    421      1.1  cgd 	register mspc10;
    422      1.1  cgd 
    423      1.1  cgd 	if (isdigit(*s)) {
    424      1.1  cgd 		while (isdigit(*s)) {
    425      1.1  cgd 			pad *= 10;
    426      1.1  cgd 			pad += *s++ - '0';
    427      1.1  cgd 		}
    428      1.1  cgd 		pad *= 10;
    429      1.1  cgd 		if (*s == '.' && isdigit(s[1])) {
    430      1.1  cgd 			pad += s[1] - '0';
    431      1.1  cgd 			s += 2;
    432      1.1  cgd 		}
    433      1.1  cgd 	}
    434      1.1  cgd 
    435      1.1  cgd 	puts(s);
    436      1.1  cgd 	/*
    437      1.1  cgd 	 * If no delay needed, or output speed is
    438      1.1  cgd 	 * not comprehensible, then don't try to delay.
    439      1.1  cgd 	 */
    440      1.1  cgd 	if (pad == 0)
    441      1.1  cgd 		return;
    442      1.1  cgd 	if (tmode.sg_ospeed <= 0 ||
    443      1.1  cgd 	    tmode.sg_ospeed >= (sizeof tmspc10 / sizeof tmspc10[0]))
    444      1.1  cgd 		return;
    445      1.1  cgd 
    446      1.1  cgd 	/*
    447      1.1  cgd 	 * Round up by a half a character frame, and then do the delay.
    448      1.1  cgd 	 * Too bad there are no user program accessible programmed delays.
    449      1.1  cgd 	 * Transmitting pad characters slows many terminals down and also
    450      1.1  cgd 	 * loads the system.
    451      1.1  cgd 	 */
    452      1.1  cgd 	mspc10 = tmspc10[tmode.sg_ospeed];
    453      1.1  cgd 	pad += mspc10 / 2;
    454      1.1  cgd 	for (pad /= mspc10; pad > 0; pad--)
    455      1.1  cgd 		putchr(*PC);
    456      1.1  cgd }
    457      1.1  cgd 
    458  1.1.1.2   pk static void
    459      1.1  cgd puts(s)
    460      1.1  cgd 	register char *s;
    461      1.1  cgd {
    462      1.1  cgd 	while (*s)
    463      1.1  cgd 		putchr(*s++);
    464      1.1  cgd }
    465      1.1  cgd 
    466      1.1  cgd char	outbuf[OBUFSIZ];
    467      1.1  cgd int	obufcnt = 0;
    468      1.1  cgd 
    469  1.1.1.2   pk static void
    470      1.1  cgd putchr(cc)
    471  1.1.1.2   pk 	int cc;
    472      1.1  cgd {
    473      1.1  cgd 	char c;
    474      1.1  cgd 
    475      1.1  cgd 	c = cc;
    476      1.1  cgd 	if (!NP) {
    477      1.1  cgd 		c |= partab[c&0177] & 0200;
    478      1.1  cgd 		if (OP)
    479      1.1  cgd 			c ^= 0200;
    480      1.1  cgd 	}
    481      1.1  cgd 	if (!UB) {
    482      1.1  cgd 		outbuf[obufcnt++] = c;
    483      1.1  cgd 		if (obufcnt >= OBUFSIZ)
    484      1.1  cgd 			oflush();
    485      1.1  cgd 	} else
    486      1.1  cgd 		write(STDOUT_FILENO, &c, 1);
    487      1.1  cgd }
    488      1.1  cgd 
    489  1.1.1.2   pk static void
    490      1.1  cgd oflush()
    491      1.1  cgd {
    492      1.1  cgd 	if (obufcnt)
    493      1.1  cgd 		write(STDOUT_FILENO, outbuf, obufcnt);
    494      1.1  cgd 	obufcnt = 0;
    495      1.1  cgd }
    496      1.1  cgd 
    497  1.1.1.2   pk static void
    498      1.1  cgd prompt()
    499      1.1  cgd {
    500      1.1  cgd 
    501      1.1  cgd 	putf(LM);
    502      1.1  cgd 	if (CO)
    503      1.1  cgd 		putchr('\n');
    504      1.1  cgd }
    505      1.1  cgd 
    506  1.1.1.2   pk static void
    507      1.1  cgd putf(cp)
    508      1.1  cgd 	register char *cp;
    509      1.1  cgd {
    510      1.1  cgd 	extern char editedhost[];
    511      1.1  cgd 	time_t t;
    512      1.1  cgd 	char *slash, db[100];
    513      1.1  cgd 
    514      1.1  cgd 	while (*cp) {
    515      1.1  cgd 		if (*cp != '%') {
    516      1.1  cgd 			putchr(*cp++);
    517      1.1  cgd 			continue;
    518      1.1  cgd 		}
    519      1.1  cgd 		switch (*++cp) {
    520      1.1  cgd 
    521      1.1  cgd 		case 't':
    522  1.1.1.2   pk 			slash = strrchr(ttyn, '/');
    523      1.1  cgd 			if (slash == (char *) 0)
    524      1.1  cgd 				puts(ttyn);
    525      1.1  cgd 			else
    526      1.1  cgd 				puts(&slash[1]);
    527      1.1  cgd 			break;
    528      1.1  cgd 
    529      1.1  cgd 		case 'h':
    530      1.1  cgd 			puts(editedhost);
    531      1.1  cgd 			break;
    532      1.1  cgd 
    533      1.1  cgd 		case 'd': {
    534      1.1  cgd 			static char fmt[] = "%l:% %P on %A, %d %B %Y";
    535      1.1  cgd 
    536      1.1  cgd 			fmt[4] = 'M';		/* I *hate* SCCS... */
    537      1.1  cgd 			(void)time(&t);
    538      1.1  cgd 			(void)strftime(db, sizeof(db), fmt, localtime(&t));
    539      1.1  cgd 			puts(db);
    540      1.1  cgd 			break;
    541      1.1  cgd 		}
    542      1.1  cgd 
    543      1.1  cgd 		case '%':
    544      1.1  cgd 			putchr('%');
    545      1.1  cgd 			break;
    546      1.1  cgd 		}
    547      1.1  cgd 		cp++;
    548      1.1  cgd 	}
    549      1.1  cgd }
    550