xref: /freebsd/contrib/ncurses/progs/tset.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
1 /****************************************************************************
2  * Copyright (c) 1998 Free Software Foundation, Inc.                        *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 
29 /****************************************************************************
30  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
31  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32  ****************************************************************************/
33 
34 
35 /*
36  * tset.c - terminal initialization utility
37  *
38  * This code was mostly swiped from 4.4BSD tset, with some obsolescent
39  * cruft removed and substantial portions rewritten.  A Regents of the
40  * University of California copyright applies to some portions of the
41  * code, and is reproduced below:
42  */
43 /*-
44  * Copyright (c) 1980, 1991, 1993
45  *	The Regents of the University of California.  All rights reserved.
46  *
47  * Redistribution and use in source and binary forms, with or without
48  * modification, are permitted provided that the following conditions
49  * are met:
50  * 1. Redistributions of source code must retain the above copyright
51  *    notice, this list of conditions and the following disclaimer.
52  * 2. Redistributions in binary form must reproduce the above copyright
53  *    notice, this list of conditions and the following disclaimer in the
54  *    documentation and/or other materials provided with the distribution.
55  * 3. All advertising materials mentioning features or use of this software
56  *    must display the following acknowledgement:
57  *	This product includes software developed by the University of
58  *	California, Berkeley and its contributors.
59  * 4. Neither the name of the University nor the names of its contributors
60  *    may be used to endorse or promote products derived from this software
61  *    without specific prior written permission.
62  *
63  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
64  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
65  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
66  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
67  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
68  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
69  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
70  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
71  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
72  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
73  * SUCH DAMAGE.
74  */
75 
76 #define __INTERNAL_CAPS_VISIBLE	/* we need to see has_hardware_tabs */
77 #include <progs.priv.h>
78 
79 #include <errno.h>
80 #include <stdio.h>
81 #include <termcap.h>
82 #include <fcntl.h>
83 
84 #if HAVE_GETTTYNAM && HAVE_TTYENT_H
85 #include <ttyent.h>
86 #endif
87 #ifdef NeXT
88 char *ttyname(int fd);
89 #endif
90 
91 /* this is just to stifle a missing-prototype warning */
92 #ifdef linux
93 # include <sys/ioctl.h>
94 #endif
95 
96 #if NEED_PTEM_H
97 /* they neglected to define struct winsize in termios.h -- it's only
98    in termio.h	*/
99 #include	<sys/stream.h>
100 #include	<sys/ptem.h>
101 #endif
102 
103 #include <curses.h>	/* for bool typedef */
104 #include <dump_entry.h>
105 
106 MODULE_ID("$Id: tset.c,v 0.37 1999/03/14 12:30:02 tom Exp $")
107 
108 extern char **environ;
109 
110 #undef CTRL
111 #define CTRL(x)	((x) & 0x1f)
112 
113 const char *_nc_progname = "tset";
114 
115 static TTY mode, oldmode;
116 
117 static int	terasechar = -1;	/* new erase character */
118 static int	intrchar = -1;		/* new interrupt character */
119 static int	isreset;		/* invoked as reset */
120 static int	tkillchar = -1;		/* new kill character */
121 static int	tlines, tcolumns;		/* window size */
122 
123 #define LOWERCASE(c) ((isalpha(c) && isupper(c)) ? tolower(c) : (c))
124 
125 static int
126 CaselessCmp(const char *a, const char *b) /* strcasecmp isn't portable */
127 {
128 	while (*a && *b) {
129 		int cmp = LOWERCASE(*a) - LOWERCASE(*b);
130 		if (cmp != 0)
131 			break;
132 		a++, b++;
133 	}
134 	return LOWERCASE(*a) - LOWERCASE(*b);
135 }
136 
137 #if !HAVE_STRDUP
138 #define strdup _nc_strdup
139 extern char *_nc_strdup(const char *);
140 #endif /* not HAVE_STRDUP */
141 
142 static void
143 err(const char *fmt, ...)
144 {
145 	va_list ap;
146 	va_start(ap, fmt);
147 	(void)fprintf(stderr, "tset: ");
148 	(void)vfprintf(stderr, fmt, ap);
149 	va_end(ap);
150 	(void)fprintf(stderr, "\n");
151 	exit(EXIT_FAILURE);
152 	/* NOTREACHED */
153 }
154 
155 static void
156 failed(const char *msg)
157 {
158 	char	temp[BUFSIZ];
159 	perror(strcat(strcpy(temp, "tset: "), msg));
160 	exit(EXIT_FAILURE);
161 	/* NOTREACHED */
162 }
163 
164 static void
165 cat(char *file)
166 {
167 	register int fd, nr, nw;
168 	char buf[BUFSIZ];
169 
170 	if ((fd = open(file, O_RDONLY, 0)) < 0)
171 		failed(file);
172 
173 	while ((nr = read(fd, buf, sizeof(buf))) > 0)
174 		if ((nw = write(STDERR_FILENO, buf, (size_t)nr)) == -1)
175 			failed("write to stderr");
176 	if (nr != 0)
177 		failed(file);
178 	(void)close(fd);
179 }
180 
181 static int
182 outc(int c)
183 {
184 	return putc(c, stderr);
185 }
186 
187 /* Prompt the user for a terminal type. */
188 static const char *
189 askuser(const char *dflt)
190 {
191 	static char answer[256];
192 	char *p;
193 
194 	/* We can get recalled; if so, don't continue uselessly. */
195 	if (feof(stdin) || ferror(stdin)) {
196 		(void)fprintf(stderr, "\n");
197 		exit(EXIT_FAILURE);
198 	}
199 	for (;;) {
200 		if (dflt)
201 			(void)fprintf(stderr, "Terminal type? [%s] ", dflt);
202 		else
203 			(void)fprintf(stderr, "Terminal type? ");
204 		(void)fflush(stderr);
205 
206 		if (fgets(answer, sizeof(answer), stdin) == 0) {
207 			if (dflt == 0) {
208 				(void)fprintf(stderr, "\n");
209 				exit(EXIT_FAILURE);
210 			}
211 			return (dflt);
212 		}
213 
214 		if ((p = strchr(answer, '\n')) != 0)
215 			*p = '\0';
216 		if (answer[0])
217 			return (answer);
218 		if (dflt != 0)
219 			return (dflt);
220 	}
221 }
222 
223 /**************************************************************************
224  *
225  * Mapping logic begins here
226  *
227  **************************************************************************/
228 
229 /* Baud rate conditionals for mapping. */
230 #define	GT		0x01
231 #define	EQ		0x02
232 #define	LT		0x04
233 #define	NOT		0x08
234 #define	GE		(GT | EQ)
235 #define	LE		(LT | EQ)
236 
237 typedef struct map {
238 	struct map *next;	/* Linked list of maps. */
239 	const char *porttype;	/* Port type, or "" for any. */
240 	const char *type;	/* Terminal type to select. */
241 	int conditional;	/* Baud rate conditionals bitmask. */
242 	speed_t speed;		/* Baud rate to compare against. */
243 } MAP;
244 
245 static MAP *cur, *maplist;
246 
247 typedef struct speeds {
248 	const char *string;
249 	int	speed;
250 } SPEEDS;
251 
252 static const SPEEDS speeds[] = {
253 	{ "0",		B0 },
254 	{ "50",		B50 },
255 	{ "75",		B75 },
256 	{ "110",	B110 },
257 	{ "134",	B134 },
258 	{ "134.5",	B134 },
259 	{ "150",	B150 },
260 	{ "200",	B200 },
261 	{ "300",	B300 },
262 	{ "600",	B600 },
263 	{ "1200",	B1200 },
264 	{ "1800",	B1800 },
265 	{ "2400",	B2400 },
266 	{ "4800",	B4800 },
267 	{ "9600",	B9600 },
268 	{ "19200",	B19200 },
269 	{ "38400",	B38400 },
270 	{ "19200",	B19200 },
271 	{ "38400",	B38400 },
272 #ifdef B19200
273 	{ "19200",	B19200 },
274 #else
275 #ifdef EXTA
276 	{ "19200",	EXTA },
277 #endif
278 #endif
279 #ifdef B38400
280 	{ "38400",	B38400 },
281 #else
282 #ifdef EXTB
283 	{ "38400",	EXTB },
284 #endif
285 #endif
286 #ifdef B57600
287 	{ "57600",	B57600 },
288 #endif
289 #ifdef B115200
290 	{ "115200",	B115200 },
291 #endif
292 #ifdef B230400
293 	{ "230400",	B230400 },
294 #endif
295 #ifdef B460800
296 	{ "460800",	B460800 },
297 #endif
298 	{ (char *)0,    0 }
299 };
300 
301 static int
302 tbaudrate(char *rate)
303 {
304 	const SPEEDS *sp;
305 	int found = FALSE;
306 
307 	/* The baudrate number can be preceded by a 'B', which is ignored. */
308 	if (*rate == 'B')
309 		++rate;
310 
311 	for (sp = speeds; sp->string; ++sp) {
312 		if (!CaselessCmp(rate, sp->string)) {
313 			found = TRUE;
314 			break;
315 		}
316 	}
317 	if (!found)
318 		err("unknown baud rate %s", rate);
319 	return (sp->speed);
320 }
321 
322 /*
323  * Syntax for -m:
324  * [port-type][test baudrate]:terminal-type
325  * The baud rate tests are: >, <, @, =, !
326  */
327 static void
328 add_mapping(const char *port, char *arg)
329 {
330 	MAP *mapp;
331 	char *copy, *p;
332 	const char *termp;
333 	char *base = 0;
334 
335 	copy = strdup(arg);
336 	mapp = malloc(sizeof(MAP));
337 	if (copy == 0 || mapp == 0)
338 		failed("malloc");
339 	mapp->next = 0;
340 	if (maplist == 0)
341 		cur = maplist = mapp;
342 	else {
343 		cur->next = mapp;
344 		cur =  mapp;
345 	}
346 
347 	mapp->porttype = arg;
348 	mapp->conditional = 0;
349 
350 	arg = strpbrk(arg, "><@=!:");
351 
352 	if (arg == 0) {			/* [?]term */
353 		mapp->type = mapp->porttype;
354 		mapp->porttype = 0;
355 		goto done;
356 	}
357 
358 	if (arg == mapp->porttype)		/* [><@=! baud]:term */
359 		termp = mapp->porttype = 0;
360 	else
361 		termp = base = arg;
362 
363 	for (;; ++arg)				/* Optional conditionals. */
364 		switch(*arg) {
365 		case '<':
366 			if (mapp->conditional & GT)
367 				goto badmopt;
368 			mapp->conditional |= LT;
369 			break;
370 		case '>':
371 			if (mapp->conditional & LT)
372 				goto badmopt;
373 			mapp->conditional |= GT;
374 			break;
375 		case '@':
376 		case '=':			/* Not documented. */
377 			mapp->conditional |= EQ;
378 			break;
379 		case '!':
380 			mapp->conditional |= NOT;
381 			break;
382 		default:
383 			goto next;
384 		}
385 
386 next:	if (*arg == ':') {
387 		if (mapp->conditional)
388 			goto badmopt;
389 		++arg;
390 	} else {				/* Optional baudrate. */
391 		arg = strchr(p = arg, ':');
392 		if (arg == 0)
393 			goto badmopt;
394 		*arg++ = '\0';
395 		mapp->speed = tbaudrate(p);
396 	}
397 
398 	if (arg == (char *)0)		/* Non-optional type. */
399 		goto badmopt;
400 
401 	mapp->type = arg;
402 
403 	/* Terminate porttype, if specified. */
404 	if (termp != 0)
405 		*base = '\0';
406 
407 	/* If a NOT conditional, reverse the test. */
408 	if (mapp->conditional & NOT)
409 		mapp->conditional = ~mapp->conditional & (EQ | GT | LT);
410 
411 	/* If user specified a port with an option flag, set it. */
412 done:	if (port) {
413 		if (mapp->porttype)
414 badmopt:		err("illegal -m option format: %s", copy);
415 		mapp->porttype = port;
416 	}
417 
418 #ifdef MAPDEBUG
419 	(void)printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
420 	(void)printf("type: %s\n", mapp->type);
421 	(void)printf("conditional: ");
422 	p = "";
423 	if (mapp->conditional & GT) {
424 		(void)printf("GT");
425 		p = "/";
426 	}
427 	if (mapp->conditional & EQ) {
428 		(void)printf("%sEQ", p);
429 		p = "/";
430 	}
431 	if (mapp->conditional & LT)
432 		(void)printf("%sLT", p);
433 	(void)printf("\nspeed: %d\n", mapp->speed);
434 #endif
435 }
436 
437 /*
438  * Return the type of terminal to use for a port of type 'type', as specified
439  * by the first applicable mapping in 'map'.  If no mappings apply, return
440  * 'type'.
441  */
442 static const char *
443 mapped(const char *type)
444 {
445 	MAP *mapp;
446 	int match;
447 
448 	for (mapp = maplist; mapp; mapp = mapp->next)
449 		if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) {
450 			switch (mapp->conditional) {
451 			case 0:			/* No test specified. */
452 				match = TRUE;
453 				break;
454 			case EQ:
455 				match = (ospeed == mapp->speed);
456 				break;
457 			case GE:
458 				match = (ospeed >= mapp->speed);
459 				break;
460 			case GT:
461 				match = (ospeed > mapp->speed);
462 				break;
463 			case LE:
464 				match = (ospeed <= mapp->speed);
465 				break;
466 			case LT:
467 				match = (ospeed < mapp->speed);
468 				break;
469 			default:
470 				match = FALSE;
471 			}
472 			if (match)
473 				return (mapp->type);
474 		}
475 	/* No match found; return given type. */
476 	return (type);
477 }
478 
479 /**************************************************************************
480  *
481  * Entry fetching
482  *
483  **************************************************************************/
484 
485 /*
486  * Figure out what kind of terminal we're dealing with, and then read in
487  * its termcap entry.
488  */
489 static const char *
490 get_termcap_entry(char *userarg)
491 {
492 	int rval, errret;
493 	char *p;
494 	const char *ttype;
495 #if HAVE_GETTTYNAM
496 	struct ttyent *t;
497 #else
498 	FILE *fp;
499 #endif
500 	char *ttypath;
501 
502 	if (userarg) {
503 		ttype = userarg;
504 		goto found;
505 	}
506 
507 	/* Try the environment. */
508 	if ((ttype = getenv("TERM")) != 0)
509 		goto map;
510 
511 	if ((ttypath = ttyname(STDERR_FILENO)) != 0) {
512 		if ((p = strrchr(ttypath, '/')) != 0)
513 			++p;
514 		else
515 			p = ttypath;
516 #if HAVE_GETTTYNAM
517 		/*
518 		 * We have the 4.3BSD library call getttynam(3); that means
519 		 * there's an /etc/ttys to look up device-to-type mappings in.
520 		 * Try ttyname(3); check for dialup or other mapping.
521 		 */
522 		if ((t = getttynam(p))) {
523 			ttype = t->ty_type;
524 			goto map;
525 		}
526 #else
527 		if ((fp = fopen("/etc/ttytype", "r")) != 0
528 		 || (fp = fopen("/etc/ttys", "r")) != 0) {
529 			char buffer[BUFSIZ];
530 			char *s, *t, *d;
531 
532 			while (fgets(buffer, sizeof(buffer)-1, fp) != 0) {
533 				for (s = buffer, t = d = 0; *s; s++) {
534 					if (isspace(*s))
535 						*s = '\0';
536 					else if (t == 0)
537 						t = s;
538 					else if (d == 0 && s != buffer && s[-1] == '\0')
539 						d = s;
540 				}
541 				if (t != 0 && d != 0 && !strcmp(d,p)) {
542 					ttype = strdup(t);
543 					fclose(fp);
544 					goto map;
545 				}
546 			}
547 			fclose(fp);
548 		}
549 #endif /* HAVE_GETTTYNAM */
550 	}
551 
552 	/* If still undefined, use "unknown". */
553 	ttype = "unknown";
554 
555 map:	ttype = mapped(ttype);
556 
557 	/*
558 	 * If not a path, remove TERMCAP from the environment so we get a
559 	 * real entry from /etc/termcap.  This prevents us from being fooled
560 	 * by out of date stuff in the environment.
561 	 */
562 found:	if ((p = getenv("TERMCAP")) != 0 && *p != '/') {
563 		/* 'unsetenv("TERMCAP")' is not portable.
564 		 * The 'environ' array is better.
565 		 */
566 		int n;
567 		for (n = 0; environ[n] != 0; n++) {
568 			if (!strncmp("TERMCAP=", environ[n], 8)) {
569 				while ((environ[n] = environ[n+1]) != 0) {
570 					n++;
571 				}
572 				break;
573 			}
574 		}
575 	}
576 
577 	/*
578 	 * ttype now contains a pointer to the type of the terminal.
579 	 * If the first character is '?', ask the user.
580 	 */
581 	if (ttype[0] == '?') {
582 		if (ttype[1] != '\0')
583 			ttype = askuser(ttype + 1);
584 		else
585 			ttype = askuser(0);
586 	}
587 	/* Find the terminfo entry.  If it doesn't exist, ask the user. */
588 	while ((rval = setupterm((NCURSES_CONST char *)ttype, STDOUT_FILENO, &errret)) != OK) {
589 		if (errret == 0) {
590 			(void)fprintf(stderr, "tset: unknown terminal type %s\n",
591 			    ttype);
592 			ttype = 0;
593 		}
594 		else {
595 			(void)fprintf(stderr, "tset: can't initialize terminal type %s (error %d)\n", ttype, errret);
596 			ttype = 0;
597 		}
598 		ttype = askuser(ttype);
599 	}
600 #if BROKEN_LINKER
601 	tgetflag("am");	/* force lib_termcap.o to be linked for 'ospeed' */
602 #endif
603 	return (ttype);
604 }
605 
606 /**************************************************************************
607  *
608  * Mode-setting logic
609  *
610  **************************************************************************/
611 
612 /* some BSD systems have these built in, some systems are missing
613  * one or more definitions. The safest solution is to override.
614  */
615 #undef CEOF
616 #undef CERASE
617 #undef CINTR
618 #undef CKILL
619 #undef CLNEXT
620 #undef CRPRNT
621 #undef CQUIT
622 #undef CSTART
623 #undef CSTOP
624 #undef CSUSP
625 
626 /* control-character defaults */
627 #define CEOF	CTRL('D')
628 #define CERASE	CTRL('H')
629 #define CINTR	127		/* ^? */
630 #define CKILL	CTRL('U')
631 #define CLNEXT  CTRL('v')
632 #define CRPRNT  CTRL('r')
633 #define CQUIT	CTRL('\\')
634 #define CSTART	CTRL('Q')
635 #define CSTOP	CTRL('S')
636 #define CSUSP	CTRL('Z')
637 
638 #define	CHK(val, dft)	((int)val <= 0 ? dft : val)
639 
640 static bool	set_tabs (void);
641 
642 /*
643  * Reset the terminal mode bits to a sensible state.  Very useful after
644  * a child program dies in raw mode.
645  */
646 static void
647 reset_mode(void)
648 {
649 #ifdef TERMIOS
650 	tcgetattr(STDERR_FILENO, &mode);
651 #else
652 	stty(STDERR_FILENO,&mode);
653 #endif
654 
655 #ifdef TERMIOS
656 #if defined(VDISCARD) && defined(CDISCARD)
657 	mode.c_cc[VDISCARD] = CHK(mode.c_cc[VDISCARD], CDISCARD);
658 #endif
659 	mode.c_cc[VEOF] = CHK(mode.c_cc[VEOF], CEOF);
660 	mode.c_cc[VERASE] = CHK(mode.c_cc[VERASE], CERASE);
661 #if defined(VFLUSH) && defined(CFLUSH)
662 	mode.c_cc[VFLUSH] = CHK(mode.c_cc[VFLUSH], CFLUSH);
663 #endif
664 	mode.c_cc[VINTR] = CHK(mode.c_cc[VINTR], CINTR);
665 	mode.c_cc[VKILL] = CHK(mode.c_cc[VKILL], CKILL);
666 #if defined(VLNEXT) && defined(CLNEXT)
667 	mode.c_cc[VLNEXT] = CHK(mode.c_cc[VLNEXT], CLNEXT);
668 #endif
669 	mode.c_cc[VQUIT] = CHK(mode.c_cc[VQUIT], CQUIT);
670 #if defined(VREPRINT) && defined(CRPRNT)
671 	mode.c_cc[VREPRINT] = CHK(mode.c_cc[VREPRINT], CRPRNT);
672 #endif
673 #if defined(VSTART) && defined(CSTART)
674 	mode.c_cc[VSTART] = CHK(mode.c_cc[VSTART], CSTART);
675 #endif
676 #if defined(VSTOP) && defined(CSTOP)
677 	mode.c_cc[VSTOP] = CHK(mode.c_cc[VSTOP], CSTOP);
678 #endif
679 #if defined(VSUSP) && defined(CSUSP)
680 	mode.c_cc[VSUSP] = CHK(mode.c_cc[VSUSP], CSUSP);
681 #endif
682 #if defined(VWERASE) && defined(CWERASE)
683 	mode.c_cc[VWERASE] = CHK(mode.c_cc[VWERASE], CWERASE);
684 #endif
685 
686 	mode.c_iflag &= ~(IGNBRK | PARMRK | INPCK | ISTRIP | INLCR | IGNCR
687 #ifdef IUCLC
688 			  | IUCLC
689 #endif
690 #ifdef IXANY
691 			  | IXANY
692 #endif
693 			  | IXOFF);
694 
695 	mode.c_iflag |= (BRKINT | IGNPAR | ICRNL | IXON
696 #ifdef IMAXBEL
697 			 | IMAXBEL
698 #endif
699 			 );
700 
701 	mode.c_oflag &= ~(0
702 #ifdef OLCUC
703 			  | OLCUC
704 #endif
705 #ifdef OCRNL
706 			  | OCRNL
707 #endif
708 #ifdef ONOCR
709 			  | ONOCR
710 #endif
711 #ifdef ONLRET
712 			  | ONLRET
713 #endif
714 #ifdef OFILL
715 			  | OFILL
716 #endif
717 #ifdef OFDEL
718 			  | OFDEL
719 #endif
720 #ifdef NLDLY
721 			  | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY
722 #endif
723 			  );
724 
725 	mode.c_oflag |= (OPOST
726 #ifdef ONLCR
727 			 | ONLCR
728 #endif
729 			 );
730 
731 	mode.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CLOCAL);
732 	mode.c_cflag |= (CS8 | CREAD);
733 	mode.c_lflag &= ~(ECHONL | NOFLSH
734 #ifdef TOSTOP
735 			  | TOSTOP
736 #endif
737 #ifdef ECHOPTR
738 			  | ECHOPRT
739 #endif
740 #ifdef XCASE
741 			  | XCASE
742 #endif
743 			  );
744 
745 	mode.c_lflag |= (ISIG | ICANON | ECHO | ECHOE | ECHOK
746 #ifdef ECHOCTL
747 			 | ECHOCTL
748 #endif
749 #ifdef ECHOKE
750 			 | ECHOKE
751 #endif
752  			 );
753 #endif
754 
755 #ifdef TERMIOS
756 	tcsetattr(STDERR_FILENO, TCSADRAIN, &mode);
757 #else
758 	stty(STDERR_FILENO, &mode);
759 #endif
760 }
761 
762 /*
763  * Returns a "good" value for the erase character.  This is loosely based on
764  * the BSD4.4 logic.
765  */
766 static int
767 default_erase(void)
768 {
769 	int result;
770 
771 	if (over_strike
772 	 && key_backspace != 0
773 	 && strlen(key_backspace) == 1)
774 		result = key_backspace[0];
775 	else
776 		result = CERASE;
777 
778 	return result;
779 }
780 
781 /*
782  * Update the values of the erase, interrupt, and kill characters in 'mode'.
783  *
784  * SVr4 tset (e.g., Solaris 2.5) only modifies the intr, quit or erase
785  * characters if they're unset, or if we specify them as options.  This differs
786  * from BSD 4.4 tset, which always sets erase.
787  */
788 static void
789 set_control_chars(void)
790 {
791 #ifdef TERMIOS
792 	if (mode.c_cc[VERASE] == 0 || terasechar >= 0)
793 		mode.c_cc[VERASE] = terasechar >= 0 ? terasechar : default_erase();
794 
795 	if (mode.c_cc[VINTR] == 0 || intrchar >= 0)
796 		 mode.c_cc[VINTR] = intrchar >= 0 ? intrchar : CINTR;
797 
798 	if (mode.c_cc[VKILL] == 0 || tkillchar >= 0)
799 		mode.c_cc[VKILL] = tkillchar >= 0 ? tkillchar : CKILL;
800 #endif
801 }
802 
803 /*
804  * Set up various conversions in 'mode', including parity, tabs, returns,
805  * echo, and case, according to the termcap entry.  If the program we're
806  * running was named with a leading upper-case character, map external
807  * uppercase to internal lowercase.
808  */
809 static void
810 set_conversions(void)
811 {
812 #ifdef __OBSOLETE__
813 	/*
814 	 * Conversion logic for some *really* ancient terminal glitches,
815 	 * not supported in terminfo.  Left here for succeeding generations
816 	 * to marvel at.
817 	 */
818 	if (tgetflag("UC")) {
819 #ifdef IUCLC
820 		mode.c_iflag |= IUCLC;
821 		mode.c_oflag |= OLCUC;
822 #endif
823 	} else if (tgetflag("LC")) {
824 #ifdef IUCLC
825 		mode.c_iflag &= ~IUCLC;
826 		mode.c_oflag &= ~OLCUC;
827 #endif
828 	}
829 	mode.c_iflag &= ~(PARMRK | INPCK);
830 	mode.c_lflag |= ICANON;
831 	if (tgetflag("EP")) {
832 		mode.c_cflag |= PARENB;
833 		mode.c_cflag &= ~PARODD;
834 	}
835 	if (tgetflag("OP")) {
836 		mode.c_cflag |= PARENB;
837 		mode.c_cflag |= PARODD;
838 	}
839 #endif /* __OBSOLETE__ */
840 
841 #ifdef TERMIOS
842 #ifdef ONLCR
843 	mode.c_oflag |= ONLCR;
844 #endif
845 	mode.c_iflag |= ICRNL;
846 	mode.c_lflag |= ECHO;
847 #ifdef OXTABS
848 	mode.c_oflag |= OXTABS;
849 #endif /* OXTABS */
850 
851 	/* test used to be tgetflag("NL") */
852 	if (newline != (char *)0 && newline[0] == '\n' && !newline[1]) {
853 		/* Newline, not linefeed. */
854 #ifdef ONLCR
855 		mode.c_oflag &= ~ONLCR;
856 #endif
857 		mode.c_iflag &= ~ICRNL;
858 	}
859 #ifdef __OBSOLETE__
860 	if (tgetflag("HD"))			/* Half duplex. */
861 		mode.c_lflag &= ~ECHO;
862 #endif /* __OBSOLETE__ */
863 #ifdef OXTABS
864 	/* test used to be tgetflag("pt") */
865 	if (has_hardware_tabs)			/* Print tabs. */
866 		mode.c_oflag &= ~OXTABS;
867 #endif /* OXTABS */
868 	mode.c_lflag |= (ECHOE | ECHOK);
869 #endif
870 }
871 
872 /* Output startup string. */
873 static void
874 set_init(void)
875 {
876 	char	*p;
877 	bool settle;
878 
879 #ifdef __OBSOLETE__
880 	if (pad_char != (char *)0)		/* Get/set pad character. */
881 		PC = pad_char[0];
882 #endif /* OBSOLETE */
883 
884 #ifdef TAB3
885 	if (oldmode.c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) {
886 		oldmode.c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET);
887 		tcsetattr(STDERR_FILENO, TCSADRAIN, &oldmode);
888 	}
889 #endif
890 	settle = set_tabs();
891 
892 	if (isreset) {
893 		if ((p = reset_1string) != 0) {
894 			tputs(p, 0, outc);
895 			settle = TRUE;
896 		}
897 		if ((p = reset_2string) != 0) {
898 			tputs(p, 0, outc);
899 			settle = TRUE;
900 		}
901 		/* What about rf, rs3, as per terminfo man page? */
902 		/* also might be nice to send rmacs, rmul, rmm */
903 		if ((p = reset_file) != 0
904 		 || (p = init_file) != 0) {
905 			cat(p);
906 			settle = TRUE;
907 		}
908 	}
909 
910 	if (settle) {
911 		(void)putc('\r', stderr);
912 		(void)fflush(stderr);
913 		(void)napms(1000);	/* Settle the terminal. */
914 	}
915 }
916 
917 /*
918  * Set the hardware tabs on the terminal, using the ct (clear all tabs),
919  * st (set one tab) and ch (horizontal cursor addressing) capabilities.
920  * This is done before if and is, so they can patch in case we blow this.
921  * Return TRUE if we set any tab stops, FALSE if not.
922  */
923 static bool
924 set_tabs()
925 {
926 	if (set_tab && clear_all_tabs) {
927 		int c;
928 
929 		(void)putc('\r', stderr);	/* Force to left margin. */
930 		tputs(clear_all_tabs, 0, outc);
931 
932 		for (c = 8; c < tcolumns; c += 8) {
933 			/* Get to the right column.  In BSD tset, this
934 			 * used to try a bunch of half-clever things
935 			 * with cup and hpa, for an average saving of
936 			 * somewhat less than two character times per
937 			 * tab stop, less that .01 sec at 2400cps. We
938 			 * lost all this cruft because it seemed to be
939 			 * introducing some odd bugs.
940 			 * ----------12345678----------- */
941 			(void)fputs("        ", stderr);
942 			tputs(set_tab, 0, outc);
943 		}
944 		putc('\r', stderr);
945 		return (TRUE);
946 	}
947 	return (FALSE);
948 }
949 
950 /**************************************************************************
951  *
952  * Main sequence
953  *
954  **************************************************************************/
955 
956 /*
957  * Tell the user if a control key has been changed from the default value.
958  */
959 static void
960 report(const char *name, int which, unsigned def)
961 {
962 #ifdef TERMIOS
963 	unsigned older, newer;
964 	char *p;
965 
966 	newer = mode.c_cc[which];
967 	older = oldmode.c_cc[which];
968 
969 	if (older == newer && older == def)
970 		return;
971 
972 	(void)fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to");
973 
974 	/*
975 	 * Check 'delete' before 'backspace', since the key_backspace value
976 	 * is ambiguous.
977 	 */
978 	if (newer == 0177)
979 		(void)fprintf(stderr, "delete.\n");
980 	else if ((p = key_backspace) != 0
981 	 && newer == (unsigned char)p[0]
982 	 && p[1] == '\0')
983 		(void)fprintf(stderr, "backspace.\n");
984 	else if (newer < 040) {
985 		newer ^= 0100;
986 		(void)fprintf(stderr, "control-%c (^%c).\n", newer, newer);
987 	} else
988 		(void)fprintf(stderr, "%c.\n", newer);
989 #endif
990 }
991 
992 /*
993  * Convert the obsolete argument forms into something that getopt can handle.
994  * This means that -e, -i and -k get default arguments supplied for them.
995  */
996 static void
997 obsolete(char **argv)
998 {
999 	for (; *argv; ++argv) {
1000 		char *parm = argv[0];
1001 
1002 		if (parm[0] == '-' && parm[1] == '\0')
1003 		{
1004 		    argv[0] = strdup("-q");
1005 		    continue;
1006 		}
1007 
1008 		if ((parm[0] != '-')
1009 		 || (argv[1] && argv[1][0] != '-')
1010 		 || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k')
1011 		 || (parm[2] != '\0'))
1012 			continue;
1013 		switch(argv[0][1]) {
1014 		case 'e':
1015 			argv[0] = strdup("-e^H");
1016 			break;
1017 		case 'i':
1018 			argv[0] = strdup("-i^C");
1019 			break;
1020 		case 'k':
1021 			argv[0] = strdup("-k^U");
1022 			break;
1023 		}
1024 	}
1025 }
1026 
1027 static void
1028 usage(const char* pname)
1029 {
1030 	(void)fprintf(stderr,
1031 "usage: %s [-IQrs] [-] [-e ch] [-i ch] [-k ch] [-m mapping] [terminal]\n", pname);
1032 	exit(EXIT_FAILURE);
1033 }
1034 
1035 static char arg_to_char(void)
1036 {
1037 	return (optarg[0] == '^' && optarg[1] != '\0')
1038 		? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
1039 		: optarg[0];
1040 }
1041 
1042 int
1043 main(int argc, char **argv)
1044 {
1045 #if defined(TIOCGWINSZ) && defined(TIOCSWINSZ)
1046 	struct winsize win;
1047 #endif
1048 	int ch, noinit, noset, quiet, Sflag, sflag, showterm;
1049 	const char *p;
1050 	const char *ttype;
1051 
1052 #ifdef TERMIOS
1053 	if (tcgetattr(STDERR_FILENO, &mode) < 0)
1054 		failed("standard error");
1055 
1056 	oldmode = mode;
1057 	ospeed = cfgetospeed(&mode);
1058 #else
1059 	if (gtty(STDERR_FILENO, &mode) < 0)
1060 		failed("standard error");
1061 
1062 	oldmode = mode;
1063 	ospeed = mode.sg_ospeed;
1064 #endif
1065 
1066 	if ((p = strrchr(*argv, '/')) != 0)
1067 		++p;
1068 	else
1069 		p = *argv;
1070 	if (!CaselessCmp(p, "reset")) {
1071 		isreset = 1;
1072 		reset_mode();
1073 	}
1074 
1075 	obsolete(argv);
1076 	noinit = noset = quiet = Sflag = sflag = showterm = 0;
1077 	while ((ch = getopt(argc, argv, "a:d:e:Ii:k:m:np:qQSrs")) != EOF) {
1078 		switch (ch) {
1079 		case 'q':		/* display term only */
1080 			noset = 1;
1081 			break;
1082 		case 'a':		/* OBSOLETE: map identifier to type */
1083 			add_mapping("arpanet", optarg);
1084 			break;
1085 		case 'd':		/* OBSOLETE: map identifier to type */
1086 			add_mapping("dialup", optarg);
1087 			break;
1088 		case 'e':		/* erase character */
1089 			terasechar = arg_to_char();
1090 			break;
1091 		case 'I':		/* no initialization strings */
1092 			noinit = 1;
1093 			break;
1094 		case 'i':		/* interrupt character */
1095 			intrchar = arg_to_char();
1096 			break;
1097 		case 'k':		/* kill character */
1098 			tkillchar = arg_to_char();
1099 			break;
1100 		case 'm':		/* map identifier to type */
1101 			add_mapping(0, optarg);
1102 			break;
1103 		case 'n':		/* OBSOLETE: set new tty driver */
1104 			break;
1105 		case 'p':		/* OBSOLETE: map identifier to type */
1106 			add_mapping("plugboard", optarg);
1107 			break;
1108 		case 'Q':		/* don't output control key settings */
1109 			quiet = 1;
1110 			break;
1111 		case 'S':		/* OBSOLETE: output TERM & TERMCAP */
1112 			Sflag = 1;
1113 			break;
1114 		case 'r':		/* display term on stderr */
1115 			showterm = 1;
1116 			break;
1117 		case 's':		/* output TERM set command */
1118 			sflag = 1;
1119 			break;
1120 		case '?':
1121 		default:
1122 			usage(*argv);
1123 		}
1124 	}
1125 	argc -= optind;
1126 	argv += optind;
1127 
1128 	if (argc > 1)
1129 		usage(*argv);
1130 
1131 	ttype = get_termcap_entry(*argv);
1132 
1133 	if (!noset) {
1134 		tcolumns = columns;
1135 		tlines = lines;
1136 
1137 #if defined(TIOCGWINSZ) && defined(TIOCSWINSZ)
1138 		/* Set window size */
1139 		(void)ioctl(STDERR_FILENO, TIOCGWINSZ, &win);
1140 		if (win.ws_row == 0 && win.ws_col == 0 &&
1141 		    tlines > 0 && tcolumns > 0) {
1142 			win.ws_row = tlines;
1143 			win.ws_col = tcolumns;
1144 			(void)ioctl(STDERR_FILENO, TIOCSWINSZ, &win);
1145 		}
1146 #endif
1147 		set_control_chars();
1148 		set_conversions();
1149 
1150 		if (!noinit)
1151 			set_init();
1152 
1153 		/* Set the modes if they've changed. */
1154 		if (memcmp(&mode, &oldmode, sizeof(mode)))
1155 #ifdef TERMIOS
1156 			tcsetattr(STDERR_FILENO, TCSADRAIN, &mode);
1157 #else
1158 			stty(STDERR_FILENO, &mode);
1159 #endif
1160 	}
1161 
1162 	/* Get the terminal name from the entry. */
1163 	ttype = _nc_first_name(cur_term->type.term_names);
1164 
1165 	if (noset)
1166 		(void)printf("%s\n", ttype);
1167 	else {
1168 		if (showterm)
1169 			(void)fprintf(stderr, "Terminal type is %s.\n", ttype);
1170 		/*
1171 		 * If erase, kill and interrupt characters could have been
1172 		 * modified and not -Q, display the changes.
1173 		 */
1174 		if (!quiet) {
1175 			report("Erase", VERASE, CERASE);
1176 			report("Kill", VKILL, CINTR);
1177 			report("Interrupt", VINTR, CKILL);
1178 		}
1179 	}
1180 
1181 	if (Sflag)
1182 		err("The -S option is not supported under terminfo.");
1183 
1184 	if (sflag) {
1185 		/*
1186 		 * Figure out what shell we're using.  A hack, we look for an
1187 		 * environmental variable SHELL ending in "csh".
1188 		 */
1189 		if ((p = getenv("SHELL")) != 0
1190 		 && !strcmp(p + strlen(p) - 3, "csh"))
1191 			p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n";
1192 		else
1193 			p = "TERM=%s;\n";
1194 		(void) printf(p, ttype);
1195 	}
1196 
1197 	return EXIT_SUCCESS;
1198 }
1199 
1200 /* tset.c ends here */
1201