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