/* Copyright (c) 1979 Regents of the University of California */ # include <sgtty.h> # include <stdio.h> /* ** TSET -- set terminal modes ** ** This program does sophisticated terminal initialization. ** I recommend that you include it in your .start_up or .login ** file to initialize whatever terminal you are on. ** ** There are several features: ** ** A special file or sequence (as controlled by the ttycap file) ** is sent to the terminal. ** ** Mode bits are set on a per-terminal_type basis (much better ** than UNIX itself). This allows special delays, automatic ** tabs, etc. ** ** Erase and Kill characters can be set to whatever you want. ** Default is to change erase to control-H on a terminal which ** can overstrike, and leave it alone on anything else. Kill ** is always left alone unless specifically requested. ** ** Terminals which are dialups or plugboard types can be aliased ** to whatever type you may have in your home or office. Thus, ** if you know that when you dial up you will always be on a ** TI 733, you can specify that fact to tset. ** ** The htmp file, used by ex, etc., can be updated. ** ** The current terminal type can be queried. ** ** Usage: ** tset [-] [-r] [-EC] [-eC] [-d type] [-p type] ** [-b type] [-h] [-u] [type] ** ** In systems with environments, use: ** setenv TERM `tset - ...` ** ** Positional Parameters: ** type -- the terminal type to force. If this is ** specified, initialization is for this ** terminal type. ** ** Flags: ** - -- report terminal type. Whatever type is ** decided on is reported. ** -r -- report to user, on diagnostic output instead ** of standard output. ** -EC -- set the erase character to C on all terminals ** except those which cannot backspace (e.g., ** a TTY 33). C defaults to control-H. ** -eC -- set the erase character to C on all terminals. ** C defaults to control-H. If neither -E or -e ** are specified, the erase character is set to ** control-H if the terminal can both backspace ** and not overstrike (e.g., a CRT). If the erase ** character is NULL (zero byte), it will be reset ** to '#' if nothing else is specified. ** -kC -- set the kill character to C on all terminals. ** Default for C is control-X. If not specified, ** the kill character is untouched; however, if ** not specified and the kill character is NULL ** (zero byte), the kill character is set to '@'. ** -d type -- set the dialup type to be type. If the ** terminal type seems to be dialup, make it ** 'type' instead. There need not be a space ** between 'd' and 'type'. ** -p type -- ditto for a plugboard. ** -b type -- ditto for a bussiplexer. ** -h -- don't read htmp file. Normally the terminal type ** is determined by reading the htmp file (unless ** -d or -p are specified). This forces a read ** of the ttytype file -- useful when htmp is ** somehow wrong. On a version seven system, this ** flag means don't look at the TERM entry in ** the environment. ** -u -- don't update htmp. It seemed like this should ** be put in. Note that htmp is never actually ** written if there are no changes, so don't bother ** bother using this for efficiency reasons alone. ** On version seven systems this flag is ignored. ** ** Files: ** /etc/ttytype ** contains a terminal id -> terminal type ** mapping; used when -h, -d, or -p is used. ** /etc/termcap ** a terminal_type -> terminal_capabilities ** mapping. ** ** Return Codes: ** -1 -- couldn't open ttycap. ** 1 -- bad terminal type, or standard output not tty. ** 0 -- ok. ** ** Defined Constants: ** DIALUP -- the type code for a dialup port ** PLUGBOARD -- the code for a plugboard port. ** BUSSIPLEXER -- the code for a bussiplexer port. ** BACKSPACE -- control-H, the default for -e. ** CONTROLX -- control-X, the default for -k. ** OLDERASE -- the system default erase character. ** OLDKILL -- the system default kill character. ** FILEDES -- the file descriptor to do the operation ** on, nominally 1 or 2. ** STDOUT -- the standard output file descriptor. ** UIDMASK -- the bit pattern to mask with the getuid() ** call to get just the user id. ** ** Requires: ** Routines to handle htmp, ttytype, and ttycap. ** ** Compilation Flags: ** PLUGBOARD -- if defined, accept the -p flag. ** BUSSIPLEXER -- if defined, accept the -b flag. ** FULLLOGIN -- if defined, login sets the ttytype from ** /etc/ttytype file. ** VERSION7 -- if set, use environments, not htmp. ** Also, use 'ioctl' not 'stty' -- to get type- ** ahead. ** GTTYN -- if set, uses generalized tty names. ** ** Compilation Instructions: ** cc -n -O tset.c -lX ** mv a.out tset ** chown bin tset ** chmod 4755 tset ** ** where 'bin' should be whoever owns the 'htmp' file. ** If 'htmp' is 666, then tset need not be setuid. ** ** Author: ** Eric Allman ** Electronics Research Labs ** U.C. Berkeley ** ** History: ** 3/79 -- Use ioctl in version7. ** 12/78 -- modified for eventual migration to VAX/UNIX, ** so the '-' option is changed to output only ** the terminal type to STDOUT instead of ** FILEDES. FULLLOGIN flag added. BUSSIPLEXER ** and -r added. ** 9/78 -- '-' and '-p' options added (now fully ** compatible with ttytype!), and spaces are ** permitted between the -d and the type. ** 8/78 -- The sense of -h and -u were reversed, and the ** -f flag is dropped -- same effect is available ** by just stating the terminal type. ** 10/77 -- This version, in much it's previous state, ** written by Eric Allman. */ # define BACKSPACE ('H' & 037) # define CONTROLX ('X' & 037) # define OLDERASE '#' # define OLDKILL '@' # define FILEDES 2 # define STDOUT 1 # define DIALUP "du" # define PLUGBOARD "pb" # define BUSSIPLEXER "bx" /* # define FULLLOGIN FULLLOGIN login does everything */ # define VERSION7 VERSION7 /* version seven flag */ # define GTTYN GTTYN /* general tty names */ # ifdef VERSION7 # define UIDMASK 0177777 # else # define UIDMASK 0377 # endif # ifdef GTTYN typedef char *ttyid_t; # else typedef char ttyid_t; # endif # define NOTTY 0 char Erase_char; /* new erase character */ char Kill_char; /* new kill character */ char Specialerase; /* set => Erase_char only on terminals with backspace */ ttyid_t Ttyid = NOTTY; /* terminal identifier */ char *Ttytype; /* type of terminal */ char *Dialtype; /* override type if dialup terminal */ char *Plugtype; /* override type if plugboard port */ char *Bxtype; /* override type if bussiplexer port */ int Dash_u; /* don't-update-htmp flag */ int Dash_h; /* don't-read-htmp flag */ int Report; /* report current type */ int Ureport; /* report to user */ char Usage[] = "usage: tset [-] [-r] [-eC] [-kC] [-d T] [-p T] [-b T] [-h] [-u] [type]\n"; char Capbuf[256]; /* line from /etc/ttycap for this Ttytype */ struct delay { int d_delay; int d_bits; }; # include "tset.del.h" main(argc, argv) int argc; char *argv[]; { struct sgttyb mode; struct sgttyb oldmode; char buf[256]; auto char *bufp; register char *p; char *command; register int i; register int error; int mdvect[2]; extern char *stypeof(); # ifndef VERSION7 extern char *hsgettype(); # else extern char *getenv(); # endif # ifdef GTTYN extern char *ttyname(); # endif /* scan argument list and collect flags */ error = 0; command = argv[0]; argc--; while (--argc >= 0) { p = *++argv; if (p[0] == '-') { switch (p[1]) { case 0: /* report current terminal type */ Report++; continue; case 'r': /* report to user */ Ureport++; continue; case 'E': /* special erase: operate on all but TTY33 */ Specialerase++; /* exlicit fall-through to -e case */ case 'e': /* erase character */ if (p[2] == 0) Erase_char = BACKSPACE; else Erase_char = p[2]; continue; case 'k': /* kill character */ if (p[2] == 0) Kill_char = CONTROLX; else Kill_char = p[2]; continue; case 'd': /* dialup type */ if (p[2] != 0) Dialtype = &p[2]; else if (--argc < 0 || argv[1][0] == '-') error++; else Dialtype = *++argv; continue; # ifdef PLUGBOARD case 'p': /* plugboard type */ if (p[2] != 0) Plugtype = &p[2]; else if (--argc < 0 || argv[1][0] == '-') error++; else Plugtype = *++argv; continue; # endif # ifdef BUSSIPLEXER case 'b': /* bussiplexer type */ if (p[2] != 0) Bxtype = &p[2]; else if (--argc < 0 || argv[1][0] == '-') error++; else Bxtype = *++argv; # endif case 'h': /* don't get type from htmp */ Dash_h++; continue; # ifndef VERSION7 case 'u': /* don't update htmp */ Dash_u++; continue; # endif default: prs("Bad flag "); prs(p); prs("\n"); error++; } } else { /* terminal type */ Ttytype = p; } } if (error) { prs(Usage); exit(1); } # ifndef FULLLOGIN /* if dialup is specified, check ttytype not htmp */ if (Dialtype != 0 || Plugtype != 0 || Bxtype != 0) Dash_h++; # endif /* determine terminal id if needed */ if (Ttyid == NOTTY && (Ttytype == 0 || !Dash_h || !Dash_u)) # ifndef VERSION7 Ttyid = ttyn(FILEDES); # else Ttyid = ttyname(FILEDES); # endif # ifndef VERSION7 /* get htmp if ever used */ if (!Dash_u || (Ttytype == 0 && !Dash_h)) { /* get htmp entry */ hget(Ttyid); /* if not for this user, look at ttytype file */ if (hgettype() == 0 || hgetuid() != (getuid() & UIDMASK)) Dash_h++; } # endif /* find terminal type (if not already known) */ if (Ttytype == 0) { /* get type from /etc/ttytype or /etc/htmp */ if (!Dash_h) { # ifndef VERSION7 Ttytype = hsgettype(); # else Ttytype = getenv("TERM"); # endif } if (Ttytype == 0) { Ttytype = stypeof(Ttyid); } /* check for dialup or plugboard override */ if (Dialtype != 0 && bequal(Ttytype, DIALUP, 2)) Ttytype = Dialtype; # ifdef PLUGBOARD else if (Plugtype != 0 && bequal(Ttytype, PLUGBOARD, 2)) Ttytype = Plugtype; # endif # ifdef BUSSIPLEXER else if (Bxtype != 0 && bequal(Ttytype, BUSSIPLEXER, 2)) Ttytype = Bxtype; # endif } /* Ttytype now contains a pointer to the type of the terminal */ if (gtty(FILEDES, &mode) < 0) { prs("Not a terminal\n"); exit(1); } bmove(&mode, &oldmode, sizeof mode); /* get terminal capabilities */ switch (tgetent(Capbuf, Ttytype)) { case -1: prs("Cannot open ttycap file\n"); exit(-1); case 0: prs("Type "); prs(Ttytype); prs(" unknown\n"); exit(1); } /* report type if appropriate */ if (Report || Ureport) { /* find first alias (if any) */ for (p = Capbuf; *p != 0 && *p != '|' && *p != ':'; p++) continue; if (*p == 0 || *p == ':') p = Capbuf; else p++; bufp = p; while (*p != '|' && *p != ':' && *p != 0) p++; i = *p; if (Report) { *p = '\n'; write(STDOUT, bufp, p + 1 - bufp); } if (Ureport) { *p = '\0'; prs("Terminal type is "); prs(bufp); prs("\n"); } *p = i; } /* determine erase and kill characters */ if (Specialerase && !tgetflag("bs")) Erase_char = 0; if (Erase_char == 0) { if (mode.sg_erase == 0) mode.sg_erase = OLDERASE; if (tgetflag("bs") && !tgetflag("os")) mode.sg_erase = BACKSPACE; } else mode.sg_erase = Erase_char; if (mode.sg_kill == 0) mode.sg_kill = OLDKILL; if (Kill_char != 0) mode.sg_kill = Kill_char; /* set modes */ setdelay("dC", CRdelay, CRbits, &mode.sg_flags); setdelay("dN", NLdelay, NLbits, &mode.sg_flags); setdelay("dB", BSdelay, BSbits, &mode.sg_flags); setdelay("dF", FFdelay, FFbits, &mode.sg_flags); setdelay("dT", TBdelay, TBbits, &mode.sg_flags); if (tgetflag("UC") || command[0] == 'T') mode.sg_flags |= LCASE; else if (tgetflag("LC")) mode.sg_flags &= ~LCASE; mode.sg_flags &= ~(EVENP | ODDP | RAW); if (tgetflag("EP")) mode.sg_flags |= EVENP; if (tgetflag("OP")) mode.sg_flags |= ODDP; if ((mode.sg_flags & (EVENP | ODDP)) == 0) mode.sg_flags |= EVENP | ODDP; mode.sg_flags |= CRMOD | ECHO | XTABS; if (tgetflag("NL")) /* new line, not line feed */ mode.sg_flags &= ~CRMOD; if (tgetflag("HD")) /* half duplex */ mode.sg_flags &= ~ECHO; if (tgetflag("pt")) /* print tabs */ mode.sg_flags &= ~XTABS; # ifdef VERSION7 if (!bequal(&mode, &oldmode, sizeof mode)) ioctl(FILEDES, TIOCSETN, &mode); # else if (!bequal(&mode, &oldmode, sizeof mode)) stty(FILEDES, &mode); # endif /* output startup string */ bufp = buf; if (tgetstr("is", &bufp) != 0) prs(buf); bufp = buf; if (tgetstr("if", &bufp) != 0) cat(buf); /* tell about changing erase and kill characters */ reportek("Erase", mode.sg_erase, oldmode.sg_erase, OLDERASE); reportek("Kill", mode.sg_kill, oldmode.sg_kill, OLDKILL); # ifndef VERSION7 /* update htmp */ if (!Dash_u) { if (Ttyid == 0) Ttyid = ttyn(FILEDES); if (Ttyid == 'x') prs("Cannot update htmp\n"); else { /* update htmp file only if changed */ if (!bequal(Capbuf, hsgettype(), 2)) { hsettype(Capbuf[0] | (Capbuf[1] << 8)); hput(Ttyid); } } } # endif exit(0); } reportek(name, new, old, def) char *name; char old; char new; char def; { register char o; register char n; register char *p; o = old; n = new; if (o == n && n == def) return; prs(name); if (o == n) prs(" is "); else prs(" set to "); if (n < 040) { prs("control-"); n = (n & 037) | 0100; } p = "x\n"; p[0] = n; prs(p); } setdelay(cap, dtab, bits, flags) char *cap; struct delay dtab[]; int bits; int *flags; { register int i; register struct delay *p; /* see if this capability exists at all */ i = tgetnum(cap); if (i < 0) i = 0; /* clear out the bits, replace with new ones */ *flags &= ~bits; /* scan dtab for first entry with adequate delay */ for (p = dtab; p->d_delay >= 0; p++) { if (p->d_delay >= i) { p++; break; } } /* use last entry if none will do */ *flags |= (--p)->d_bits; } prs(s) char *s; { register char *p; register char *q; register int i; p = q = s; i = 0; while (*q++ != 0) i++; if (i > 0) write(FILEDES, p, i); } cat(file) char *file; { register int fd; register int i; char buf[512]; fd = open(file, 0); if (fd < 0) { prs("Cannot open "); prs(file); prs("\n"); exit(-1); } while ((i = read(fd, buf, 512)) > 0) write(FILEDES, buf, i); close(fd); } bmove(from, to, length) char *from; char *to; int length; { register char *p, *q; register int i; i = length; p = from; q = to; while (i-- > 0) *q++ = *p++; } bequal(a, b, len) char *a; char *b; int len; { register char *p, *q; register int i; i = len; p = a; q = b; while (i-- > 0) if (*p++ != *q++) return (0); return (1); }