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