/**************************************************************************** * Copyright (c) 1998 Free Software Foundation, Inc. * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the * * "Software"), to deal in the Software without restriction, including * * without limitation the rights to use, copy, modify, merge, publish, * * distribute, distribute with modifications, sublicense, and/or sell * * copies of the Software, and to permit persons to whom the Software is * * furnished to do so, subject to the following conditions: * * * * The above copyright notice and this permission notice shall be included * * in all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * * * Except as contained in this notice, the name(s) of the above copyright * * holders shall not be used in advertising or otherwise to promote the * * sale, use or other dealings in this Software without prior written * * authorization. * ****************************************************************************/ /**************************************************************************** * Author: Zeyd M. Ben-Halim 1992,1995 * * and: Eric S. Raymond * ****************************************************************************/ /* * tset.c - terminal initialization utility * * This code was mostly swiped from 4.4BSD tset, with some obsolescent * cruft removed and substantial portions rewritten. A Regents of the * University of California copyright applies to some portions of the * code, and is reproduced below: */ /*- * Copyright (c) 1980, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #define __INTERNAL_CAPS_VISIBLE /* we need to see has_hardware_tabs */ #include #include #include #include #include #if HAVE_GETTTYNAM && HAVE_TTYENT_H #include #endif #ifdef NeXT char *ttyname(int fd); #endif /* this is just to stifle a missing-prototype warning */ #ifdef linux # include #endif #if NEED_PTEM_H /* they neglected to define struct winsize in termios.h -- it's only in termio.h */ #include #include #endif #include /* for bool typedef */ #include MODULE_ID("$From: tset.c,v 0.37 1999/03/14 12:30:02 tom Exp $") extern char **environ; #undef CTRL #define CTRL(x) ((x) & 0x1f) const char *_nc_progname = "tset"; static TTY mode, oldmode; static int terasechar = -1; /* new erase character */ static int intrchar = -1; /* new interrupt character */ static int isreset; /* invoked as reset */ static int tkillchar = -1; /* new kill character */ static int tlines, tcolumns; /* window size */ #define LOWERCASE(c) ((isalpha(c) && isupper(c)) ? tolower(c) : (c)) static int CaselessCmp(const char *a, const char *b) /* strcasecmp isn't portable */ { while (*a && *b) { int cmp = LOWERCASE(*a) - LOWERCASE(*b); if (cmp != 0) break; a++, b++; } return LOWERCASE(*a) - LOWERCASE(*b); } #if !HAVE_STRDUP #define strdup _nc_strdup extern char *_nc_strdup(const char *); #endif /* not HAVE_STRDUP */ static void err(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)fprintf(stderr, "tset: "); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); exit(EXIT_FAILURE); /* NOTREACHED */ } static void failed(const char *msg) { char temp[BUFSIZ]; perror(strcat(strcpy(temp, "tset: "), msg)); exit(EXIT_FAILURE); /* NOTREACHED */ } static void cat(char *file) { register int fd, nr, nw; char buf[BUFSIZ]; if ((fd = open(file, O_RDONLY, 0)) < 0) failed(file); while ((nr = read(fd, buf, sizeof(buf))) > 0) if ((nw = write(STDERR_FILENO, buf, (size_t)nr)) == -1) failed("write to stderr"); if (nr != 0) failed(file); (void)close(fd); } static int outc(int c) { return putc(c, stderr); } /* Prompt the user for a terminal type. */ static const char * askuser(const char *dflt) { static char answer[256]; char *p; /* We can get recalled; if so, don't continue uselessly. */ if (feof(stdin) || ferror(stdin)) { (void)fprintf(stderr, "\n"); exit(EXIT_FAILURE); } for (;;) { if (dflt) (void)fprintf(stderr, "Terminal type? [%s] ", dflt); else (void)fprintf(stderr, "Terminal type? "); (void)fflush(stderr); if (fgets(answer, sizeof(answer), stdin) == 0) { if (dflt == 0) { (void)fprintf(stderr, "\n"); exit(EXIT_FAILURE); } return (dflt); } if ((p = strchr(answer, '\n')) != 0) *p = '\0'; if (answer[0]) return (answer); if (dflt != 0) return (dflt); } } /************************************************************************** * * Mapping logic begins here * **************************************************************************/ /* Baud rate conditionals for mapping. */ #define GT 0x01 #define EQ 0x02 #define LT 0x04 #define NOT 0x08 #define GE (GT | EQ) #define LE (LT | EQ) typedef struct map { struct map *next; /* Linked list of maps. */ const char *porttype; /* Port type, or "" for any. */ const char *type; /* Terminal type to select. */ int conditional; /* Baud rate conditionals bitmask. */ speed_t speed; /* Baud rate to compare against. */ } MAP; static MAP *cur, *maplist; typedef struct speeds { const char *string; int speed; } SPEEDS; static const SPEEDS speeds[] = { { "0", B0 }, { "50", B50 }, { "75", B75 }, { "110", B110 }, { "134", B134 }, { "134.5", B134 }, { "150", B150 }, { "200", B200 }, { "300", B300 }, { "600", B600 }, { "1200", B1200 }, { "1800", B1800 }, { "2400", B2400 }, { "4800", B4800 }, { "9600", B9600 }, { "19200", B19200 }, { "38400", B38400 }, { "19200", B19200 }, { "38400", B38400 }, #ifdef B19200 { "19200", B19200 }, #else #ifdef EXTA { "19200", EXTA }, #endif #endif #ifdef B38400 { "38400", B38400 }, #else #ifdef EXTB { "38400", EXTB }, #endif #endif #ifdef B57600 { "57600", B57600 }, #endif #ifdef B115200 { "115200", B115200 }, #endif #ifdef B230400 { "230400", B230400 }, #endif #ifdef B460800 { "460800", B460800 }, #endif { (char *)0, 0 } }; static int tbaudrate(char *rate) { const SPEEDS *sp; int found = FALSE; /* The baudrate number can be preceded by a 'B', which is ignored. */ if (*rate == 'B') ++rate; for (sp = speeds; sp->string; ++sp) { if (!CaselessCmp(rate, sp->string)) { found = TRUE; break; } } if (!found) err("unknown baud rate %s", rate); return (sp->speed); } /* * Syntax for -m: * [port-type][test baudrate]:terminal-type * The baud rate tests are: >, <, @, =, ! */ static void add_mapping(const char *port, char *arg) { MAP *mapp; char *copy, *p; const char *termp; char *base = 0; copy = strdup(arg); mapp = malloc(sizeof(MAP)); if (copy == 0 || mapp == 0) failed("malloc"); mapp->next = 0; if (maplist == 0) cur = maplist = mapp; else { cur->next = mapp; cur = mapp; } mapp->porttype = arg; mapp->conditional = 0; arg = strpbrk(arg, "><@=!:"); if (arg == 0) { /* [?]term */ mapp->type = mapp->porttype; mapp->porttype = 0; goto done; } if (arg == mapp->porttype) /* [><@=! baud]:term */ termp = mapp->porttype = 0; else termp = base = arg; for (;; ++arg) /* Optional conditionals. */ switch(*arg) { case '<': if (mapp->conditional & GT) goto badmopt; mapp->conditional |= LT; break; case '>': if (mapp->conditional & LT) goto badmopt; mapp->conditional |= GT; break; case '@': case '=': /* Not documented. */ mapp->conditional |= EQ; break; case '!': mapp->conditional |= NOT; break; default: goto next; } next: if (*arg == ':') { if (mapp->conditional) goto badmopt; ++arg; } else { /* Optional baudrate. */ arg = strchr(p = arg, ':'); if (arg == 0) goto badmopt; *arg++ = '\0'; mapp->speed = tbaudrate(p); } if (arg == (char *)0) /* Non-optional type. */ goto badmopt; mapp->type = arg; /* Terminate porttype, if specified. */ if (termp != 0) *base = '\0'; /* If a NOT conditional, reverse the test. */ if (mapp->conditional & NOT) mapp->conditional = ~mapp->conditional & (EQ | GT | LT); /* If user specified a port with an option flag, set it. */ done: if (port) { if (mapp->porttype) badmopt: err("illegal -m option format: %s", copy); mapp->porttype = port; } #ifdef MAPDEBUG (void)printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY"); (void)printf("type: %s\n", mapp->type); (void)printf("conditional: "); p = ""; if (mapp->conditional & GT) { (void)printf("GT"); p = "/"; } if (mapp->conditional & EQ) { (void)printf("%sEQ", p); p = "/"; } if (mapp->conditional & LT) (void)printf("%sLT", p); (void)printf("\nspeed: %d\n", mapp->speed); #endif } /* * Return the type of terminal to use for a port of type 'type', as specified * by the first applicable mapping in 'map'. If no mappings apply, return * 'type'. */ static const char * mapped(const char *type) { MAP *mapp; int match; for (mapp = maplist; mapp; mapp = mapp->next) if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) { switch (mapp->conditional) { case 0: /* No test specified. */ match = TRUE; break; case EQ: match = (ospeed == mapp->speed); break; case GE: match = (ospeed >= mapp->speed); break; case GT: match = (ospeed > mapp->speed); break; case LE: match = (ospeed <= mapp->speed); break; case LT: match = (ospeed < mapp->speed); break; default: match = FALSE; } if (match) return (mapp->type); } /* No match found; return given type. */ return (type); } /************************************************************************** * * Entry fetching * **************************************************************************/ /* * Figure out what kind of terminal we're dealing with, and then read in * its termcap entry. */ static const char * get_termcap_entry(char *userarg) { int rval, errret; char *p; const char *ttype; #if HAVE_GETTTYNAM struct ttyent *t; #else FILE *fp; #endif char *ttypath; if (userarg) { ttype = userarg; goto found; } /* Try the environment. */ if ((ttype = getenv("TERM")) != 0) goto map; if ((ttypath = ttyname(STDERR_FILENO)) != 0) { if ((p = strrchr(ttypath, '/')) != 0) ++p; else p = ttypath; #if HAVE_GETTTYNAM /* * We have the 4.3BSD library call getttynam(3); that means * there's an /etc/ttys to look up device-to-type mappings in. * Try ttyname(3); check for dialup or other mapping. */ if ((t = getttynam(p))) { ttype = t->ty_type; goto map; } #else if ((fp = fopen("/etc/ttytype", "r")) != 0 || (fp = fopen("/etc/ttys", "r")) != 0) { char buffer[BUFSIZ]; char *s, *t, *d; while (fgets(buffer, sizeof(buffer)-1, fp) != 0) { for (s = buffer, t = d = 0; *s; s++) { if (isspace(*s)) *s = '\0'; else if (t == 0) t = s; else if (d == 0 && s != buffer && s[-1] == '\0') d = s; } if (t != 0 && d != 0 && !strcmp(d,p)) { ttype = strdup(t); fclose(fp); goto map; } } fclose(fp); } #endif /* HAVE_GETTTYNAM */ } /* If still undefined, use "unknown". */ ttype = "unknown"; map: ttype = mapped(ttype); /* * If not a path, remove TERMCAP from the environment so we get a * real entry from /etc/termcap. This prevents us from being fooled * by out of date stuff in the environment. */ found: if ((p = getenv("TERMCAP")) != 0 && *p != '/') { /* 'unsetenv("TERMCAP")' is not portable. * The 'environ' array is better. */ int n; for (n = 0; environ[n] != 0; n++) { if (!strncmp("TERMCAP=", environ[n], 8)) { while ((environ[n] = environ[n+1]) != 0) { n++; } break; } } } /* * ttype now contains a pointer to the type of the terminal. * If the first character is '?', ask the user. */ if (ttype[0] == '?') { if (ttype[1] != '\0') ttype = askuser(ttype + 1); else ttype = askuser(0); } /* Find the terminfo entry. If it doesn't exist, ask the user. */ while ((rval = setupterm((NCURSES_CONST char *)ttype, STDOUT_FILENO, &errret)) != OK) { if (errret == 0) { (void)fprintf(stderr, "tset: unknown terminal type %s\n", ttype); ttype = 0; } else { (void)fprintf(stderr, "tset: can't initialize terminal type %s (error %d)\n", ttype, errret); ttype = 0; } ttype = askuser(ttype); } #if BROKEN_LINKER tgetflag("am"); /* force lib_termcap.o to be linked for 'ospeed' */ #endif return (ttype); } /************************************************************************** * * Mode-setting logic * **************************************************************************/ /* some BSD systems have these built in, some systems are missing * one or more definitions. The safest solution is to override. */ #undef CEOF #undef CERASE #undef CINTR #undef CKILL #undef CLNEXT #undef CRPRNT #undef CQUIT #undef CSTART #undef CSTOP #undef CSUSP /* control-character defaults */ #define CEOF CTRL('D') #define CERASE CTRL('H') #define CINTR 127 /* ^? */ #define CKILL CTRL('U') #define CLNEXT CTRL('v') #define CRPRNT CTRL('r') #define CQUIT CTRL('\\') #define CSTART CTRL('Q') #define CSTOP CTRL('S') #define CSUSP CTRL('Z') #define CHK(val, dft) ((int)val <= 0 ? dft : val) static bool set_tabs (void); /* * Reset the terminal mode bits to a sensible state. Very useful after * a child program dies in raw mode. */ static void reset_mode(void) { #ifdef TERMIOS tcgetattr(STDERR_FILENO, &mode); #else stty(STDERR_FILENO,&mode); #endif #ifdef TERMIOS #if defined(VDISCARD) && defined(CDISCARD) mode.c_cc[VDISCARD] = CHK(mode.c_cc[VDISCARD], CDISCARD); #endif mode.c_cc[VEOF] = CHK(mode.c_cc[VEOF], CEOF); mode.c_cc[VERASE] = CHK(mode.c_cc[VERASE], CERASE); #if defined(VFLUSH) && defined(CFLUSH) mode.c_cc[VFLUSH] = CHK(mode.c_cc[VFLUSH], CFLUSH); #endif mode.c_cc[VINTR] = CHK(mode.c_cc[VINTR], CINTR); mode.c_cc[VKILL] = CHK(mode.c_cc[VKILL], CKILL); #if defined(VLNEXT) && defined(CLNEXT) mode.c_cc[VLNEXT] = CHK(mode.c_cc[VLNEXT], CLNEXT); #endif mode.c_cc[VQUIT] = CHK(mode.c_cc[VQUIT], CQUIT); #if defined(VREPRINT) && defined(CRPRNT) mode.c_cc[VREPRINT] = CHK(mode.c_cc[VREPRINT], CRPRNT); #endif #if defined(VSTART) && defined(CSTART) mode.c_cc[VSTART] = CHK(mode.c_cc[VSTART], CSTART); #endif #if defined(VSTOP) && defined(CSTOP) mode.c_cc[VSTOP] = CHK(mode.c_cc[VSTOP], CSTOP); #endif #if defined(VSUSP) && defined(CSUSP) mode.c_cc[VSUSP] = CHK(mode.c_cc[VSUSP], CSUSP); #endif #if defined(VWERASE) && defined(CWERASE) mode.c_cc[VWERASE] = CHK(mode.c_cc[VWERASE], CWERASE); #endif mode.c_iflag &= ~(IGNBRK | PARMRK | INPCK | ISTRIP | INLCR | IGNCR #ifdef IUCLC | IUCLC #endif #ifdef IXANY | IXANY #endif | IXOFF); mode.c_iflag |= (BRKINT | IGNPAR | ICRNL | IXON #ifdef IMAXBEL | IMAXBEL #endif ); mode.c_oflag &= ~(0 #ifdef OLCUC | OLCUC #endif #ifdef OCRNL | OCRNL #endif #ifdef ONOCR | ONOCR #endif #ifdef ONLRET | ONLRET #endif #ifdef OFILL | OFILL #endif #ifdef OFDEL | OFDEL #endif #ifdef NLDLY | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY #endif ); mode.c_oflag |= (OPOST #ifdef ONLCR | ONLCR #endif ); mode.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CLOCAL); mode.c_cflag |= (CS8 | CREAD); mode.c_lflag &= ~(ECHONL | NOFLSH #ifdef TOSTOP | TOSTOP #endif #ifdef ECHOPTR | ECHOPRT #endif #ifdef XCASE | XCASE #endif ); mode.c_lflag |= (ISIG | ICANON | ECHO | ECHOE | ECHOK #ifdef ECHOCTL | ECHOCTL #endif #ifdef ECHOKE | ECHOKE #endif ); #endif #ifdef TERMIOS tcsetattr(STDERR_FILENO, TCSADRAIN, &mode); #else stty(STDERR_FILENO, &mode); #endif } /* * Returns a "good" value for the erase character. This is loosely based on * the BSD4.4 logic. */ static int default_erase(void) { int result; if (over_strike && key_backspace != 0 && strlen(key_backspace) == 1) result = key_backspace[0]; else result = CERASE; return result; } /* * Update the values of the erase, interrupt, and kill characters in 'mode'. * * SVr4 tset (e.g., Solaris 2.5) only modifies the intr, quit or erase * characters if they're unset, or if we specify them as options. This differs * from BSD 4.4 tset, which always sets erase. */ static void set_control_chars(void) { #ifdef TERMIOS if (mode.c_cc[VERASE] == 0 || terasechar >= 0) mode.c_cc[VERASE] = terasechar >= 0 ? terasechar : default_erase(); if (mode.c_cc[VINTR] == 0 || intrchar >= 0) mode.c_cc[VINTR] = intrchar >= 0 ? intrchar : CINTR; if (mode.c_cc[VKILL] == 0 || tkillchar >= 0) mode.c_cc[VKILL] = tkillchar >= 0 ? tkillchar : CKILL; #endif } /* * Set up various conversions in 'mode', including parity, tabs, returns, * echo, and case, according to the termcap entry. If the program we're * running was named with a leading upper-case character, map external * uppercase to internal lowercase. */ static void set_conversions(void) { #ifdef __OBSOLETE__ /* * Conversion logic for some *really* ancient terminal glitches, * not supported in terminfo. Left here for succeeding generations * to marvel at. */ if (tgetflag("UC")) { #ifdef IUCLC mode.c_iflag |= IUCLC; mode.c_oflag |= OLCUC; #endif } else if (tgetflag("LC")) { #ifdef IUCLC mode.c_iflag &= ~IUCLC; mode.c_oflag &= ~OLCUC; #endif } mode.c_iflag &= ~(PARMRK | INPCK); mode.c_lflag |= ICANON; if (tgetflag("EP")) { mode.c_cflag |= PARENB; mode.c_cflag &= ~PARODD; } if (tgetflag("OP")) { mode.c_cflag |= PARENB; mode.c_cflag |= PARODD; } #endif /* __OBSOLETE__ */ #ifdef TERMIOS #ifdef ONLCR mode.c_oflag |= ONLCR; #endif mode.c_iflag |= ICRNL; mode.c_lflag |= ECHO; #ifdef OXTABS mode.c_oflag |= OXTABS; #endif /* OXTABS */ /* test used to be tgetflag("NL") */ if (newline != (char *)0 && newline[0] == '\n' && !newline[1]) { /* Newline, not linefeed. */ #ifdef ONLCR mode.c_oflag &= ~ONLCR; #endif mode.c_iflag &= ~ICRNL; } #ifdef __OBSOLETE__ if (tgetflag("HD")) /* Half duplex. */ mode.c_lflag &= ~ECHO; #endif /* __OBSOLETE__ */ #ifdef OXTABS /* test used to be tgetflag("pt") */ if (has_hardware_tabs) /* Print tabs. */ mode.c_oflag &= ~OXTABS; #endif /* OXTABS */ mode.c_lflag |= (ECHOE | ECHOK); #endif } /* Output startup string. */ static void set_init(void) { char *p; bool settle; #ifdef __OBSOLETE__ if (pad_char != (char *)0) /* Get/set pad character. */ PC = pad_char[0]; #endif /* OBSOLETE */ #ifdef TAB3 if (oldmode.c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) { oldmode.c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET); tcsetattr(STDERR_FILENO, TCSADRAIN, &oldmode); } #endif settle = set_tabs(); if (isreset) { if ((p = reset_1string) != 0) { tputs(p, 0, outc); settle = TRUE; } if ((p = reset_2string) != 0) { tputs(p, 0, outc); settle = TRUE; } /* What about rf, rs3, as per terminfo man page? */ /* also might be nice to send rmacs, rmul, rmm */ if ((p = reset_file) != 0 || (p = init_file) != 0) { cat(p); settle = TRUE; } } if (settle) { (void)putc('\r', stderr); (void)fflush(stderr); (void)napms(1000); /* Settle the terminal. */ } } /* * Set the hardware tabs on the terminal, using the ct (clear all tabs), * st (set one tab) and ch (horizontal cursor addressing) capabilities. * This is done before if and is, so they can patch in case we blow this. * Return TRUE if we set any tab stops, FALSE if not. */ static bool set_tabs() { if (set_tab && clear_all_tabs) { int c; (void)putc('\r', stderr); /* Force to left margin. */ tputs(clear_all_tabs, 0, outc); for (c = 8; c < tcolumns; c += 8) { /* Get to the right column. In BSD tset, this * used to try a bunch of half-clever things * with cup and hpa, for an average saving of * somewhat less than two character times per * tab stop, less that .01 sec at 2400cps. We * lost all this cruft because it seemed to be * introducing some odd bugs. * ----------12345678----------- */ (void)fputs(" ", stderr); tputs(set_tab, 0, outc); } putc('\r', stderr); return (TRUE); } return (FALSE); } /************************************************************************** * * Main sequence * **************************************************************************/ /* * Tell the user if a control key has been changed from the default value. */ static void report(const char *name, int which, unsigned def) { #ifdef TERMIOS unsigned older, newer; char *p; newer = mode.c_cc[which]; older = oldmode.c_cc[which]; if (older == newer && older == def) return; (void)fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to"); /* * Check 'delete' before 'backspace', since the key_backspace value * is ambiguous. */ if (newer == 0177) (void)fprintf(stderr, "delete.\n"); else if ((p = key_backspace) != 0 && newer == (unsigned char)p[0] && p[1] == '\0') (void)fprintf(stderr, "backspace.\n"); else if (newer < 040) { newer ^= 0100; (void)fprintf(stderr, "control-%c (^%c).\n", newer, newer); } else (void)fprintf(stderr, "%c.\n", newer); #endif } /* * Convert the obsolete argument forms into something that getopt can handle. * This means that -e, -i and -k get default arguments supplied for them. */ static void obsolete(char **argv) { for (; *argv; ++argv) { char *parm = argv[0]; if (parm[0] == '-' && parm[1] == '\0') { argv[0] = strdup("-q"); continue; } if ((parm[0] != '-') || (argv[1] && argv[1][0] != '-') || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k') || (parm[2] != '\0')) continue; switch(argv[0][1]) { case 'e': argv[0] = strdup("-e^H"); break; case 'i': argv[0] = strdup("-i^C"); break; case 'k': argv[0] = strdup("-k^U"); break; } } } static void usage(const char* pname) { (void)fprintf(stderr, "usage: %s [-IQrs] [-] [-e ch] [-i ch] [-k ch] [-m mapping] [terminal]\n", pname); exit(EXIT_FAILURE); } static char arg_to_char(void) { return (optarg[0] == '^' && optarg[1] != '\0') ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1])) : optarg[0]; } int main(int argc, char **argv) { #if defined(TIOCGWINSZ) && defined(TIOCSWINSZ) struct winsize win; #endif int ch, noinit, noset, quiet, Sflag, sflag, showterm; const char *p; const char *ttype; #ifdef __OpenBSD__ char tcapbuf[1024], *t; int tcgetent(char *, const char *); void wrtermcap (char *); #endif #ifdef TERMIOS if (tcgetattr(STDERR_FILENO, &mode) < 0) failed("standard error"); oldmode = mode; ospeed = cfgetospeed(&mode); #else if (gtty(STDERR_FILENO, &mode) < 0) failed("standard error"); oldmode = mode; ospeed = mode.sg_ospeed; #endif if ((p = strrchr(*argv, '/')) != 0) ++p; else p = *argv; if (!CaselessCmp(p, "reset")) { isreset = 1; reset_mode(); } obsolete(argv); noinit = noset = quiet = Sflag = sflag = showterm = 0; while ((ch = getopt(argc, argv, "a:d:e:Ii:k:m:np:qQSrs")) != -1) { switch (ch) { case 'q': /* display term only */ noset = 1; break; case 'a': /* OBSOLETE: map identifier to type */ add_mapping("arpanet", optarg); break; case 'd': /* OBSOLETE: map identifier to type */ add_mapping("dialup", optarg); break; case 'e': /* erase character */ terasechar = arg_to_char(); break; case 'I': /* no initialization strings */ noinit = 1; break; case 'i': /* interrupt character */ intrchar = arg_to_char(); break; case 'k': /* kill character */ tkillchar = arg_to_char(); break; case 'm': /* map identifier to type */ add_mapping(0, optarg); break; case 'n': /* OBSOLETE: set new tty driver */ break; case 'p': /* OBSOLETE: map identifier to type */ add_mapping("plugboard", optarg); break; case 'Q': /* don't output control key settings */ quiet = 1; break; case 'S': /* OBSOLETE: output TERM & TERMCAP */ Sflag = 1; break; case 'r': /* display term on stderr */ showterm = 1; break; case 's': /* output TERM set command */ sflag = 1; break; case '?': default: usage(*argv); } } argc -= optind; argv += optind; if (argc > 1) usage(*argv); ttype = get_termcap_entry(*argv); #ifdef __OpenBSD__ if (tcgetent(tcapbuf, ttype) < 0) tcapbuf[0] = '\0'; #endif if (!noset) { tcolumns = columns; tlines = lines; #if defined(TIOCGWINSZ) && defined(TIOCSWINSZ) /* Set window size */ (void)ioctl(STDERR_FILENO, TIOCGWINSZ, &win); if (win.ws_row == 0 && win.ws_col == 0 && tlines > 0 && tcolumns > 0) { win.ws_row = tlines; win.ws_col = tcolumns; (void)ioctl(STDERR_FILENO, TIOCSWINSZ, &win); } #endif set_control_chars(); set_conversions(); if (!noinit) set_init(); /* Set the modes if they've changed. */ if (memcmp(&mode, &oldmode, sizeof(mode))) #ifdef TERMIOS tcsetattr(STDERR_FILENO, TCSADRAIN, &mode); #else stty(STDERR_FILENO, &mode); #endif } /* Get the terminal name from the entry. */ ttype = _nc_first_name(cur_term->type.term_names); if (noset) (void)printf("%s\n", ttype); else { if (showterm) (void)fprintf(stderr, "Terminal type is %s.\n", ttype); /* * If erase, kill and interrupt characters could have been * modified and not -Q, display the changes. */ if (!quiet) { report("Erase", VERASE, CERASE); report("Kill", VKILL, CINTR); report("Interrupt", VINTR, CKILL); } } #ifdef __OpenBSD__ if (Sflag) { if (tcapbuf[0]) { (void) printf("%s ", ttype); wrtermcap(tcapbuf); } else err("No termcap entry for %s, only terminfo.", ttype); } #else if (Sflag) err("The -S option is not supported under terminfo."); #endif /* __OpenBSD__ */ #ifdef __OpenBSD__ if (sflag) { /* * Figure out what shell we're using. A hack, we look for an * environmental variable SHELL ending in "csh". */ if ((p = getenv("SHELL")) != 0 && !strcmp(p + strlen(p) - 3, "csh")) { if (tcapbuf[0]) p = "set noglob histchars="";\nsetenv TERM %s;\nsetenv TERMCAP "; else p = "set noglob histchars="";\nsetenv TERM %s;\n"; t = "unset noglob histchars;\n"; } else { if (tcapbuf) { p = "TERM=%s;\nTERMCAP="; t = "export TERMCAP TERM;\n"; } else { if (tcapbuf) { p = "TERM=%s;\nTERMCAP="; t = "export TERMCAP TERM;\n"; } else { p = "TERM=%s;\n"; t = "export TERMCAP;\n"; } } } (void) printf(p, ttype); if (tcapbuf[0]) { putchar('\''); wrtermcap(tcapbuf); fputs("';\n", stdout); } (void)printf(t); } #else if (sflag) { /* * Figure out what shell we're using. A hack, we look for an * environmental variable SHELL ending in "csh". */ if ((p = getenv("SHELL")) != 0 && !strcmp(p + strlen(p) - 3, "csh")) p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n"; else p = "TERM=%s;\n"; (void) printf(p, ttype); } #endif /* __OpenBSD__ */ return EXIT_SUCCESS; } /* tset.c ends here */