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