xref: /illumos-gate/usr/src/ucbcmd/tset/tset.c (revision bbcfe1fdf407bba4db5c2e62a69b67be0c4cb443)
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	<stdlib.h>
272 #include	<termio.h>
273 #include	<signal.h>
274 
275 
276 #define	YES		1
277 #define	NO		0
278 #undef CNTL
279 #define	CNTL(c)		((c)&037)
280 #define	BACKSPACE	(CNTL('H'))
281 #define	isdigit(c)	(c >= '0' && c <= '9')
282 #define	isalnum(c)	(c > ' ' && (index("<@=>!:|\177", c) == NULL))
283 #define	OLDERASE	'#'
284 
285 /* default special characters */
286 #ifndef CERASE
287 #define	CERASE	'\177'
288 #endif
289 #ifndef CKILL
290 #define	CKILL	CNTL('U')
291 #endif
292 #ifndef CINTR
293 #define	CINTR	CNTL('C')
294 #endif
295 #ifndef CDSUSP
296 #define	CQUIT	034		/* FS, ^\ */
297 #define	CSTART	CNTL('Q')
298 #define	CSTOP	CNTL('S')
299 #define	CEOF	CNTL('D')
300 #define	CEOT	CEOF
301 #define	CBRK	0377
302 #define	CSUSP	CNTL('Z')
303 #define	CDSUSP	CNTL('Y')
304 #define	CRPRNT	CNTL('R')
305 #define	CFLUSH	CNTL('O')
306 #define	CWERASE	CNTL('W')
307 #define	CLNEXT	CNTL('V')
308 #endif
309 
310 #define	FILEDES		2	/* do gtty/stty on this descriptor */
311 #define	STDOUT		1	/* output of -s/-S to this descriptor */
312 
313 #define	UIDMASK		-1
314 
315 #define	USAGE	"usage: tset [-] [-rsIQS] [-eC] [-kC] "	\
316 		"[-iC] [-m [ident][test speed]:type] [type]\n"
317 
318 #define	OLDFLAGS
319 #define	DIALUP		"dialup"
320 #define	OLDDIALUP	"sd"
321 #define	PLUGBOARD	"plugboard"
322 #define	OLDPLUGBOARD	"sp"
323 
324 #define	DEFTYPE		"unknown"
325 
326 /*
327  * Baud Rate Conditionals
328  */
329 #define	ANY		0
330 #define	GT		1
331 #define	EQ		2
332 #define	LT		4
333 #define	GE		(GT|EQ)
334 #define	LE		(LT|EQ)
335 #define	NE		(GT|LT)
336 #define	ALL		(GT|EQ|LT)
337 
338 
339 
340 #define	NMAP		10
341 
342 struct	map {
343 	char *Ident;
344 	char Test;
345 	char Speed;
346 	char *Type;
347 } map[NMAP];
348 
349 struct map *Map = map;
350 
351 struct {
352 	char	*string;
353 	int	speed;
354 	int	baudrate;
355 } speeds[] = {
356 	"0",		B0,		0,
357 	"50",		B50,		50,
358 	"75",		B75,		75,
359 	"110",		B110,		110,
360 	"134",		B134,		134,
361 	"134.5",	B134,		134,
362 	"150",		B150,		150,
363 	"200",		B200,		200,
364 	"300",		B300,		300,
365 	"600",		B600,		600,
366 	"1200",		B1200,		1200,
367 	"1800",		B1800,		1800,
368 	"2400",		B2400,		2400,
369 	"4800",		B4800,		4800,
370 	"9600",		B9600,		9600,
371 	"19200",	EXTA,		19200,
372 	"exta",		EXTA,		19200,
373 	"extb",		EXTB,		38400,
374 	"57600",	B57600,		57600,
375 	"76800",	B76800,		76800,
376 	"115200",	B115200,	115200,
377 	"153600",	B153600,	153600,
378 	"230400",	B230400,	230400,
379 	"307200",	B307200,	307200,
380 	"460800",	B460800,	460800,
381 	"921600",	B921600,	921600,
382 	"1000000",	B1000000,	1000000,
383 	"1152000",	B1152000,	1152000,
384 	"1500000",	B1500000,	1500000,
385 	"2000000",	B2000000,	2000000,
386 	"2500000",	B2500000,	2500000,
387 	"3000000",	B3000000,	3000000,
388 	"3500000",	B3500000,	3500000,
389 	"4000000",	B4000000,	4000000,
390 	0,
391 };
392 
393 signed char Erase_char;		/* new erase character */
394 char	Kill_char;		/* new kill character */
395 char	Intr_char;		/* new interrupt character */
396 char	Specialerase;	/* set => Erase_char only on terminals with backspace */
397 
398 char	*TtyType;		/* type of terminal */
399 char	*DefType;		/* default type if none other computed */
400 char	*NewType;		/* mapping identifier based on old flags */
401 int	Mapped;			/* mapping has been specified */
402 int	Dash_u;			/* don't update htmp */
403 int	Dash_h;			/* don't read htmp */
404 int	DoSetenv;		/* output setenv commands */
405 int	BeQuiet;		/* be quiet */
406 int	NoInit;			/* don't output initialization string */
407 int	IsReset;		/* invoked as reset */
408 int	Report;			/* report current type */
409 int	Ureport;		/* report to user */
410 int	RepOnly;		/* report only */
411 int	CmndLine;		/* output full command lines (-s option) */
412 int	Ask;			/* ask user for termtype */
413 int	DoVirtTerm = YES;	/* Set up a virtual terminal */
414 int	PadBaud;		/* Min rate of padding needed */
415 
416 #define	CAPBUFSIZ	1024
417 char	Capbuf[CAPBUFSIZ];	/* line from /etc/termcap for this TtyType */
418 char	*Ttycap;		/* termcap line from termcap or environ */
419 
420 char	Aliasbuf[128];
421 char	*Alias[16];
422 
423 extern char *strcpy();
424 extern char *index();
425 
426 struct delay
427 {
428 	int	d_delay;
429 	int	d_bits;
430 };
431 
432 #include	"tset.delays.h"
433 
434 struct termio	mode;
435 struct termio	oldmode;
436 struct termios	modes;
437 struct termios	oldmodes;
438 int		istermios;
439 
440 void reportek(char *, char, char, char);
441 void setdelay(char *, struct delay [], tcflag_t, tcflag_t *);
442 void prs(char *);
443 void prc(char);
444 void flush(void);
445 void cat(char *);
446 void bmove(char *, char *, int);
447 void makealias(char *);
448 void wrtermcap(char *);
449 void fatal(char *, char *);
450 char reset();			/* Routine for checking&resetting chars */
451 
452 int
453 main(int argc, char *argv[])
454 {
455 	char		buf[CAPBUFSIZ];
456 	char		termbuf[32];
457 	auto char	*bufp;
458 	char		*p;
459 	char		*command;
460 	int		i;
461 	int		Break;
462 	int		Not;
463 	char		*nextarg();
464 	char		*mapped();
465 	extern char	*rindex();
466 	struct winsize	win;
467 	extern char	*getenv();
468 	extern char	*tgetstr();
469 	char		bs_char;
470 	int		csh;
471 	int		settle = NO;
472 	void		setmode();
473 	extern char	PC;
474 	extern short	ospeed;
475 
476 	if ((istermios = ioctl(FILEDES, TCGETS, (char *)&modes)) < 0) {
477 		if (ioctl(FILEDES, TCGETA, (char *)&mode) < 0) {
478 			prs("Not a terminal\n");
479 			exit(1);
480 		}
481 		bmove((char *)&mode, (char *)&oldmode, sizeof (mode));
482 		modes.c_lflag = oldmodes.c_lflag = mode.c_lflag;
483 		modes.c_oflag = oldmodes.c_oflag = mode.c_oflag;
484 		modes.c_iflag = oldmodes.c_iflag = mode.c_iflag;
485 		modes.c_cflag = oldmodes.c_cflag = mode.c_cflag;
486 		for (i = 0; i < NCC; i++)
487 			modes.c_cc[i] = oldmodes.c_cc[i] = mode.c_cc[i];
488 	} else
489 		bmove((char *)&modes, (char *)&oldmodes, sizeof (modes));
490 	ospeed = cfgetospeed(&modes);
491 	(void) signal(SIGINT, setmode);
492 	(void) signal(SIGQUIT, setmode);
493 	(void) signal(SIGTERM, setmode);
494 
495 	if (command = rindex(argv[0], '/'))
496 		command++;
497 	else
498 		command = argv[0];
499 	if (sequal(command, "reset")) {
500 		/*
501 		 * Reset the teletype mode bits to a sensible state.
502 		 * Copied from the program by Kurt Shoens & Mark Horton.
503 		 * Very useful after crapping out in raw.
504 		 */
505 		if ((istermios = ioctl(FILEDES, TCGETS, (char *)&modes)) < 0) {
506 			(void) ioctl(FILEDES, TCGETA, (char *)&mode);
507 			modes.c_lflag = mode.c_lflag;
508 			modes.c_oflag = mode.c_oflag;
509 			modes.c_iflag = mode.c_iflag;
510 			modes.c_cflag = mode.c_cflag;
511 			for (i = 0; i < NCC; i++)
512 				modes.c_cc[i] = mode.c_cc[i];
513 		}
514 		curerase = reset(curerase, CERASE);
515 		curkill = reset(curkill, CKILL);
516 		curintr = reset(curintr, CINTR);
517 		modes.c_cc[VQUIT] = reset(modes.c_cc[VQUIT], CQUIT);
518 		modes.c_cc[VEOF] = reset(modes.c_cc[VEOF], CEOF);
519 
520 		modes.c_iflag |= (BRKINT|ISTRIP|ICRNL|IXON);
521 		modes.c_iflag &= ~(IGNBRK|PARMRK|INPCK|INLCR|IGNCR|IUCLC|IXOFF);
522 		modes.c_oflag |= (OPOST|ONLCR);
523 		modes.c_oflag &= ~(OLCUC|OCRNL|ONOCR|ONLRET|OFILL|OFDEL|
524 		    NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
525 		modes.c_cflag |= (CS7|CREAD);
526 		modes.c_cflag &= ~(PARODD|CLOCAL);
527 		modes.c_lflag |= (ISIG|ICANON|ECHO|ECHOK);
528 		modes.c_lflag &= ~(XCASE|ECHONL|NOFLSH);
529 		if (istermios < 0) {
530 			mode.c_lflag = modes.c_lflag;
531 			mode.c_oflag = modes.c_oflag;
532 			mode.c_iflag = modes.c_iflag;
533 			mode.c_cflag = modes.c_cflag;
534 			for (i = 0; i < NCC; i++)
535 				mode.c_cc[i] = modes.c_cc[i];
536 			(void) ioctl(FILEDES, TCSETAW, (char *)&mode);
537 		} else
538 			(void) ioctl(FILEDES, TCSETSW, (char *)&modes);
539 		Dash_u = YES;
540 		BeQuiet = YES;
541 		IsReset = YES;
542 	} else if (argc == 2 && sequal(argv[1], "-")) {
543 		RepOnly = YES;
544 		Dash_u = YES;
545 	}
546 	argc--;
547 
548 	/* scan argument list and collect flags */
549 	while (--argc >= 0) {
550 		p = *++argv;
551 		if (*p == '-') {
552 			if (*++p == '\0')
553 				Report = YES; /* report current terminal type */
554 			else
555 				while (*p)
556 					switch (*p++) {
557 
558 			case 'r':	/* report to user */
559 				Ureport = YES;
560 				continue;
561 
562 			case 'E':
563 				/* special erase: operate on all but TTY33 */
564 				Specialerase = YES;
565 				/* explicit fall-through to -e case */
566 				/* FALLTHROUGH */
567 
568 			case 'e':	/* erase character */
569 				if (*p == '\0')
570 					Erase_char = -1;
571 				else {
572 					if (*p == '^' && p[1] != '\0')
573 						if (*++p == '?')
574 							Erase_char = '\177';
575 						else
576 							Erase_char = CNTL(*p);
577 					else
578 						Erase_char = *p;
579 					p++;
580 				}
581 				continue;
582 
583 			case 'i':	/* interrupt character */
584 				if (*p == '\0')
585 					Intr_char = CNTL('C');
586 				else {
587 					if (*p == '^' && p[1] != '\0')
588 						if (*++p == '?')
589 							Intr_char = '\177';
590 						else
591 							Intr_char = CNTL(*p);
592 					else
593 						Intr_char = *p;
594 					p++;
595 				}
596 				continue;
597 
598 			case 'k':	/* kill character */
599 				if (*p == '\0')
600 					Kill_char = CNTL('U');
601 				else {
602 					if (*p == '^' && p[1] != '\0')
603 						if (*++p == '?')
604 							Kill_char = '\177';
605 						else
606 							Kill_char = CNTL(*p);
607 					else
608 						Kill_char = *p;
609 					p++;
610 				}
611 				continue;
612 
613 #ifdef OLDFLAGS
614 #ifdef	OLDDIALUP
615 			case 'd':	/* dialup type */
616 				NewType = DIALUP;
617 				goto mapold;
618 #endif
619 
620 #ifdef OLDPLUGBOARD
621 			case 'p':	/* plugboard type */
622 				NewType = PLUGBOARD;
623 				goto mapold;
624 #endif
625 
626 #ifdef OLDARPANET
627 			case 'a':	/* arpanet type */
628 				Newtype = ARPANET;
629 				goto mapold;
630 #endif
631 
632 mapold:				Map->Ident = NewType;
633 				Map->Test = ALL;
634 				if (*p == '\0') {
635 					p = nextarg(argc--, argv++);
636 				}
637 				Map->Type = p;
638 				Map++;
639 				Mapped = YES;
640 				p = "";
641 				continue;
642 #endif
643 
644 			case 'm':	/* map identifier to type */
645 				/*
646 				 * This code is very loose. Almost no
647 				 * syntax checking is done!! However,
648 				 * illegal syntax will only produce
649 				 * weird results.
650 				 */
651 				if (*p == '\0') {
652 					p = nextarg(argc--, argv++);
653 				}
654 				if (isalnum(*p)) {
655 					Map->Ident = p;	/* identifier */
656 					while (isalnum(*p)) p++;
657 				}
658 				else
659 					Map->Ident = "";
660 				Break = NO;
661 				Not = NO;
662 				while (!Break)
663 					switch (*p) {
664 					case '\0':
665 						p = nextarg(argc--, argv++);
666 						continue;
667 
668 					case ':':	/* mapped type */
669 						*p++ = '\0';
670 						Break = YES;
671 						continue;
672 
673 					case '>':	/* conditional */
674 						Map->Test |= GT;
675 						*p++ = '\0';
676 						continue;
677 
678 					case '<':	/* conditional */
679 						Map->Test |= LT;
680 						*p++ = '\0';
681 						continue;
682 
683 					case '=':	/* conditional */
684 					case '@':
685 						Map->Test |= EQ;
686 						*p++ = '\0';
687 						continue;
688 
689 					case '!':	/* invert conditions */
690 						Not = ~Not;
691 						*p++ = '\0';
692 						continue;
693 
694 					case 'B':	/* Baud rate */
695 						p++;
696 						/* intentional fallthru */
697 					default:
698 						if (isdigit(*p) || *p == 'e') {
699 							Map->Speed =
700 							    baudrate(p);
701 							while (isalnum(*p) ||
702 							    *p == '.')
703 								p++;
704 						} else
705 							Break = YES;
706 						continue;
707 				}
708 				if (Not) {	/* invert sense of test */
709 					Map->Test = (~(Map->Test))&ALL;
710 				}
711 				if (*p == '\0') {
712 					p = nextarg(argc--, argv++);
713 				}
714 				Map->Type = p;
715 				p = "";
716 				Map++;
717 				Mapped = YES;
718 				continue;
719 
720 			case 'h':	/* don't get type from htmp or env */
721 				Dash_h = YES;
722 				continue;
723 
724 			case 'u':	/* don't update htmp */
725 				Dash_u = YES;
726 				continue;
727 
728 			case 's':	/* output setenv commands */
729 				DoSetenv = YES;
730 				CmndLine = YES;
731 				continue;
732 
733 			case 'S':	/* output setenv strings */
734 				DoSetenv = YES;
735 				CmndLine = NO;
736 				continue;
737 
738 			case 'Q':	/* be quiet */
739 				BeQuiet = YES;
740 				continue;
741 
742 			case 'I':	/* no initialization */
743 				NoInit = YES;
744 				continue;
745 
746 			case 'A':	/* Ask user */
747 				Ask = YES;
748 				continue;
749 
750 			case 'v':	/* no virtual terminal */
751 				DoVirtTerm = NO;
752 				continue;
753 
754 			default:
755 				*p-- = '\0';
756 				fatal("Bad flag -", p);
757 			}
758 		} else {
759 			/* terminal type */
760 			DefType = p;
761 		}
762 	}
763 
764 	if (DefType) {
765 		if (Mapped) {
766 			Map->Ident = "";	/* means "map any type" */
767 			Map->Test = ALL;	/* at all baud rates */
768 			Map->Type = DefType;	/* to the default type */
769 		} else
770 			TtyType = DefType;
771 	}
772 
773 	/*
774 	 * Get rid of $TERMCAP, if it's there, so we get a real
775 	 * entry from /etc/termcap.  This prevents us from being
776 	 * fooled by out of date stuff in the environment, and
777 	 * makes tabs work right on CB/Unix.
778 	 */
779 	bufp = getenv("TERMCAP");
780 	if (bufp && *bufp != '/')
781 		(void) strcpy(bufp-8, "NOTHING"); /* overwrite only "TERMCAP" */
782 	/* get current idea of terminal type from environment */
783 	if (!Dash_h && TtyType == NULL)
784 		TtyType = getenv("TERM");
785 
786 	/* If still undefined, use DEFTYPE */
787 	if (TtyType == NULL) {
788 		TtyType = DEFTYPE;
789 	}
790 
791 	/* check for dialup or other mapping */
792 	if (Mapped) {
793 		if (!(Alias[0] && isalias(TtyType)))
794 			if (tgetent(Capbuf, TtyType) > 0)
795 				makealias(Capbuf);
796 		TtyType = mapped(TtyType);
797 	}
798 
799 	/* TtyType now contains a pointer to the type of the terminal */
800 	/* If the first character is '?', ask the user */
801 	if (TtyType[0] == '?') {
802 		Ask = YES;
803 		TtyType++;
804 		if (TtyType[0] == '\0')
805 			TtyType = DEFTYPE;
806 	}
807 	if (Ask) {
808 ask:
809 		prs("TERM = (");
810 		prs(TtyType);
811 		prs(") ");
812 		flush();
813 
814 		/* read the terminal.  If not empty, set type */
815 		i = read(2, termbuf, sizeof (termbuf) - 1);
816 		if (i > 0) {
817 			if (termbuf[i - 1] == '\n')
818 				i--;
819 			termbuf[i] = '\0';
820 			if (termbuf[0] != '\0')
821 				TtyType = termbuf;
822 		}
823 	}
824 
825 	/* get terminal capabilities */
826 	if (!(Alias[0] && isalias(TtyType))) {
827 		switch (tgetent(Capbuf, TtyType)) {
828 		case -1:
829 			prs("Cannot find termcap\n");
830 			flush();
831 			exit(-1);
832 
833 		case 0:
834 			prs("Type ");
835 			prs(TtyType);
836 			prs(" unknown\n");
837 			flush();
838 			if (DoSetenv) {
839 				TtyType = DEFTYPE;
840 				Alias[0] = '\0';
841 				goto ask;
842 			} else
843 				exit(1);
844 		}
845 	}
846 	Ttycap = Capbuf;
847 
848 	if (!RepOnly) {
849 		/* determine erase and kill characters */
850 		if (Specialerase && !tgetflag("bs"))
851 			Erase_char = 0;
852 		bufp = buf;
853 		p = tgetstr("kb", &bufp);
854 		if (p == NULL || p[1] != '\0')
855 			p = tgetstr("bc", &bufp);
856 		if (p != NULL && p[1] == '\0')
857 			bs_char = p[0];
858 		else if (tgetflag("bs"))
859 			bs_char = BACKSPACE;
860 		else
861 			bs_char = 0;
862 		/*
863 		 * The next statement can't be fixed, because now users
864 		 * depend on keeping their erase character as DEL if the
865 		 * system set it there.  People who want backspace have
866 		 * to say tset -e.
867 		 */
868 		if (Erase_char == 0 && !tgetflag("os") &&
869 		    curerase == OLDERASE) {
870 			if (tgetflag("bs") || bs_char != 0)
871 				Erase_char = -1;
872 		}
873 		if (Erase_char < 0)
874 			Erase_char = (bs_char != 0) ? bs_char : BACKSPACE;
875 
876 		if (curerase == 0)
877 			curerase = CERASE;
878 		if (Erase_char != 0)
879 			curerase = Erase_char;
880 
881 		if (curintr == 0)
882 			curintr = CINTR;
883 		if (Intr_char != 0)
884 			curintr = Intr_char;
885 
886 		if (curkill == 0)
887 			curkill = CKILL;
888 		if (Kill_char != 0)
889 			curkill = Kill_char;
890 
891 		/* set modes */
892 		PadBaud = tgetnum("pb");	/* OK if fails */
893 		for (i = 0; speeds[i].string; i++)
894 			if (speeds[i].baudrate == PadBaud) {
895 				PadBaud = speeds[i].speed;
896 				break;
897 			}
898 		setdelay("dC", CRdelay, CRbits, &modes.c_oflag);
899 		setdelay("dN", NLdelay, NLbits, &modes.c_oflag);
900 		setdelay("dB", BSdelay, BSbits, &modes.c_oflag);
901 		setdelay("dF", FFdelay, FFbits, &modes.c_oflag);
902 		setdelay("dT", TBdelay, TBbits, &modes.c_oflag);
903 		setdelay("dV", VTdelay, VTbits, &modes.c_oflag);
904 
905 		if (tgetflag("UC") || (command[0] & 0140) == 0100) {
906 			modes.c_iflag |= IUCLC;
907 			modes.c_oflag |= OLCUC;
908 			modes.c_cflag |= XCASE;
909 		} else if (tgetflag("LC")) {
910 			modes.c_iflag &= ~IUCLC;
911 			modes.c_oflag &= ~OLCUC;
912 			modes.c_cflag &= ~XCASE;
913 		}
914 		modes.c_iflag &= ~(PARMRK|INPCK);
915 		modes.c_lflag |= ICANON;
916 		if (tgetflag("EP")) {
917 			modes.c_iflag |= INPCK;
918 			modes.c_cflag |= PARENB;
919 			modes.c_cflag &= ~PARODD;
920 		}
921 		if (tgetflag("OP")) {
922 			modes.c_iflag |= INPCK;
923 			modes.c_cflag |= PARENB;
924 			modes.c_cflag |= PARODD;
925 		}
926 
927 		modes.c_oflag |= ONLCR;
928 		modes.c_iflag |= ICRNL;
929 		modes.c_lflag |= ECHO;
930 		modes.c_oflag |= TAB3;
931 		if (tgetflag("NL")) {	/* new line, not line feed */
932 			modes.c_oflag &= ~ONLCR;
933 			modes.c_iflag &= ~ICRNL;
934 		}
935 		if (tgetflag("HD"))	/* half duplex */
936 			modes.c_lflag &= ~ECHO;
937 		if (tgetflag("pt"))	/* print tabs */
938 			modes.c_oflag &= ~TAB3;
939 
940 		modes.c_lflag |= (ECHOE|ECHOK);
941 		if (tgetflag("hc")) {	/* set printer modes */
942 			modes.c_lflag &= ~ECHOE;
943 		}
944 
945 		/* get pad character */
946 		bufp = buf;
947 		if (tgetstr("pc", &bufp) != 0)
948 			PC = buf[0];
949 
950 		/* output startup string */
951 		if (!NoInit) {
952 			if (oldmodes.c_oflag&(TAB3|ONLCR|OCRNL|ONLRET)) {
953 				oldmodes.c_oflag &= (TAB3|ONLCR|OCRNL|ONLRET);
954 				setmode(-1);
955 			}
956 			if (settabs()) {
957 				settle = YES;
958 				flush();
959 			}
960 			bufp = buf;
961 			if (IsReset && tgetstr("rs", &bufp) != 0 ||
962 			    tgetstr("is", &bufp) != 0) {
963 				tputs(buf, 0, prc);
964 				settle = YES;
965 				flush();
966 			}
967 			bufp = buf;
968 			if (IsReset && tgetstr("rf", &bufp) != 0 ||
969 			    tgetstr("if", &bufp) != 0) {
970 				cat(buf);
971 				settle = YES;
972 			}
973 			if (settle) {
974 				prc('\r');
975 				if (IsReset)
976 					prc('\n');  /* newline too */
977 				flush();
978 				sleep(1);	/* let terminal settle down */
979 			}
980 		}
981 
982 		setmode(0);	/* set new modes, if they've changed */
983 
984 		/* set up environment for the shell we are using */
985 		/* (this code is rather heuristic, checking for $SHELL */
986 		/* ending in the 3 characters "csh") */
987 		csh = NO;
988 		if (DoSetenv) {
989 			char *sh;
990 
991 			if ((sh = getenv("SHELL")) && (i = strlen(sh)) >= 3) {
992 				if ((csh = sequal(&sh[i-3], "csh")) && CmndLine)
993 					(void) write(STDOUT,
994 					    "set noglob;\n", 12);
995 			}
996 			if (!csh) {	/* running Bourne shell */
997 				(void) write(STDOUT,
998 				    "export TERMCAP TERM;\n", 21);
999 			}
1000 		}
1001 	}
1002 
1003 	/* report type if appropriate */
1004 	if (DoSetenv || Report || Ureport) {
1005 		/* if type is the short name, find first alias (if any) */
1006 		makealias(Ttycap);
1007 		if (sequal(TtyType, Alias[0]) && Alias[1]) {
1008 			TtyType = Alias[1];
1009 		}
1010 
1011 		if (DoSetenv) {
1012 			if (csh) {
1013 				if (CmndLine)
1014 					(void) write(STDOUT,
1015 					    "setenv TERM ", 12);
1016 				(void) write(STDOUT, TtyType, strlen(TtyType));
1017 				(void) write(STDOUT, " ", 1);
1018 				if (CmndLine)
1019 					(void) write(STDOUT, ";\n", 2);
1020 			} else {
1021 				(void) write(STDOUT, "TERM=", 5);
1022 				(void) write(STDOUT, TtyType, strlen(TtyType));
1023 				(void) write(STDOUT, ";\n", 2);
1024 			}
1025 		} else if (Report) {
1026 			(void) write(STDOUT, TtyType, strlen(TtyType));
1027 			(void) write(STDOUT, "\n", 1);
1028 		}
1029 		if (Ureport) {
1030 			prs("Terminal type is ");
1031 			prs(TtyType);
1032 			prs("\n");
1033 			flush();
1034 		}
1035 
1036 		if (DoSetenv) {
1037 			if (csh) {
1038 				if (CmndLine)
1039 					(void) write(STDOUT,
1040 					    "setenv TERMCAP '", 16);
1041 			} else
1042 				(void) write(STDOUT, "TERMCAP='", 9);
1043 			wrtermcap(Ttycap);
1044 			if (csh) {
1045 				if (CmndLine) {
1046 					(void) write(STDOUT, "';\n", 3);
1047 					(void) write(STDOUT,
1048 					    "unset noglob;\n", 14);
1049 				}
1050 			} else
1051 				(void) write(STDOUT, "';\n", 3);
1052 		}
1053 	}
1054 
1055 	if (RepOnly)
1056 		exit(0);
1057 
1058 	/* tell about changing erase, kill and interrupt characters */
1059 	reportek("Erase", curerase, olderase, CERASE);
1060 	reportek("Kill", curkill, oldkill, CKILL);
1061 	reportek("Interrupt", curintr, oldintr, CINTR);
1062 
1063 	return (0);
1064 }
1065 
1066 /*
1067  * Set the hardware tabs on the terminal, using the ct (clear all tabs),
1068  * st (set one tab) and ch (horizontal cursor addressing) capabilities.
1069  * This is done before if and is, so they can patch in case we blow this.
1070  */
1071 int
1072 settabs(void)
1073 {
1074 	char caps[100];
1075 	char *capsp = caps;
1076 	char *clear_tabs, *set_tab, *set_column, *set_pos;
1077 	char *tg_out, *tgoto();
1078 	int c;
1079 	extern char *tgetstr();
1080 	int lines, columns;
1081 
1082 	clear_tabs = tgetstr("ct", &capsp);
1083 	set_tab = tgetstr("st", &capsp);
1084 	set_column = tgetstr("ch", &capsp);
1085 	if (set_column == 0)
1086 		set_pos = tgetstr("cm", &capsp);
1087 
1088 	if (clear_tabs && set_tab) {
1089 		prc('\r');	/* force to be at left margin */
1090 		tputs(clear_tabs, 0, prc);
1091 	}
1092 	if (set_tab) {
1093 		columns = tgetnum("co");
1094 		lines = tgetnum("li");
1095 		for (c = 0; c < columns; c += 8) {
1096 			/* get to that column. */
1097 			tg_out = "OOPS";	/* also returned by tgoto */
1098 			if (set_column)
1099 				tg_out = tgoto(set_column, 0, c);
1100 			if (*tg_out == 'O' && set_pos)
1101 				tg_out = tgoto(set_pos, c, lines-1);
1102 			if (*tg_out != 'O')
1103 				tputs(tg_out, 1, prc);
1104 			else if (c != 0) {
1105 				prc(' '); prc(' '); prc(' '); prc(' ');
1106 				prc(' '); prc(' '); prc(' '); prc(' ');
1107 			}
1108 			/* set the tab */
1109 			tputs(set_tab, 0, prc);
1110 		}
1111 		prc('\r');
1112 		return (1);
1113 	}
1114 	return (0);
1115 }
1116 
1117 /*
1118  * flag serves several purposes:
1119  *	if called as the result of a signal, flag will be > 0.
1120  *	if called from terminal init, flag == -1 means reset "oldmode".
1121  *	called with flag == 0 at end of normal mode processing.
1122  */
1123 void
1124 setmode(int flag)
1125 {
1126 	struct termio *ttymode;
1127 	struct termios *ttymodes;
1128 	int i;
1129 
1130 	ttymode = (struct termio *)0;
1131 	ttymodes = (struct termios *)0;
1132 
1133 	if (flag < 0) { /* unconditionally reset oldmode (called from init) */
1134 		if (istermios < 0) {
1135 			oldmode.c_lflag = oldmodes.c_lflag;
1136 			oldmode.c_oflag = oldmodes.c_oflag;
1137 			oldmode.c_iflag = oldmodes.c_iflag;
1138 			oldmode.c_cflag = oldmodes.c_cflag;
1139 			for (i = 0; i < NCC; i++)
1140 				oldmode.c_cc[i] = oldmodes.c_cc[i];
1141 			ttymode = &oldmode;
1142 		} else
1143 			ttymodes = &oldmodes;
1144 	} else {
1145 		if (istermios < 0) {
1146 			oldmode.c_lflag = oldmodes.c_lflag;
1147 			oldmode.c_oflag = oldmodes.c_oflag;
1148 			oldmode.c_iflag = oldmodes.c_iflag;
1149 			oldmode.c_cflag = oldmodes.c_cflag;
1150 			for (i = 0; i < NCC; i++)
1151 				oldmode.c_cc[i] = oldmodes.c_cc[i];
1152 			mode.c_lflag = modes.c_lflag;
1153 			mode.c_oflag = modes.c_oflag;
1154 			mode.c_iflag = modes.c_iflag;
1155 			mode.c_cflag = modes.c_cflag;
1156 			for (i = 0; i < NCC; i++)
1157 				mode.c_cc[i] = modes.c_cc[i];
1158 			if (!bequal((char *)&mode, (char *)&oldmode,
1159 			    sizeof (mode)))
1160 				ttymode = &mode;
1161 		} else if (!bequal((char *)&modes, (char *)&oldmodes,
1162 		    sizeof (modes)))
1163 			ttymodes = &modes;
1164 	}
1165 
1166 	if (ttymode) {
1167 		(void) ioctl(FILEDES, TCSETAW, (char *)ttymode);
1168 	} else if (ttymodes) {
1169 		(void) ioctl(FILEDES, TCSETSW, (char *)ttymodes);
1170 	}
1171 	if (flag > 0)	/* trapped signal */
1172 		exit(1);
1173 }
1174 
1175 void
1176 reportek(char *name, char new, char old, char def)
1177 {
1178 	char	o;
1179 	char	n;
1180 	char	*p;
1181 	char		buf[32];
1182 	char		*bufp;
1183 	extern char *tgetstr();
1184 
1185 	if (BeQuiet)
1186 		return;
1187 	o = old;
1188 	n = new;
1189 
1190 	if (o == n && n == def)
1191 		return;
1192 	prs(name);
1193 	if (o == n)
1194 		prs(" is ");
1195 	else
1196 		prs(" set to ");
1197 	bufp = buf;
1198 	if (tgetstr("kb", &bufp) > (char *)0 && n == buf[0] && buf[1] == '\0')
1199 		prs("Backspace\n");
1200 	else if (n == 0177)
1201 		prs("Delete\n");
1202 	else {
1203 		if (n < 040) {
1204 			prs("Ctrl-");
1205 			n ^= 0100;
1206 		}
1207 		p = "x\n";
1208 		p[0] = n;
1209 		prs(p);
1210 	}
1211 	flush();
1212 }
1213 
1214 
1215 
1216 void
1217 setdelay(char *cap, struct delay dtab[], tcflag_t bits, tcflag_t *flags)
1218 {
1219 	int		i;
1220 	struct delay	*p;
1221 	extern short	ospeed;
1222 
1223 	/* see if this capability exists at all */
1224 	i = tgetnum(cap);
1225 	if (i < 0)
1226 		i = 0;
1227 	/* No padding at speeds below PadBaud */
1228 	if (PadBaud > ospeed)
1229 		i = 0;
1230 
1231 	/* clear out the bits, replace with new ones */
1232 	*flags &= ~bits;
1233 
1234 	/* scan dtab for first entry with adequate delay */
1235 	for (p = dtab; p->d_delay >= 0; p++) {
1236 		if (p->d_delay >= i) {
1237 			p++;
1238 			break;
1239 		}
1240 	}
1241 
1242 	/* use last entry if none will do */
1243 	*flags |= (tcflag_t)((--p)->d_bits);
1244 }
1245 
1246 void
1247 prs(char *s)
1248 {
1249 	while (*s != '\0')
1250 		prc(*s++);
1251 }
1252 
1253 
1254 char	OutBuf[256];
1255 int	OutPtr;
1256 
1257 void
1258 prc(char c)
1259 {
1260 	OutBuf[OutPtr++] = c;
1261 	if (OutPtr >= sizeof (OutBuf))
1262 		flush();
1263 }
1264 
1265 void
1266 flush(void)
1267 {
1268 	if (OutPtr > 0)
1269 		(void) write(2, OutBuf, OutPtr);
1270 	OutPtr = 0;
1271 }
1272 
1273 void
1274 cat(char *file)
1275 {
1276 	int	fd;
1277 	int	i;
1278 	char		buf[BUFSIZ];
1279 
1280 	fd = open(file, 0);
1281 	if (fd < 0) {
1282 		prs("Cannot open ");
1283 		prs(file);
1284 		prs("\n");
1285 		flush();
1286 		return;
1287 	}
1288 
1289 	while ((i = read(fd, buf, BUFSIZ)) > 0)
1290 		(void) write(FILEDES, buf, i);
1291 
1292 	(void) close(fd);
1293 }
1294 
1295 
1296 void
1297 bmove(char *from, char *to, int length)
1298 {
1299 	char	*p, *q;
1300 	int	i;
1301 
1302 	i = length;
1303 	p = from;
1304 	q = to;
1305 
1306 	while (i-- > 0)
1307 		*q++ = *p++;
1308 }
1309 
1310 
1311 int
1312 bequal(char *a, char *b, int len)	/* must be same thru len chars */
1313 {
1314 	char	*p, *q;
1315 	int	i;
1316 
1317 	i = len;
1318 	p = a;
1319 	q = b;
1320 
1321 	while ((*p == *q) && --i > 0) {
1322 		p++; q++;
1323 	}
1324 	return ((*p == *q) && i >= 0);
1325 }
1326 
1327 int
1328 sequal(char *a, char *b)	/* must be same thru NULL */
1329 {
1330 	char *p = a, *q = b;
1331 
1332 	while (*p && *q && (*p == *q)) {
1333 		p++; q++;
1334 	}
1335 	return (*p == *q);
1336 }
1337 
1338 void
1339 makealias(char *buf)
1340 {
1341 	int i;
1342 	char *a;
1343 	char *b;
1344 
1345 	Alias[0] = a = Aliasbuf;
1346 	b = buf;
1347 	i = 1;
1348 	while (*b && *b != ':') {
1349 		if (*b == '|') {
1350 			*a++ = '\0';
1351 			Alias[i++] = a;
1352 			b++;
1353 		} else
1354 			*a++ = *b++;
1355 	}
1356 	*a = '\0';
1357 	Alias[i] = NULL;
1358 #ifdef	DEB
1359 	for (i = 0; Alias[i]; printf("A:%s\n", Alias[i++]))
1360 		;
1361 #endif
1362 }
1363 
1364 int
1365 isalias(char *ident)	/* is ident same as one of the aliases? */
1366 {
1367 	char **a = Alias;
1368 
1369 	if (*a)
1370 		while (*a)
1371 			if (sequal(ident, *a))
1372 				return (YES);
1373 			else
1374 				a++;
1375 	return (NO);
1376 }
1377 
1378 
1379 /*
1380  * routine to output the string for the environment TERMCAP variable
1381  */
1382 #define	WHITE(c)	(c == ' ' || c == '\t')
1383 char delcap[128][2];
1384 int ncap = 0;
1385 
1386 void
1387 wrtermcap(char *bp)
1388 {
1389 	char buf[CAPBUFSIZ];
1390 	char *p = buf;
1391 	char *tp;
1392 	char *putbuf();
1393 	int space, empty;
1394 
1395 	/* discard names with blanks */
1396 /* May not be desireable ? */
1397 	while (*bp && *bp != ':') {
1398 		if (*bp == '|') {
1399 			tp = bp+1;
1400 			space = NO;
1401 			while (*tp && *tp != '|' && *tp != ':') {
1402 				space = (space || WHITE(*tp));
1403 				tp++;
1404 			}
1405 			if (space) {
1406 				bp = tp;
1407 				continue;
1408 			}
1409 		}
1410 		*p++ = *bp++;
1411 	}
1412 
1413 	while (*bp) {
1414 		switch (*bp) {
1415 		case ':':	/* discard empty, cancelled  or dupl fields */
1416 			tp = bp + 1;
1417 			empty = YES;
1418 			while (*tp && *tp != ':') {
1419 				empty = (empty && WHITE(*tp));
1420 				tp++;
1421 			}
1422 			if (empty || cancelled(bp+1)) {
1423 				bp = tp;
1424 				continue;
1425 			}
1426 			break;
1427 
1428 		case ' ':	/* no spaces in output */
1429 			p = putbuf(p, "\\040");
1430 			bp++;
1431 			continue;
1432 
1433 		case '!':	/* the shell thinks this is history */
1434 			p = putbuf(p, "\\041");
1435 			bp++;
1436 			continue;
1437 
1438 		case ',':	/* the shell thinks this is history */
1439 			p = putbuf(p, "\\054");
1440 			bp++;
1441 			continue;
1442 
1443 		case '"':	/* no quotes in output */
1444 			p = putbuf(p, "\\042");
1445 			bp++;
1446 			continue;
1447 
1448 		case '\'':	/* no quotes in output */
1449 			p = putbuf(p, "\\047");
1450 			bp++;
1451 			continue;
1452 
1453 		case '`':	/* no back quotes in output */
1454 			p = putbuf(p, "\\140");
1455 			bp++;
1456 			continue;
1457 
1458 		case '\\':
1459 		case '^':	/* anything following is OK */
1460 			*p++ = *bp++;
1461 		}
1462 		*p++ = *bp++;
1463 	}
1464 	*p++ = ':';	/* we skipped the last : with the : lookahead hack */
1465 	(void) write(STDOUT, buf, p-buf);
1466 }
1467 
1468 int
1469 cancelled(char *cap)
1470 {
1471 	int i;
1472 
1473 	for (i = 0; i < ncap; i++) {
1474 		if (cap[0] == delcap[i][0] && cap[1] == delcap[i][1])
1475 			return (YES);
1476 	}
1477 	/* delete a second occurrance of the same capability */
1478 	delcap[ncap][0] = cap[0];
1479 	delcap[ncap][1] = cap[1];
1480 	ncap++;
1481 	return (cap[2] == '@');
1482 }
1483 
1484 char *
1485 putbuf(ptr, str)
1486 char	*ptr;
1487 char	*str;
1488 {
1489 	char buf[20];
1490 
1491 	while (*str) {
1492 		switch (*str) {
1493 		case '\033':
1494 			ptr = putbuf(ptr, "\\E");
1495 			str++;
1496 			break;
1497 		default:
1498 			if (*str <= ' ') {
1499 				(void) sprintf(buf, "\\%03o", *str);
1500 				ptr = putbuf(ptr, buf);
1501 				str++;
1502 			} else
1503 				*ptr++ = *str++;
1504 		}
1505 	}
1506 	return (ptr);
1507 }
1508 
1509 int
1510 baudrate(char *p)
1511 {
1512 	char buf[8];
1513 	int i = 0;
1514 
1515 	while (i < 7 && (isalnum(*p) || *p == '.'))
1516 		buf[i++] = *p++;
1517 	buf[i] = '\0';
1518 	for (i = 0; speeds[i].string; i++)
1519 		if (sequal(speeds[i].string, buf))
1520 			return (speeds[i].speed);
1521 	return (-1);
1522 }
1523 
1524 char *
1525 mapped(type)
1526 char	*type;
1527 {
1528 	extern short	ospeed;
1529 	int	match;
1530 
1531 #ifdef DEB
1532 	printf("spd:%d\n", ospeed);
1533 	prmap();
1534 #endif
1535 	Map = map;
1536 	while (Map->Ident) {
1537 		if (*(Map->Ident) == '\0' ||
1538 		    sequal(Map->Ident, type) || isalias(Map->Ident)) {
1539 			match = NO;
1540 			switch (Map->Test) {
1541 			case ANY:	/* no test specified */
1542 			case ALL:
1543 				match = YES;
1544 				break;
1545 
1546 			case GT:
1547 				match = (ospeed > Map->Speed);
1548 				break;
1549 
1550 			case GE:
1551 				match = (ospeed >= Map->Speed);
1552 				break;
1553 
1554 			case EQ:
1555 				match = (ospeed == Map->Speed);
1556 				break;
1557 
1558 			case LE:
1559 				match = (ospeed <= Map->Speed);
1560 				break;
1561 
1562 			case LT:
1563 				match = (ospeed < Map->Speed);
1564 				break;
1565 
1566 			case NE:
1567 				match = (ospeed != Map->Speed);
1568 				break;
1569 			}
1570 			if (match)
1571 				return (Map->Type);
1572 		}
1573 		Map++;
1574 	}
1575 	/* no match found; return given type */
1576 	return (type);
1577 }
1578 
1579 #ifdef DEB
1580 prmap()
1581 {
1582 	Map = map;
1583 	while (Map->Ident) {
1584 		printf("%s t:%d s:%d %s\n",
1585 		    Map->Ident, Map->Test, Map->Speed, Map->Type);
1586 		Map++;
1587 	}
1588 }
1589 #endif
1590 
1591 char *
1592 nextarg(argc, argv)
1593 int	argc;
1594 char	*argv[];
1595 {
1596 	if (argc <= 0)
1597 		fatal("Too few args: ", *argv);
1598 	if (*(*++argv) == '-')
1599 		fatal("Unexpected arg: ", *argv);
1600 	return (*argv);
1601 }
1602 
1603 void
1604 fatal(char *mesg, char *obj)
1605 {
1606 	prs(mesg);
1607 	prs(obj);
1608 	prc('\n');
1609 	prs(USAGE);
1610 	flush();
1611 	exit(1);
1612 }
1613 
1614 
1615 /*
1616  * Stolen from /usr/src/ucb/reset.c, which this mod obsoletes.
1617  */
1618 char
1619 reset(ch, def)
1620 	char ch;
1621 	int def;
1622 {
1623 	if (ch == 0 || (ch&0377) == 0377)
1624 		return (def);
1625 	return (ch);
1626 }
1627