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