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