xref: /illumos-gate/usr/src/ucbcmd/tset/tset.c (revision de81e71e)
1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved  	*/
8 
9 
10 /*
11  * Copyright (c) 1980 Regents of the University of California.
12  * All rights reserved. The Berkeley software License Agreement
13  * specifies the terms and conditions for redistribution.
14  */
15 
16 /*
17  *  TSET -- set terminal modes
18  *
19  *	This program does sophisticated terminal initialization.
20  *	I recommend that you include it in your .profile or .login
21  *	file to initialize whatever terminal you are on.
22  *
23  *	There are several features:
24  *
25  *	A special file or sequence (as controlled by the termcap file)
26  *	is sent to the terminal.
27  *
28  *	Mode bits are set on a per-terminal_type basis (much better
29  *	than UNIX itself).  This allows special delays, automatic
30  *	tabs, etc.
31  *
32  *	Erase and Kill characters can be set to whatever you want.
33  *	Default is to change erase to control-H on a terminal which
34  *	can overstrike, and leave it alone on anything else.  Kill
35  *	is always left alone unless specifically requested.  These
36  *	characters can be represented as "^X" meaning control-X;
37  *	X is any character.
38  *
39  *	Terminals which are dialups or plugboard types can be aliased
40  *	to whatever type you may have in your home or office.  Thus,
41  *	if you know that when you dial up you will always be on a
42  *	TI 733, you can specify that fact to tset.  You can represent
43  *	a type as "?type".  This will ask you what type you want it
44  *	to be -- if you reply with just a newline, it will default
45  *	to the type given.
46  *
47  *	The current terminal type can be queried.
48  *
49  *	Usage:
50  *		tset [-] [-EC] [-eC] [-kC] [-iC] [-s] [-h] [-u] [-r]
51  *			[-m [ident] [test baudrate] :type]
52  *			[-Q] [-I] [-S] [type]
53  *
54  *		In systems with environments, use:
55  *			eval `tset -s ...`
56  *		Actually, this doesn't work in old csh's.
57  *		Instead, use:
58  *			tset -s ... > tset.tmp
59  *			source tset.tmp
60  *			rm tset.tmp
61  *		or:
62  *			set noglob
63  *			set term=(`tset -S ....`)
64  *			setenv TERM $term[1]
65  *			setenv TERMCAP "$term[2]"
66  *			unset term
67  *			unset noglob
68  *
69  *	Positional Parameters:
70  *		type -- the terminal type to force.  If this is
71  *			specified, initialization is for this
72  *			terminal type.
73  *
74  *	Flags:
75  *		- -- report terminal type.  Whatever type is
76  *			decided on is reported.  If no other flags
77  *			are stated, the only affect is to write
78  *			the terminal type on the standard output.
79  *		-r -- report to user in addition to other flags.
80  *		-EC -- set the erase character to C on all terminals
81  *			except those which cannot backspace (e.g.,
82  *			a TTY 33).  C defaults to control-H.
83  *		-eC -- set the erase character to C on all terminals.
84  *			C defaults to control-H.  If not specified,
85  *			the erase character is untouched; however, if
86  *			not specified and the erase character is NULL
87  *			(zero byte), the erase character is set to CERASE.
88  *		-kC -- set the kill character to C on all terminals.
89  *			Default for C is control-U.  If not specified,
90  *			the kill character is untouched; however, if
91  *			not specified and the kill character is NULL
92  *			(zero byte), the kill character is set to CKILL.
93  *		-iC -- set the interrupt character to C on all terminals.
94  *			Default for C is control-C.  If not specified, the
95  *			interrupt character is untouched; however, if
96  *			not specified and the interrupt character is NULL
97  *			(zero byte), the interrupt character is set to
98  *			control-C.
99  *		-qC -- reserved for setable quit character.
100  *		-m -- map the system identified type to some user
101  *			specified type. The mapping can be baud rate
102  *			dependent. This replaces the old -d, -p flags.
103  *			(-d type  ->  -m dialup:type)
104  *			(-p type  ->  -m plug:type)
105  *			Syntax:	-m identifier [test baudrate] :type
106  *			where: ``identifier'' is terminal type found in
107  *			/etc/ttys for this port, (abscence of an identifier
108  *			matches any identifier); ``test'' may be any combination
109  *			of  >  =  <  !  @; ``baudrate'' is as with stty(1);
110  *			``type'' is the actual terminal type to use if the
111  *			mapping condition is met. Multiple maps are scanned
112  *			in order and the first match prevails.
113  *		-n -- If the new tty driver from UCB is available, this flag
114  *			will activate the new options for erase and kill
115  *			processing. This will be different for printers
116  *			and crt's. For crts, if the baud rate is < 1200 then
117  *			erase and kill don't remove characters from the screen.
118  *		-h -- don't read htmp file.  Normally the terminal type
119  *			is determined by reading the htmp file or the
120  *			environment (unless some mapping is specified).
121  *			This forces a read of the ttytype file -- useful
122  *			when htmp is somehow wrong. (V6 only)
123  *		-u -- don't update htmp.  It seemed like this should
124  *			be put in.  Note that htmp is never actually
125  *			written if there are no changes, so don't bother
126  *			bother using this for efficiency reasons alone.
127  *		-s -- output setenv commands for TERM.  This can be
128  *			used with
129  *				`tset -s ...`
130  *			and is to be prefered to:
131  *				setenv TERM `tset - ...`
132  *			because -s sets the TERMCAP variable also.
133  *		-S -- Similar to -s but outputs 2 strings suitable for
134  *			use in csh .login files as follows:
135  *				set noglob
136  *				set term=(`tset -S .....`)
137  *				setenv TERM $term[1]
138  *				setenv TERMCAP "$term[2]"
139  *				unset term
140  *				unset noglob
141  *		-Q -- be quiet.  don't output 'Erase set to' etc.
142  *		-I -- don't do terminal initialization (is & if
143  *			strings).
144  *		-v -- On virtual terminal systems, don't set up a
145  *			virtual terminal.  Otherwise tset will tell
146  *			the operating system what kind of terminal you
147  *			are on (if it is a known terminal) and fix up
148  *			the output of -s to use virtual terminal sequences.
149  *
150  *	Files:
151  *		/etc/ttys
152  *			contains a terminal id -> terminal type
153  *			mapping; used when any user mapping is specified,
154  *			or the environment doesn't have TERM set.
155  *		/etc/termcap
156  *			a terminal_type -> terminal_capabilities
157  *			mapping.
158  *
159  *	Return Codes:
160  *		-1 -- couldn't open termcap.
161  *		1 -- bad terminal type, or standard output not tty.
162  *		0 -- ok.
163  *
164  *	Defined Constants:
165  *		DIALUP -- the type code for a dialup port.
166  *		PLUGBOARD -- the type code for a plugboard port.
167  *		ARPANET -- the type code for an arpanet port.
168  *		BACKSPACE -- control-H, the default for -e.
169  *		CNTL('U') -- control-U, the default for -k.
170  *		OLDERASE -- the ancient default erase character.
171  *		FILEDES -- the file descriptor to do the operation
172  *			on, nominally 1 or 2.
173  *		STDOUT -- the standard output file descriptor.
174  *		UIDMASK -- the bit pattern to mask with the getuid()
175  *			call to get just the user id.
176  *		GTTYN -- defines file containing generalized ttynames
177  *			and compiles code to look there.
178  *
179  *	Requires:
180  *		Routines to handle htmp, ttys, and termcap.
181  *
182  *	Compilation Flags:
183  *		OLDFLAGS -- must be defined to compile code for any of
184  *			the -d, -p, or -a flags.
185  *		OLDDIALUP -- accept the -d flag.
186  *		OLDPLUGBOARD -- accept the -p flag.
187  *		OLDARPANET -- accept the -a flag.
188  *		V6 -- if clear, use environments, not htmp.
189  *			also use TIOCSETN rather than stty to avoid flushing
190  *		GTTYN -- if set, compiles code to look at /etc/ttys.
191  *
192  *	Trace Flags:
193  *		none
194  *
195  *	Diagnostics:
196  *		Bad flag
197  *			An incorrect option was specified.
198  *		Too few args
199  *			more command line arguments are required.
200  *		Unexpected arg
201  *			wrong type of argument was encountered.
202  *		Cannot open ...
203  *			The specified file could not be openned.
204  *		Type ... unknown
205  *			An unknown terminal type was specified.
206  *		Cannot update htmp
207  *			Cannot update htmp file when the standard
208  *			output is not a terminal.
209  *		Erase set to ...
210  *			Telling that the erase character has been
211  *			set to the specified character.
212  *		Kill set to ...
213  *			Ditto for kill
214  *		Erase is ...    Kill is ...
215  *			Tells that the erase/kill characters were
216  *			wierd before, but they are being left as-is.
217  *		Not a terminal
218  *			Set if FILEDES is not a terminal.
219  *
220  *	Compilation Instructions:
221  *		cc -n -O tset.c -ltermlib
222  *		mv a.out tset
223  *		chown bin tset
224  *		chmod 4755 tset
225  *
226  *		where 'bin' should be whoever owns the 'htmp' file.
227  *		If 'htmp' is 666, then tset need not be setuid.
228  *
229  *		For version 6 the compile command should be:
230  *		cc -n -O -I/usr/include/retrofit tset.c -ltermlib -lretro -lS
231  *
232  *
233  *	History:
234  *		1/81 -- Added alias checking for mapping identifiers.
235  *		7/80 -- '-S' added. '-m' mapping added. TERMCAP string
236  *			cleaned up.
237  *		3/80 -- Changed to use tputs.  Prc & flush added.
238  *		10/79 -- '-s' option extended to handle TERMCAP
239  *			variable, set noglob, quote the entry,
240  *			and know about the Bourne shell.  Terminal
241  *			initialization moved to before any information
242  *			output so screen clears would not screw you.
243  *			'-Q' option added.
244  *		8/79 -- '-' option alone changed to only output
245  *			type.  '-s' option added.  'VERSION7'
246  *			changed to 'V6' for compatibility.
247  *		12/78 -- modified for eventual migration to VAX/UNIX,
248  *			so the '-' option is changed to output only
249  *			the terminal type to STDOUT instead of
250  *			FILEDES.
251  *		9/78 -- '-' and '-p' options added (now fully
252  *			compatible with ttytype!), and spaces are
253  *			permitted between the -d and the type.
254  *		8/78 -- The sense of -h and -u were reversed, and the
255  *			-f flag is dropped -- same effect is available
256  *			by just stating the terminal type.
257  *		10/77 -- Written.
258  */
259 
260 
261 #define	index strchr
262 #define	rindex strrchr
263 #define	curerase modes.c_cc[VERASE]
264 #define	curkill modes.c_cc[VKILL]
265 #define	curintr modes.c_cc[VINTR]
266 #define	olderase oldmodes.c_cc[VERASE]
267 #define	oldkill oldmodes.c_cc[VKILL]
268 #define	oldintr oldmodes.c_cc[VINTR]
269 
270 #include	<stdio.h>
271 #include	<termio.h>
272 #include	<signal.h>
273 
274 
275 #define	YES		1
276 #define	NO		0
277 #undef CNTL
278 #define	CNTL(c)		((c)&037)
279 #define	BACKSPACE	(CNTL('H'))
280 #define	isdigit(c)	(c >= '0' && c <= '9')
281 #define	isalnum(c)	(c > ' ' && (index("<@=>!:|\177", c) == NULL))
282 #define	OLDERASE	'#'
283 
284 /* default special characters */
285 #ifndef CERASE
286 #define	CERASE	'\177'
287 #endif
288 #ifndef CKILL
289 #define	CKILL	CNTL('U')
290 #endif
291 #ifndef CINTR
292 #define	CINTR	CNTL('C')
293 #endif
294 #ifndef CDSUSP
295 #define	CQUIT	034		/* FS, ^\ */
296 #define	CSTART	CNTL('Q')
297 #define	CSTOP	CNTL('S')
298 #define	CEOF	CNTL('D')
299 #define	CEOT	CEOF
300 #define	CBRK	0377
301 #define	CSUSP	CNTL('Z')
302 #define	CDSUSP	CNTL('Y')
303 #define	CRPRNT	CNTL('R')
304 #define	CFLUSH	CNTL('O')
305 #define	CWERASE	CNTL('W')
306 #define	CLNEXT	CNTL('V')
307 #endif
308 
309 #define	FILEDES		2	/* do gtty/stty on this descriptor */
310 #define	STDOUT		1	/* output of -s/-S to this descriptor */
311 
312 #define	UIDMASK		-1
313 
314 #define	USAGE	"usage: tset [-] [-rsIQS] [-eC] [-kC] "	\
315 		"[-iC] [-m [ident][test speed]:type] [type]\n"
316 
317 #define	OLDFLAGS
318 #define	DIALUP		"dialup"
319 #define	OLDDIALUP	"sd"
320 #define	PLUGBOARD	"plugboard"
321 #define	OLDPLUGBOARD	"sp"
322 
323 #define	DEFTYPE		"unknown"
324 
325 #define	NOTTY		'x'
326 
327 /*
328  * Baud Rate Conditionals
329  */
330 #define	ANY		0
331 #define	GT		1
332 #define	EQ		2
333 #define	LT		4
334 #define	GE		(GT|EQ)
335 #define	LE		(LT|EQ)
336 #define	NE		(GT|LT)
337 #define	ALL		(GT|EQ|LT)
338 
339 
340 
341 #define	NMAP		10
342 
343 struct	map {
344 	char *Ident;
345 	char Test;
346 	char Speed;
347 	char *Type;
348 } map[NMAP];
349 
350 struct map *Map = map;
351 
352 /* This should be available in an include file */
353 struct
354 {
355 	char	*string;
356 	int	speed;
357 	int	baudrate;
358 } speeds[] = {
359 	"0",	B0,	0,
360 	"50",	B50,	50,
361 	"75",	B75,	75,
362 	"110",	B110,	110,
363 	"134",	B134,	134,
364 	"134.5", B134,	134,
365 	"150",	B150,	150,
366 	"200",	B200,	200,
367 	"300",	B300,	300,
368 	"600",	B600,	600,
369 	"1200",	B1200,	1200,
370 	"1800",	B1800,	1800,
371 	"2400",	B2400,	2400,
372 	"4800",	B4800,	4800,
373 	"9600",	B9600,	9600,
374 	"19200", EXTA,	19200,
375 	"exta",	EXTA,	19200,
376 	"extb",	EXTB,	38400,
377 	"57600", B57600,	57600,
378 	"76800", B76800,	76800,
379 	"115200", B115200, 115200,
380 	"153600", B153600, 153600,
381 	"230400", B230400, 230400,
382 	"307200", B307200, 307200,
383 	"460800", B460800, 460800,
384 	"921600", B921600, 921600,
385 	0,
386 };
387 
388 signed char Erase_char;		/* new erase character */
389 char	Kill_char;		/* new kill character */
390 char	Intr_char;		/* new interrupt character */
391 char	Specialerase;	/* set => Erase_char only on terminals with backspace */
392 
393 char	Ttyid = NOTTY;		/* terminal identifier */
394 char	*TtyType;		/* type of terminal */
395 char	*DefType;		/* default type if none other computed */
396 char	*NewType;		/* mapping identifier based on old flags */
397 int	Mapped;			/* mapping has been specified */
398 int	Dash_u;			/* don't update htmp */
399 int	Dash_h;			/* don't read htmp */
400 int	DoSetenv;		/* output setenv commands */
401 int	BeQuiet;		/* be quiet */
402 int	NoInit;			/* don't output initialization string */
403 int	IsReset;		/* invoked as reset */
404 int	Report;			/* report current type */
405 int	Ureport;		/* report to user */
406 int	RepOnly;		/* report only */
407 int	CmndLine;		/* output full command lines (-s option) */
408 int	Ask;			/* ask user for termtype */
409 int	DoVirtTerm = YES;	/* Set up a virtual terminal */
410 int	PadBaud;		/* Min rate of padding needed */
411 
412 #define	CAPBUFSIZ	1024
413 char	Capbuf[CAPBUFSIZ];	/* line from /etc/termcap for this TtyType */
414 char	*Ttycap;		/* termcap line from termcap or environ */
415 
416 char	Aliasbuf[128];
417 char	*Alias[16];
418 
419 extern char *strcpy();
420 extern char *index();
421 
422 struct delay
423 {
424 	int	d_delay;
425 	int	d_bits;
426 };
427 
428 #include	"tset.delays.h"
429 
430 struct termio	mode;
431 struct termio	oldmode;
432 struct termios	modes;
433 struct termios	oldmodes;
434 int		istermios;
435 
436 void reportek(char *, char, char, char);
437 void setdelay(char *, struct delay [], tcflag_t, tcflag_t *);
438 void prs(char *);
439 void prc(char);
440 void flush(void);
441 void cat(char *);
442 void bmove(char *, char *, int);
443 void makealias(char *);
444 void wrtermcap(char *);
445 void fatal(char *, char *);
446 char reset();			/* Routine for checking&resetting chars */
447 
448 int
449 main(int argc, char *argv[])
450 {
451 	char		buf[CAPBUFSIZ];
452 	char		termbuf[32];
453 	auto char	*bufp;
454 	char		*p;
455 	char		*command;
456 	int		i;
457 	int		Break;
458 	int		Not;
459 	char		*nextarg();
460 	char		*mapped();
461 	extern char	*rindex();
462 	struct winsize	win;
463 	extern char	*getenv();
464 	extern char	*tgetstr();
465 	char		bs_char;
466 	int		csh;
467 	int		settle = NO;
468 	void		setmode();
469 	extern char	PC;
470 	extern short	ospeed;
471 
472 	if ((istermios = ioctl(FILEDES, TCGETS, (char *)&modes)) < 0) {
473 		if (ioctl(FILEDES, TCGETA, (char *)&mode) < 0) {
474 			prs("Not a terminal\n");
475 			exit(1);
476 		}
477 		bmove((char *)&mode, (char *)&oldmode, sizeof (mode));
478 		modes.c_lflag = oldmodes.c_lflag = mode.c_lflag;
479 		modes.c_oflag = oldmodes.c_oflag = mode.c_oflag;
480 		modes.c_iflag = oldmodes.c_iflag = mode.c_iflag;
481 		modes.c_cflag = oldmodes.c_cflag = mode.c_cflag;
482 		for (i = 0; i < NCC; i++)
483 			modes.c_cc[i] = oldmodes.c_cc[i] = mode.c_cc[i];
484 	} else
485 		bmove((char *)&modes, (char *)&oldmodes, sizeof (modes));
486 	ospeed = cfgetospeed(&modes);
487 	(void) signal(SIGINT, setmode);
488 	(void) signal(SIGQUIT, setmode);
489 	(void) signal(SIGTERM, setmode);
490 
491 	if (command = rindex(argv[0], '/'))
492 		command++;
493 	else
494 		command = argv[0];
495 	if (sequal(command, "reset")) {
496 		/*
497 		 * Reset the teletype mode bits to a sensible state.
498 		 * Copied from the program by Kurt Shoens & Mark Horton.
499 		 * Very useful after crapping out in raw.
500 		 */
501 		if ((istermios = ioctl(FILEDES, TCGETS, (char *)&modes)) < 0) {
502 			(void) ioctl(FILEDES, TCGETA, (char *)&mode);
503 			modes.c_lflag = mode.c_lflag;
504 			modes.c_oflag = mode.c_oflag;
505 			modes.c_iflag = mode.c_iflag;
506 			modes.c_cflag = mode.c_cflag;
507 			for (i = 0; i < NCC; i++)
508 				modes.c_cc[i] = mode.c_cc[i];
509 		}
510 		curerase = reset(curerase, CERASE);
511 		curkill = reset(curkill, CKILL);
512 		curintr = reset(curintr, CINTR);
513 		modes.c_cc[VQUIT] = reset(modes.c_cc[VQUIT], CQUIT);
514 		modes.c_cc[VEOF] = reset(modes.c_cc[VEOF], CEOF);
515 
516 		modes.c_iflag |= (BRKINT|ISTRIP|ICRNL|IXON);
517 		modes.c_iflag &= ~(IGNBRK|PARMRK|INPCK|INLCR|IGNCR|IUCLC|IXOFF);
518 		modes.c_oflag |= (OPOST|ONLCR);
519 		modes.c_oflag &= ~(OLCUC|OCRNL|ONOCR|ONLRET|OFILL|OFDEL|
520 		    NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
521 		modes.c_cflag |= (CS7|CREAD);
522 		modes.c_cflag &= ~(PARODD|CLOCAL);
523 		modes.c_lflag |= (ISIG|ICANON|ECHO|ECHOK);
524 		modes.c_lflag &= ~(XCASE|ECHONL|NOFLSH);
525 		if (istermios < 0) {
526 			mode.c_lflag = modes.c_lflag;
527 			mode.c_oflag = modes.c_oflag;
528 			mode.c_iflag = modes.c_iflag;
529 			mode.c_cflag = modes.c_cflag;
530 			for (i = 0; i < NCC; i++)
531 				mode.c_cc[i] = modes.c_cc[i];
532 			(void) ioctl(FILEDES, TCSETAW, (char *)&mode);
533 		} else
534 			(void) ioctl(FILEDES, TCSETSW, (char *)&modes);
535 		Dash_u = YES;
536 		BeQuiet = YES;
537 		IsReset = YES;
538 	} else if (argc == 2 && sequal(argv[1], "-")) {
539 		RepOnly = YES;
540 		Dash_u = YES;
541 	}
542 	argc--;
543 
544 	/* scan argument list and collect flags */
545 	while (--argc >= 0) {
546 		p = *++argv;
547 		if (*p == '-') {
548 			if (*++p == NULL)
549 				Report = YES; /* report current terminal type */
550 			else
551 				while (*p)
552 					switch (*p++) {
553 
554 			case 'r':	/* report to user */
555 				Ureport = YES;
556 				continue;
557 
558 			case 'E':
559 				/* special erase: operate on all but TTY33 */
560 				Specialerase = YES;
561 				/* explicit fall-through to -e case */
562 
563 			case 'e':	/* erase character */
564 				if (*p == NULL)
565 					Erase_char = -1;
566 				else {
567 					if (*p == '^' && p[1] != NULL)
568 						if (*++p == '?')
569 							Erase_char = '\177';
570 						else
571 							Erase_char = CNTL(*p);
572 					else
573 						Erase_char = *p;
574 					p++;
575 				}
576 				continue;
577 
578 			case 'i':	/* interrupt character */
579 				if (*p == NULL)
580 					Intr_char = CNTL('C');
581 				else {
582 					if (*p == '^' && p[1] != NULL)
583 						if (*++p == '?')
584 							Intr_char = '\177';
585 						else
586 							Intr_char = CNTL(*p);
587 					else
588 						Intr_char = *p;
589 					p++;
590 				}
591 				continue;
592 
593 			case 'k':	/* kill character */
594 				if (*p == NULL)
595 					Kill_char = CNTL('U');
596 				else {
597 					if (*p == '^' && p[1] != NULL)
598 						if (*++p == '?')
599 							Kill_char = '\177';
600 						else
601 							Kill_char = CNTL(*p);
602 					else
603 						Kill_char = *p;
604 					p++;
605 				}
606 				continue;
607 
608 #ifdef OLDFLAGS
609 #ifdef	OLDDIALUP
610 			case 'd':	/* dialup type */
611 				NewType = DIALUP;
612 				goto mapold;
613 #endif
614 
615 #ifdef OLDPLUGBOARD
616 			case 'p':	/* plugboard type */
617 				NewType = PLUGBOARD;
618 				goto mapold;
619 #endif
620 
621 #ifdef OLDARPANET
622 			case 'a':	/* arpanet type */
623 				Newtype = ARPANET;
624 				goto mapold;
625 #endif
626 
627 mapold:				Map->Ident = NewType;
628 				Map->Test = ALL;
629 				if (*p == NULL) {
630 					p = nextarg(argc--, argv++);
631 				}
632 				Map->Type = p;
633 				Map++;
634 				Mapped = YES;
635 				p = "";
636 				continue;
637 #endif
638 
639 			case 'm':	/* map identifier to type */
640 				/*
641 				 * This code is very loose. Almost no
642 				 * syntax checking is done!! However,
643 				 * illegal syntax will only produce
644 				 * weird results.
645 				 */
646 				if (*p == NULL) {
647 					p = nextarg(argc--, argv++);
648 				}
649 				if (isalnum(*p)) {
650 					Map->Ident = p;	/* identifier */
651 					while (isalnum(*p)) p++;
652 				}
653 				else
654 					Map->Ident = "";
655 				Break = NO;
656 				Not = NO;
657 				while (!Break)
658 					switch (*p) {
659 					case NULL:
660 						p = nextarg(argc--, argv++);
661 						continue;
662 
663 					case ':':	/* mapped type */
664 						*p++ = NULL;
665 						Break = YES;
666 						continue;
667 
668 					case '>':	/* conditional */
669 						Map->Test |= GT;
670 						*p++ = NULL;
671 						continue;
672 
673 					case '<':	/* conditional */
674 						Map->Test |= LT;
675 						*p++ = NULL;
676 						continue;
677 
678 					case '=':	/* conditional */
679 					case '@':
680 						Map->Test |= EQ;
681 						*p++ = NULL;
682 						continue;
683 
684 					case '!':	/* invert conditions */
685 						Not = ~Not;
686 						*p++ = NULL;
687 						continue;
688 
689 					case 'B':	/* Baud rate */
690 						p++;
691 						/* intentional fallthru */
692 					default:
693 						if (isdigit(*p) || *p == 'e') {
694 							Map->Speed =
695 							    baudrate(p);
696 							while (isalnum(*p) ||
697 							    *p == '.')
698 								p++;
699 						} else
700 							Break = YES;
701 						continue;
702 				}
703 				if (Not) {	/* invert sense of test */
704 					Map->Test = (~(Map->Test))&ALL;
705 				}
706 				if (*p == NULL) {
707 					p = nextarg(argc--, argv++);
708 				}
709 				Map->Type = p;
710 				p = "";
711 				Map++;
712 				Mapped = YES;
713 				continue;
714 
715 			case 'h':	/* don't get type from htmp or env */
716 				Dash_h = YES;
717 				continue;
718 
719 			case 'u':	/* don't update htmp */
720 				Dash_u = YES;
721 				continue;
722 
723 			case 's':	/* output setenv commands */
724 				DoSetenv = YES;
725 				CmndLine = YES;
726 				continue;
727 
728 			case 'S':	/* output setenv strings */
729 				DoSetenv = YES;
730 				CmndLine = NO;
731 				continue;
732 
733 			case 'Q':	/* be quiet */
734 				BeQuiet = YES;
735 				continue;
736 
737 			case 'I':	/* no initialization */
738 				NoInit = YES;
739 				continue;
740 
741 			case 'A':	/* Ask user */
742 				Ask = YES;
743 				continue;
744 
745 			case 'v':	/* no virtual terminal */
746 				DoVirtTerm = NO;
747 				continue;
748 
749 			default:
750 				*p-- = NULL;
751 				fatal("Bad flag -", p);
752 			}
753 		} else {
754 			/* terminal type */
755 			DefType = p;
756 		}
757 	}
758 
759 	if (DefType) {
760 		if (Mapped) {
761 			Map->Ident = "";	/* means "map any type" */
762 			Map->Test = ALL;	/* at all baud rates */
763 			Map->Type = DefType;	/* to the default type */
764 		} else
765 			TtyType = DefType;
766 	}
767 
768 	/*
769 	 * Get rid of $TERMCAP, if it's there, so we get a real
770 	 * entry from /etc/termcap.  This prevents us from being
771 	 * fooled by out of date stuff in the environment, and
772 	 * makes tabs work right on CB/Unix.
773 	 */
774 	bufp = getenv("TERMCAP");
775 	if (bufp && *bufp != '/')
776 		(void) strcpy(bufp-8, "NOTHING"); /* overwrite only "TERMCAP" */
777 	/* get current idea of terminal type from environment */
778 	if (!Dash_h && TtyType == 0)
779 		TtyType = getenv("TERM");
780 
781 	if (!RepOnly && Ttyid == NOTTY && (TtyType == 0 || !Dash_h))
782 		Ttyid = ttyname(FILEDES);
783 
784 	/* If still undefined, use DEFTYPE */
785 	if (TtyType == 0) {
786 		TtyType = DEFTYPE;
787 	}
788 
789 	/* check for dialup or other mapping */
790 	if (Mapped) {
791 		if (!(Alias[0] && isalias(TtyType)))
792 			if (tgetent(Capbuf, TtyType) > 0)
793 				makealias(Capbuf);
794 		TtyType = mapped(TtyType);
795 	}
796 
797 	/* TtyType now contains a pointer to the type of the terminal */
798 	/* If the first character is '?', ask the user */
799 	if (TtyType[0] == '?') {
800 		Ask = YES;
801 		TtyType++;
802 		if (TtyType[0] == '\0')
803 			TtyType = DEFTYPE;
804 	}
805 	if (Ask) {
806 ask:
807 		prs("TERM = (");
808 		prs(TtyType);
809 		prs(") ");
810 		flush();
811 
812 		/* read the terminal.  If not empty, set type */
813 		i = read(2, termbuf, sizeof (termbuf) - 1);
814 		if (i > 0) {
815 			if (termbuf[i - 1] == '\n')
816 				i--;
817 			termbuf[i] = '\0';
818 			if (termbuf[0] != '\0')
819 				TtyType = termbuf;
820 		}
821 	}
822 
823 	/* get terminal capabilities */
824 	if (!(Alias[0] && isalias(TtyType))) {
825 		switch (tgetent(Capbuf, TtyType)) {
826 		case -1:
827 			prs("Cannot find termcap\n");
828 			flush();
829 			exit(-1);
830 
831 		case 0:
832 			prs("Type ");
833 			prs(TtyType);
834 			prs(" unknown\n");
835 			flush();
836 			if (DoSetenv) {
837 				TtyType = DEFTYPE;
838 				Alias[0] = '\0';
839 				goto ask;
840 			} else
841 				exit(1);
842 		}
843 	}
844 	Ttycap = Capbuf;
845 
846 	if (!RepOnly) {
847 		/* determine erase and kill characters */
848 		if (Specialerase && !tgetflag("bs"))
849 			Erase_char = 0;
850 		bufp = buf;
851 		p = tgetstr("kb", &bufp);
852 		if (p == NULL || p[1] != '\0')
853 			p = tgetstr("bc", &bufp);
854 		if (p != NULL && p[1] == '\0')
855 			bs_char = p[0];
856 		else if (tgetflag("bs"))
857 			bs_char = BACKSPACE;
858 		else
859 			bs_char = 0;
860 		/*
861 		 * The next statement can't be fixed, because now users
862 		 * depend on keeping their erase character as DEL if the
863 		 * system set it there.  People who want backspace have
864 		 * to say tset -e.
865 		 */
866 		if (Erase_char == 0 && !tgetflag("os") &&
867 		    curerase == OLDERASE) {
868 			if (tgetflag("bs") || bs_char != 0)
869 				Erase_char = -1;
870 		}
871 		if (Erase_char < 0)
872 			Erase_char = (bs_char != 0) ? bs_char : BACKSPACE;
873 
874 		if (curerase == 0)
875 			curerase = CERASE;
876 		if (Erase_char != 0)
877 			curerase = Erase_char;
878 
879 		if (curintr == 0)
880 			curintr = CINTR;
881 		if (Intr_char != 0)
882 			curintr = Intr_char;
883 
884 		if (curkill == 0)
885 			curkill = CKILL;
886 		if (Kill_char != 0)
887 			curkill = Kill_char;
888 
889 		/* set modes */
890 		PadBaud = tgetnum("pb");	/* OK if fails */
891 		for (i = 0; speeds[i].string; i++)
892 			if (speeds[i].baudrate == PadBaud) {
893 				PadBaud = speeds[i].speed;
894 				break;
895 			}
896 		setdelay("dC", CRdelay, CRbits, &modes.c_oflag);
897 		setdelay("dN", NLdelay, NLbits, &modes.c_oflag);
898 		setdelay("dB", BSdelay, BSbits, &modes.c_oflag);
899 		setdelay("dF", FFdelay, FFbits, &modes.c_oflag);
900 		setdelay("dT", TBdelay, TBbits, &modes.c_oflag);
901 		setdelay("dV", VTdelay, VTbits, &modes.c_oflag);
902 
903 		if (tgetflag("UC") || (command[0] & 0140) == 0100) {
904 			modes.c_iflag |= IUCLC;
905 			modes.c_oflag |= OLCUC;
906 			modes.c_cflag |= XCASE;
907 		} else if (tgetflag("LC")) {
908 			modes.c_iflag &= ~IUCLC;
909 			modes.c_oflag &= ~OLCUC;
910 			modes.c_cflag &= ~XCASE;
911 		}
912 		modes.c_iflag &= ~(PARMRK|INPCK);
913 		modes.c_lflag |= ICANON;
914 		if (tgetflag("EP")) {
915 			modes.c_iflag |= INPCK;
916 			modes.c_cflag |= PARENB;
917 			modes.c_cflag &= ~PARODD;
918 		}
919 		if (tgetflag("OP")) {
920 			modes.c_iflag |= INPCK;
921 			modes.c_cflag |= PARENB;
922 			modes.c_cflag |= PARODD;
923 		}
924 
925 		modes.c_oflag |= ONLCR;
926 		modes.c_iflag |= ICRNL;
927 		modes.c_lflag |= ECHO;
928 		modes.c_oflag |= TAB3;
929 		if (tgetflag("NL")) {	/* new line, not line feed */
930 			modes.c_oflag &= ~ONLCR;
931 			modes.c_iflag &= ~ICRNL;
932 		}
933 		if (tgetflag("HD"))	/* half duplex */
934 			modes.c_lflag &= ~ECHO;
935 		if (tgetflag("pt"))	/* print tabs */
936 			modes.c_oflag &= ~TAB3;
937 
938 		modes.c_lflag |= (ECHOE|ECHOK);
939 		if (tgetflag("hc")) {	/* set printer modes */
940 			modes.c_lflag &= ~ECHOE;
941 		}
942 
943 		/* get pad character */
944 		bufp = buf;
945 		if (tgetstr("pc", &bufp) != 0)
946 			PC = buf[0];
947 
948 		/* output startup string */
949 		if (!NoInit) {
950 			if (oldmodes.c_oflag&(TAB3|ONLCR|OCRNL|ONLRET)) {
951 				oldmodes.c_oflag &= (TAB3|ONLCR|OCRNL|ONLRET);
952 				setmode(-1);
953 			}
954 			if (settabs()) {
955 				settle = YES;
956 				flush();
957 			}
958 			bufp = buf;
959 			if (IsReset && tgetstr("rs", &bufp) != 0 ||
960 			    tgetstr("is", &bufp) != 0) {
961 				tputs(buf, 0, prc);
962 				settle = YES;
963 				flush();
964 			}
965 			bufp = buf;
966 			if (IsReset && tgetstr("rf", &bufp) != 0 ||
967 			    tgetstr("if", &bufp) != 0) {
968 				cat(buf);
969 				settle = YES;
970 			}
971 			if (settle) {
972 				prc('\r');
973 				if (IsReset)
974 					prc('\n');  /* newline too */
975 				flush();
976 				sleep(1);	/* let terminal settle down */
977 			}
978 		}
979 
980 		setmode(0);	/* set new modes, if they've changed */
981 
982 		/* set up environment for the shell we are using */
983 		/* (this code is rather heuristic, checking for $SHELL */
984 		/* ending in the 3 characters "csh") */
985 		csh = NO;
986 		if (DoSetenv) {
987 			char *sh;
988 
989 			if ((sh = getenv("SHELL")) && (i = strlen(sh)) >= 3) {
990 				if ((csh = sequal(&sh[i-3], "csh")) && CmndLine)
991 					(void) write(STDOUT,
992 					    "set noglob;\n", 12);
993 			}
994 			if (!csh) {	/* running Bourne shell */
995 				(void) write(STDOUT,
996 				    "export TERMCAP TERM;\n", 21);
997 			}
998 		}
999 	}
1000 
1001 	/* report type if appropriate */
1002 	if (DoSetenv || Report || Ureport) {
1003 		/* if type is the short name, find first alias (if any) */
1004 		makealias(Ttycap);
1005 		if (sequal(TtyType, Alias[0]) && Alias[1]) {
1006 			TtyType = Alias[1];
1007 		}
1008 
1009 		if (DoSetenv) {
1010 			if (csh) {
1011 				if (CmndLine)
1012 					(void) write(STDOUT,
1013 					    "setenv TERM ", 12);
1014 				(void) write(STDOUT, TtyType, strlen(TtyType));
1015 				(void) write(STDOUT, " ", 1);
1016 				if (CmndLine)
1017 					(void) write(STDOUT, ";\n", 2);
1018 			} else {
1019 				(void) write(STDOUT, "TERM=", 5);
1020 				(void) write(STDOUT, TtyType, strlen(TtyType));
1021 				(void) write(STDOUT, ";\n", 2);
1022 			}
1023 		} else if (Report) {
1024 			(void) write(STDOUT, TtyType, strlen(TtyType));
1025 			(void) write(STDOUT, "\n", 1);
1026 		}
1027 		if (Ureport) {
1028 			prs("Terminal type is ");
1029 			prs(TtyType);
1030 			prs("\n");
1031 			flush();
1032 		}
1033 
1034 		if (DoSetenv) {
1035 			if (csh) {
1036 				if (CmndLine)
1037 					(void) write(STDOUT,
1038 					    "setenv TERMCAP '", 16);
1039 			} else
1040 				(void) write(STDOUT, "TERMCAP='", 9);
1041 			wrtermcap(Ttycap);
1042 			if (csh) {
1043 				if (CmndLine) {
1044 					(void) write(STDOUT, "';\n", 3);
1045 					(void) write(STDOUT,
1046 					    "unset noglob;\n", 14);
1047 				}
1048 			} else
1049 				(void) write(STDOUT, "';\n", 3);
1050 		}
1051 	}
1052 
1053 	if (RepOnly)
1054 		exit(0);
1055 
1056 	/* tell about changing erase, kill and interrupt characters */
1057 	reportek("Erase", curerase, olderase, CERASE);
1058 	reportek("Kill", curkill, oldkill, CKILL);
1059 	reportek("Interrupt", curintr, oldintr, CINTR);
1060 
1061 	return (0);
1062 }
1063 
1064 /*
1065  * Set the hardware tabs on the terminal, using the ct (clear all tabs),
1066  * st (set one tab) and ch (horizontal cursor addressing) capabilities.
1067  * This is done before if and is, so they can patch in case we blow this.
1068  */
1069 int
1070 settabs(void)
1071 {
1072 	char caps[100];
1073 	char *capsp = caps;
1074 	char *clear_tabs, *set_tab, *set_column, *set_pos;
1075 	char *tg_out, *tgoto();
1076 	int c;
1077 	extern char *tgetstr();
1078 	int lines, columns;
1079 
1080 	clear_tabs = tgetstr("ct", &capsp);
1081 	set_tab = tgetstr("st", &capsp);
1082 	set_column = tgetstr("ch", &capsp);
1083 	if (set_column == 0)
1084 		set_pos = tgetstr("cm", &capsp);
1085 
1086 	if (clear_tabs && set_tab) {
1087 		prc('\r');	/* force to be at left margin */
1088 		tputs(clear_tabs, 0, prc);
1089 	}
1090 	if (set_tab) {
1091 		columns = tgetnum("co");
1092 		lines = tgetnum("li");
1093 		for (c = 0; c < columns; c += 8) {
1094 			/* get to that column. */
1095 			tg_out = "OOPS";	/* also returned by tgoto */
1096 			if (set_column)
1097 				tg_out = tgoto(set_column, 0, c);
1098 			if (*tg_out == 'O' && set_pos)
1099 				tg_out = tgoto(set_pos, c, lines-1);
1100 			if (*tg_out != 'O')
1101 				tputs(tg_out, 1, prc);
1102 			else if (c != 0) {
1103 				prc(' '); prc(' '); prc(' '); prc(' ');
1104 				prc(' '); prc(' '); prc(' '); prc(' ');
1105 			}
1106 			/* set the tab */
1107 			tputs(set_tab, 0, prc);
1108 		}
1109 		prc('\r');
1110 		return (1);
1111 	}
1112 	return (0);
1113 }
1114 
1115 /*
1116  * flag serves several purposes:
1117  *	if called as the result of a signal, flag will be > 0.
1118  *	if called from terminal init, flag == -1 means reset "oldmode".
1119  *	called with flag == 0 at end of normal mode processing.
1120  */
1121 void
1122 setmode(int flag)
1123 {
1124 	struct termio *ttymode;
1125 	struct termios *ttymodes;
1126 	int i;
1127 
1128 	ttymode = (struct termio *)0;
1129 	ttymodes = (struct termios *)0;
1130 
1131 	if (flag < 0) { /* unconditionally reset oldmode (called from init) */
1132 		if (istermios < 0) {
1133 			oldmode.c_lflag = oldmodes.c_lflag;
1134 			oldmode.c_oflag = oldmodes.c_oflag;
1135 			oldmode.c_iflag = oldmodes.c_iflag;
1136 			oldmode.c_cflag = oldmodes.c_cflag;
1137 			for (i = 0; i < NCC; i++)
1138 				oldmode.c_cc[i] = oldmodes.c_cc[i];
1139 			ttymode = &oldmode;
1140 		} else
1141 			ttymodes = &oldmodes;
1142 	} else {
1143 		if (istermios < 0) {
1144 			oldmode.c_lflag = oldmodes.c_lflag;
1145 			oldmode.c_oflag = oldmodes.c_oflag;
1146 			oldmode.c_iflag = oldmodes.c_iflag;
1147 			oldmode.c_cflag = oldmodes.c_cflag;
1148 			for (i = 0; i < NCC; i++)
1149 				oldmode.c_cc[i] = oldmodes.c_cc[i];
1150 			mode.c_lflag = modes.c_lflag;
1151 			mode.c_oflag = modes.c_oflag;
1152 			mode.c_iflag = modes.c_iflag;
1153 			mode.c_cflag = modes.c_cflag;
1154 			for (i = 0; i < NCC; i++)
1155 				mode.c_cc[i] = modes.c_cc[i];
1156 			if (!bequal((char *)&mode, (char *)&oldmode,
1157 			    sizeof (mode)))
1158 				ttymode = &mode;
1159 		} else if (!bequal((char *)&modes, (char *)&oldmodes,
1160 		    sizeof (modes)))
1161 			ttymodes = &modes;
1162 	}
1163 
1164 	if (ttymode) {
1165 		(void) ioctl(FILEDES, TCSETAW, (char *)ttymode);
1166 	} else if (ttymodes) {
1167 		(void) ioctl(FILEDES, TCSETSW, (char *)ttymodes);
1168 	}
1169 	if (flag > 0)	/* trapped signal */
1170 		exit(1);
1171 }
1172 
1173 void
1174 reportek(char *name, char new, char old, char def)
1175 {
1176 	char	o;
1177 	char	n;
1178 	char	*p;
1179 	char		buf[32];
1180 	char		*bufp;
1181 	extern char *tgetstr();
1182 
1183 	if (BeQuiet)
1184 		return;
1185 	o = old;
1186 	n = new;
1187 
1188 	if (o == n && n == def)
1189 		return;
1190 	prs(name);
1191 	if (o == n)
1192 		prs(" is ");
1193 	else
1194 		prs(" set to ");
1195 	bufp = buf;
1196 	if (tgetstr("kb", &bufp) > (char *)0 && n == buf[0] && buf[1] == NULL)
1197 		prs("Backspace\n");
1198 	else if (n == 0177)
1199 		prs("Delete\n");
1200 	else {
1201 		if (n < 040) {
1202 			prs("Ctrl-");
1203 			n ^= 0100;
1204 		}
1205 		p = "x\n";
1206 		p[0] = n;
1207 		prs(p);
1208 	}
1209 	flush();
1210 }
1211 
1212 
1213 
1214 void
1215 setdelay(char *cap, struct delay dtab[], tcflag_t bits, tcflag_t *flags)
1216 {
1217 	int		i;
1218 	struct delay	*p;
1219 	extern short	ospeed;
1220 
1221 	/* see if this capability exists at all */
1222 	i = tgetnum(cap);
1223 	if (i < 0)
1224 		i = 0;
1225 	/* No padding at speeds below PadBaud */
1226 	if (PadBaud > ospeed)
1227 		i = 0;
1228 
1229 	/* clear out the bits, replace with new ones */
1230 	*flags &= ~bits;
1231 
1232 	/* scan dtab for first entry with adequate delay */
1233 	for (p = dtab; p->d_delay >= 0; p++) {
1234 		if (p->d_delay >= i) {
1235 			p++;
1236 			break;
1237 		}
1238 	}
1239 
1240 	/* use last entry if none will do */
1241 	*flags |= (tcflag_t)((--p)->d_bits);
1242 }
1243 
1244 void
1245 prs(char *s)
1246 {
1247 	while (*s != '\0')
1248 		prc(*s++);
1249 }
1250 
1251 
1252 char	OutBuf[256];
1253 int	OutPtr;
1254 
1255 void
1256 prc(char c)
1257 {
1258 	OutBuf[OutPtr++] = c;
1259 	if (OutPtr >= sizeof (OutBuf))
1260 		flush();
1261 }
1262 
1263 void
1264 flush(void)
1265 {
1266 	if (OutPtr > 0)
1267 		(void) write(2, OutBuf, OutPtr);
1268 	OutPtr = 0;
1269 }
1270 
1271 void
1272 cat(char *file)
1273 {
1274 	int	fd;
1275 	int	i;
1276 	char		buf[BUFSIZ];
1277 
1278 	fd = open(file, 0);
1279 	if (fd < 0) {
1280 		prs("Cannot open ");
1281 		prs(file);
1282 		prs("\n");
1283 		flush();
1284 		return;
1285 	}
1286 
1287 	while ((i = read(fd, buf, BUFSIZ)) > 0)
1288 		(void) write(FILEDES, buf, i);
1289 
1290 	(void) close(fd);
1291 }
1292 
1293 
1294 void
1295 bmove(char *from, char *to, int length)
1296 {
1297 	char	*p, *q;
1298 	int	i;
1299 
1300 	i = length;
1301 	p = from;
1302 	q = to;
1303 
1304 	while (i-- > 0)
1305 		*q++ = *p++;
1306 }
1307 
1308 
1309 int
1310 bequal(char *a, char *b, int len)	/* must be same thru len chars */
1311 {
1312 	char	*p, *q;
1313 	int	i;
1314 
1315 	i = len;
1316 	p = a;
1317 	q = b;
1318 
1319 	while ((*p == *q) && --i > 0) {
1320 		p++; q++;
1321 	}
1322 	return ((*p == *q) && i >= 0);
1323 }
1324 
1325 int
1326 sequal(char *a, char *b)	/* must be same thru NULL */
1327 {
1328 	char *p = a, *q = b;
1329 
1330 	while (*p && *q && (*p == *q)) {
1331 		p++; q++;
1332 	}
1333 	return (*p == *q);
1334 }
1335 
1336 void
1337 makealias(char *buf)
1338 {
1339 	int i;
1340 	char *a;
1341 	char *b;
1342 
1343 	Alias[0] = a = Aliasbuf;
1344 	b = buf;
1345 	i = 1;
1346 	while (*b && *b != ':') {
1347 		if (*b == '|') {
1348 			*a++ = NULL;
1349 			Alias[i++] = a;
1350 			b++;
1351 		} else
1352 			*a++ = *b++;
1353 	}
1354 	*a = NULL;
1355 	Alias[i] = NULL;
1356 #ifdef	DEB
1357 	for (i = 0; Alias[i]; printf("A:%s\n", Alias[i++]))
1358 		;
1359 #endif
1360 }
1361 
1362 int
1363 isalias(char *ident)	/* is ident same as one of the aliases? */
1364 {
1365 	char **a = Alias;
1366 
1367 	if (*a)
1368 		while (*a)
1369 			if (sequal(ident, *a))
1370 				return (YES);
1371 			else
1372 				a++;
1373 	return (NO);
1374 }
1375 
1376 
1377 /*
1378  * routine to output the string for the environment TERMCAP variable
1379  */
1380 #define	WHITE(c)	(c == ' ' || c == '\t')
1381 char delcap[128][2];
1382 int ncap = 0;
1383 
1384 void
1385 wrtermcap(char *bp)
1386 {
1387 	char buf[CAPBUFSIZ];
1388 	char *p = buf;
1389 	char *tp;
1390 	char *putbuf();
1391 	int space, empty;
1392 
1393 	/* discard names with blanks */
1394 /* May not be desireable ? */
1395 	while (*bp && *bp != ':') {
1396 		if (*bp == '|') {
1397 			tp = bp+1;
1398 			space = NO;
1399 			while (*tp && *tp != '|' && *tp != ':') {
1400 				space = (space || WHITE(*tp));
1401 				tp++;
1402 			}
1403 			if (space) {
1404 				bp = tp;
1405 				continue;
1406 			}
1407 		}
1408 		*p++ = *bp++;
1409 	}
1410 /* */
1411 
1412 	while (*bp) {
1413 		switch (*bp) {
1414 		case ':':	/* discard empty, cancelled  or dupl fields */
1415 			tp = bp + 1;
1416 			empty = YES;
1417 			while (*tp && *tp != ':') {
1418 				empty = (empty && WHITE(*tp));
1419 				tp++;
1420 			}
1421 			if (empty || cancelled(bp+1)) {
1422 				bp = tp;
1423 				continue;
1424 			}
1425 			break;
1426 
1427 		case ' ':	/* no spaces in output */
1428 			p = putbuf(p, "\\040");
1429 			bp++;
1430 			continue;
1431 
1432 		case '!':	/* the shell thinks this is history */
1433 			p = putbuf(p, "\\041");
1434 			bp++;
1435 			continue;
1436 
1437 		case ',':	/* the shell thinks this is history */
1438 			p = putbuf(p, "\\054");
1439 			bp++;
1440 			continue;
1441 
1442 		case '"':	/* no quotes in output */
1443 			p = putbuf(p, "\\042");
1444 			bp++;
1445 			continue;
1446 
1447 		case '\'':	/* no quotes in output */
1448 			p = putbuf(p, "\\047");
1449 			bp++;
1450 			continue;
1451 
1452 		case '`':	/* no back quotes in output */
1453 			p = putbuf(p, "\\140");
1454 			bp++;
1455 			continue;
1456 
1457 		case '\\':
1458 		case '^':	/* anything following is OK */
1459 			*p++ = *bp++;
1460 		}
1461 		*p++ = *bp++;
1462 	}
1463 	*p++ = ':';	/* we skipped the last : with the : lookahead hack */
1464 	(void) write(STDOUT, buf, p-buf);
1465 }
1466 
1467 int
1468 cancelled(char *cap)
1469 {
1470 	int i;
1471 
1472 	for (i = 0; i < ncap; i++) {
1473 		if (cap[0] == delcap[i][0] && cap[1] == delcap[i][1])
1474 			return (YES);
1475 	}
1476 	/* delete a second occurrance of the same capability */
1477 	delcap[ncap][0] = cap[0];
1478 	delcap[ncap][1] = cap[1];
1479 	ncap++;
1480 	return (cap[2] == '@');
1481 }
1482 
1483 char *
1484 putbuf(ptr, str)
1485 char	*ptr;
1486 char	*str;
1487 {
1488 	char buf[20];
1489 
1490 	while (*str) {
1491 		switch (*str) {
1492 		case '\033':
1493 			ptr = putbuf(ptr, "\\E");
1494 			str++;
1495 			break;
1496 		default:
1497 			if (*str <= ' ') {
1498 				(void) sprintf(buf, "\\%03o", *str);
1499 				ptr = putbuf(ptr, buf);
1500 				str++;
1501 			} else
1502 				*ptr++ = *str++;
1503 		}
1504 	}
1505 	return (ptr);
1506 }
1507 
1508 int
1509 baudrate(char *p)
1510 {
1511 	char buf[8];
1512 	int i = 0;
1513 
1514 	while (i < 7 && (isalnum(*p) || *p == '.'))
1515 		buf[i++] = *p++;
1516 	buf[i] = NULL;
1517 	for (i = 0; speeds[i].string; i++)
1518 		if (sequal(speeds[i].string, buf))
1519 			return (speeds[i].speed);
1520 	return (-1);
1521 }
1522 
1523 char *
1524 mapped(type)
1525 char	*type;
1526 {
1527 	extern short	ospeed;
1528 	int	match;
1529 
1530 #ifdef DEB
1531 	printf("spd:%d\n", ospeed);
1532 	prmap();
1533 #endif
1534 	Map = map;
1535 	while (Map->Ident) {
1536 		if (*(Map->Ident) == NULL ||
1537 		    sequal(Map->Ident, type) || isalias(Map->Ident)) {
1538 			match = NO;
1539 			switch (Map->Test) {
1540 			case ANY:	/* no test specified */
1541 			case ALL:
1542 				match = YES;
1543 				break;
1544 
1545 			case GT:
1546 				match = (ospeed > Map->Speed);
1547 				break;
1548 
1549 			case GE:
1550 				match = (ospeed >= Map->Speed);
1551 				break;
1552 
1553 			case EQ:
1554 				match = (ospeed == Map->Speed);
1555 				break;
1556 
1557 			case LE:
1558 				match = (ospeed <= Map->Speed);
1559 				break;
1560 
1561 			case LT:
1562 				match = (ospeed < Map->Speed);
1563 				break;
1564 
1565 			case NE:
1566 				match = (ospeed != Map->Speed);
1567 				break;
1568 			}
1569 			if (match)
1570 				return (Map->Type);
1571 		}
1572 		Map++;
1573 	}
1574 	/* no match found; return given type */
1575 	return (type);
1576 }
1577 
1578 #ifdef DEB
1579 prmap()
1580 {
1581 	Map = map;
1582 	while (Map->Ident) {
1583 		printf("%s t:%d s:%d %s\n",
1584 		    Map->Ident, Map->Test, Map->Speed, Map->Type);
1585 		Map++;
1586 	}
1587 }
1588 #endif
1589 
1590 char *
1591 nextarg(argc, argv)
1592 int	argc;
1593 char	*argv[];
1594 {
1595 	if (argc <= 0)
1596 		fatal("Too few args: ", *argv);
1597 	if (*(*++argv) == '-')
1598 		fatal("Unexpected arg: ", *argv);
1599 	return (*argv);
1600 }
1601 
1602 void
1603 fatal(char *mesg, char *obj)
1604 {
1605 	prs(mesg);
1606 	prs(obj);
1607 	prc('\n');
1608 	prs(USAGE);
1609 	flush();
1610 	exit(1);
1611 }
1612 
1613 
1614 /*
1615  * Stolen from /usr/src/ucb/reset.c, which this mod obsoletes.
1616  */
1617 char
1618 reset(ch, def)
1619 	char ch;
1620 	int def;
1621 {
1622 	if (ch == 0 || (ch&0377) == 0377)
1623 		return (def);
1624 	return (ch);
1625 }
1626