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