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