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