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