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