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