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