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