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