tset.c revision cc6c5292fa8a241fe50604cf6a918edfbf7cd7d2
/*
* Copyright 1999 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
** TSET -- set terminal modes
**
** This program does sophisticated terminal initialization.
** I recommend that you include it in your .profile or .login
** file to initialize whatever terminal you are on.
**
** There are several features:
**
** A special file or sequence (as controlled by the termcap 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. These
** characters can be represented as "^X" meaning control-X;
** X is any character.
**
** 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. You can represent
** a type as "?type". This will ask you what type you want it
** to be -- if you reply with just a newline, it will default
** to the type given.
**
** The current terminal type can be queried.
**
** Usage:
** tset [-] [-EC] [-eC] [-kC] [-iC] [-s] [-h] [-u] [-r]
** [-m [ident] [test baudrate] :type]
** [-Q] [-I] [-S] [type]
**
** In systems with environments, use:
** eval `tset -s ...`
** Actually, this doesn't work in old csh's.
** Instead, use:
** tset -s ... > tset.tmp
** source tset.tmp
** rm tset.tmp
** or:
** set noglob
** set term=(`tset -S ....`)
** setenv TERM $term[1]
** setenv TERMCAP "$term[2]"
** unset term
** unset noglob
**
** 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. If no other flags
** are stated, the only affect is to write
** the terminal type on the standard output.
** -r -- report to user in addition to other flags.
** -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 not specified,
** the erase character is untouched; however, if
** not specified and the erase character is NULL
** (zero byte), the erase character is set to CERASE.
** -kC -- set the kill character to C on all terminals.
** Default for C is control-U. 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 CKILL.
** -iC -- set the interrupt character to C on all terminals.
** Default for C is control-C. If not specified, the
** interrupt character is untouched; however, if
** not specified and the interrupt character is NULL
** (zero byte), the interrupt character is set to
** control-C.
** -qC -- reserved for setable quit character.
** -m -- map the system identified type to some user
** specified type. The mapping can be baud rate
** dependent. This replaces the old -d, -p flags.
** (-d type -> -m dialup:type)
** (-p type -> -m plug:type)
** Syntax: -m identifier [test baudrate] :type
** where: ``identifier'' is terminal type found in
** matches any identifier); ``test'' may be any combination
** of > = < ! @; ``baudrate'' is as with stty(1);
** ``type'' is the actual terminal type to use if the
** mapping condition is met. Multiple maps are scanned
** in order and the first match prevails.
** -n -- If the new tty driver from UCB is available, this flag
** will activate the new options for erase and kill
** processing. This will be different for printers
** and crt's. For crts, if the baud rate is < 1200 then
** erase and kill don't remove characters from the screen.
** -h -- don't read htmp file. Normally the terminal type
** is determined by reading the htmp file or the
** environment (unless some mapping is specified).
** This forces a read of the ttytype file -- useful
** when htmp is somehow wrong. (V6 only)
** -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.
** -s -- output setenv commands for TERM. This can be
** used with
** `tset -s ...`
** and is to be prefered to:
** setenv TERM `tset - ...`
** because -s sets the TERMCAP variable also.
** -S -- Similar to -s but outputs 2 strings suitable for
** use in csh .login files as follows:
** set noglob
** set term=(`tset -S .....`)
** setenv TERM $term[1]
** setenv TERMCAP "$term[2]"
** unset term
** unset noglob
** -Q -- be quiet. don't output 'Erase set to' etc.
** -I -- don't do terminal initialization (is & if
** strings).
** -v -- On virtual terminal systems, don't set up a
** virtual terminal. Otherwise tset will tell
** the operating system what kind of terminal you
** are on (if it is a known terminal) and fix up
** the output of -s to use virtual terminal sequences.
**
** Files:
** contains a terminal id -> terminal type
** mapping; used when any user mapping is specified,
** or the environment doesn't have TERM set.
** a terminal_type -> terminal_capabilities
** mapping.
**
** Return Codes:
** -1 -- couldn't open termcap.
** 1 -- bad terminal type, or standard output not tty.
** 0 -- ok.
**
** Defined Constants:
** DIALUP -- the type code for a dialup port.
** PLUGBOARD -- the type code for a plugboard port.
** ARPANET -- the type code for an arpanet port.
** BACKSPACE -- control-H, the default for -e.
** CNTL('U') -- control-U, the default for -k.
** OLDERASE -- the ancient default erase 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.
** GTTYN -- defines file containing generalized ttynames
** and compiles code to look there.
**
** Requires:
** Routines to handle htmp, ttys, and termcap.
**
** Compilation Flags:
** OLDFLAGS -- must be defined to compile code for any of
** the -d, -p, or -a flags.
** OLDDIALUP -- accept the -d flag.
** OLDPLUGBOARD -- accept the -p flag.
** OLDARPANET -- accept the -a flag.
** V6 -- if clear, use environments, not htmp.
** also use TIOCSETN rather than stty to avoid flushing
**
** Trace Flags:
** none
**
** Diagnostics:
** Bad flag
** An incorrect option was specified.
** Too few args
** more command line arguments are required.
** Unexpected arg
** wrong type of argument was encountered.
** Cannot open ...
** The specified file could not be openned.
** Type ... unknown
** An unknown terminal type was specified.
** Cannot update htmp
** Cannot update htmp file when the standard
** output is not a terminal.
** Erase set to ...
** Telling that the erase character has been
** set to the specified character.
** Kill set to ...
** Ditto for kill
** Erase is ... Kill is ...
** wierd before, but they are being left as-is.
** Not a terminal
** Set if FILEDES is not a terminal.
**
** Compilation Instructions:
** cc -n -O tset.c -ltermlib
** 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.
**
** For version 6 the compile command should be:
**
**
** History:
** 1/81 -- Added alias checking for mapping identifiers.
** 7/80 -- '-S' added. '-m' mapping added. TERMCAP string
** cleaned up.
** 3/80 -- Changed to use tputs. Prc & flush added.
** 10/79 -- '-s' option extended to handle TERMCAP
** variable, set noglob, quote the entry,
** and know about the Bourne shell. Terminal
** initialization moved to before any information
** output so screen clears would not screw you.
** '-Q' option added.
** 8/79 -- '-' option alone changed to only output
** type. '-s' option added. 'VERSION7'
** changed to 'V6' for compatibility.
** so the '-' option is changed to output only
** the terminal type to STDOUT instead of
** FILEDES.
** 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 -- Written.
*/
#include <stdio.h>
#include <termio.h>
#include <signal.h>
#define YES 1
#define NO 0
#define CNTL(c) ((c)&037)
#define OLDERASE '#'
/* default special characters */
#ifndef CERASE
#define CERASE '\177'
#endif
#ifndef CKILL
#endif
#ifndef CINTR
#endif
#ifndef CDSUSP
#define CBRK 0377
#endif
#define UIDMASK -1
#define USAGE "usage: tset [-] [-rsIQS] [-eC] [-kC] [-iC] [-m [ident][test speed]:type] [type]\n"
#define OLDFLAGS
#define DIALUP "dialup"
#define OLDDIALUP "sd"
#define PLUGBOARD "plugboard"
#define OLDPLUGBOARD "sp"
/*
#define ARPANET "arpanet"
#define OLDARPANET "sa"
*/
#define DEFTYPE "unknown"
#define NOTTY 'x'
/*
* Baud Rate Conditionals
*/
#define ANY 0
#define GT 1
#define EQ 2
#define LT 4
#define NMAP 10
struct map {
char *Ident;
char Test;
char Speed;
char *Type;
/* This should be available in an include file */
struct
{
char *string;
int speed;
int baudrate;
} speeds[] = {
"0", B0, 0,
0,
};
signed char Erase_char; /* new erase character */
char Kill_char; /* new kill character */
char Intr_char; /* new interrupt character */
char Specialerase; /* set => Erase_char only on terminals with backspace */
char *TtyType; /* type of terminal */
char *DefType; /* default type if none other computed */
char *NewType; /* mapping identifier based on old flags */
int Mapped; /* mapping has been specified */
int Dash_u; /* don't update htmp */
int Dash_h; /* don't read htmp */
int DoSetenv; /* output setenv commands */
int BeQuiet; /* be quiet */
int NoInit; /* don't output initialization string */
int IsReset; /* invoked as reset */
int Report; /* report current type */
int Ureport; /* report to user */
int RepOnly; /* report only */
int CmndLine; /* output full command lines (-s option) */
int Ask; /* ask user for termtype */
int PadBaud; /* Min rate of padding needed */
#define CAPBUFSIZ 1024
char *Ttycap; /* termcap line from termcap or environ */
char Aliasbuf[128];
char *Alias[16];
extern char *strcpy();
extern char *index();
struct delay
{
int d_delay;
int d_bits;
};
#include "tset.delays.h"
int istermios;
void reportek(char *, char, char, char);
void prs(char *);
void prc(char);
void flush(void);
void cat(char *);
void bmove(char *, char *, int);
void makealias(char *);
void wrtermcap(char *);
void fatal (char *, char *);
char reset(); /* Routine for checking&resetting chars */
int
{
char termbuf[32];
auto char *bufp;
char *p;
char *command;
int i;
int Break;
int Not;
char *nextarg();
char *mapped();
extern char *rindex();
extern char *getenv();
extern char *tgetstr();
char bs_char;
int csh;
void setmode();
extern char PC;
extern short ospeed;
{
prs("Not a terminal\n");
exit(1);
}
for(i = 0; i < NCC; i++)
} else
command++;
else
{
/*
* Reset the teletype mode bits to a sensible state.
* Copied from the program by Kurt Shoens & Mark Horton.
* Very useful after crapping out in raw.
*/
for(i = 0; i < NCC; i++)
}
if (istermios < 0) {
for(i = 0; i < NCC; i++)
} else
}
{
}
argc--;
/* scan argument list and collect flags */
while (--argc >= 0)
{
p = *++argv;
if (*p == '-')
{
if (*++p == NULL)
else while (*p) switch (*p++)
{
case 'r': /* report to user */
continue;
case 'E': /* special erase: operate on all but TTY33 */
Specialerase = YES;
/* explicit fall-through to -e case */
case 'e': /* erase character */
if (*p == NULL)
Erase_char = -1;
else
{
if (*++p == '?')
Erase_char = '\177';
else
Erase_char = CNTL(*p);
else
Erase_char = *p;
p++;
}
continue;
case 'i': /* interrupt character */
if (*p == NULL)
else
{
if (*++p == '?')
Intr_char = '\177';
else
else
Intr_char = *p;
p++;
}
continue;
case 'k': /* kill character */
if (*p == NULL)
else
{
if (*++p == '?')
Kill_char = '\177';
else
else
Kill_char = *p;
p++;
}
continue;
# ifdef OLDFLAGS
# ifdef OLDDIALUP
case 'd': /* dialup type */
goto mapold;
# endif
# ifdef OLDPLUGBOARD
case 'p': /* plugboard type */
goto mapold;
# endif
# ifdef OLDARPANET
case 'a': /* arpanet type */
goto mapold;
# endif
if (*p == NULL)
{
}
Map++;
p = "";
continue;
# endif
case 'm': /* map identifier to type */
/* This code is very loose. Almost no
** syntax checking is done!! However,
** illegal syntax will only produce
** weird results.
*/
if (*p == NULL)
{
}
if (isalnum(*p))
{
while (isalnum(*p)) p++;
}
else
while (!Break) switch (*p)
{
case NULL:
continue;
case ':': /* mapped type */
*p++ = NULL;
continue;
case '>': /* conditional */
*p++ = NULL;
continue;
case '<': /* conditional */
*p++ = NULL;
continue;
case '=': /* conditional */
case '@':
*p++ = NULL;
continue;
case '!': /* invert conditions */
*p++ = NULL;
continue;
case 'B': /* Baud rate */
p++;
/* intentional fallthru */
default:
if (isdigit(*p) || *p == 'e')
{
while (isalnum(*p) || *p == '.')
p++;
}
else
continue;
}
if (Not) /* invert sense of test */
{
}
if (*p == NULL)
{
}
p = "";
Map++;
continue;
case 'h': /* don't get type from htmp or env */
continue;
case 'u': /* don't update htmp */
continue;
case 's': /* output setenv commands */
continue;
case 'S': /* output setenv strings */
continue;
case 'Q': /* be quiet */
continue;
case 'I': /* no initialization */
continue;
case 'A': /* Ask user */
continue;
case 'v': /* no virtual terminal */
DoVirtTerm = NO;
continue;
default:
*p-- = NULL;
fatal("Bad flag -", p);
}
}
else
{
/* terminal type */
DefType = p;
}
}
if (DefType)
{
if (Mapped)
{
}
else
}
/*
* Get rid of $TERMCAP, if it's there, so we get a real
* fooled by out of date stuff in the environment, and
*/
/* get current idea of terminal type from environment */
/* If still undefined, use DEFTYPE */
if (TtyType == 0)
{
}
/* check for dialup or other mapping */
if (Mapped)
{
}
/* TtyType now contains a pointer to the type of the terminal */
/* If the first character is '?', ask the user */
if (TtyType[0] == '?')
{
TtyType++;
if (TtyType[0] == '\0')
}
if (Ask)
{
ask:
prs("TERM = (");
prs(") ");
flush();
/* read the terminal. If not empty, set type */
if (i > 0)
{
i--;
termbuf[i] = '\0';
if (termbuf[0] != '\0')
}
}
/* get terminal capabilities */
{
case -1:
prs("Cannot find termcap\n");
flush();
exit(-1);
case 0:
prs("Type ");
prs(" unknown\n");
flush();
if (DoSetenv)
{
Alias[0] = '\0';
goto ask;
}
else
exit(1);
}
}
if (!RepOnly)
{
/* determine erase and kill characters */
Erase_char = 0;
bs_char = p[0];
else if (tgetflag("bs"))
else
bs_char = 0;
/*
* The next statement can't be fixed, because now users
* depend on keeping their erase character as DEL if the
* system set it there. People who want backspace have
* to say tset -e.
*/
{
Erase_char = -1;
}
if (Erase_char < 0)
if (curerase == 0)
if (Erase_char != 0)
if (curintr == 0)
if (Intr_char != 0)
if (curkill == 0)
if (Kill_char != 0)
/* set modes */
break;
}
}
else if (tgetflag("LC")) {
}
if (tgetflag("EP")) {
}
if (tgetflag("OP")) {
}
}
if (tgetflag("hc"))
{ /** set printer modes **/
}
/* get pad character */
/* output startup string */
if (!NoInit)
{
{
setmode(-1);
}
if (settabs()) {
flush();
}
{
flush();
}
{
}
if (settle)
{
prc('\r');
if (IsReset)
flush();
}
}
setmode(0); /* set new modes, if they've changed */
/* set up environment for the shell we are using */
/* (this code is rather heuristic, checking for $SHELL */
/* ending in the 3 characters "csh") */
if (DoSetenv)
{
char *sh;
{
}
if (!csh)
/* running Bourne shell */
}
}
/* report type if appropriate */
{
/* if type is the short name, find first alias (if any) */
}
if (DoSetenv)
{
if (csh)
{
if (CmndLine)
if (CmndLine)
}
else
{
}
}
else if (Report)
{
}
if (Ureport)
{
prs("Terminal type is ");
prs("\n");
flush();
}
if (DoSetenv)
{
if (csh)
{
if (CmndLine)
}
else
if (csh)
{
if (CmndLine)
{
}
}
else
}
}
if (RepOnly)
exit(0);
/* tell about changing erase, kill and interrupt characters */
return (0);
}
/*
* 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.
*/
int
settabs(void)
{
char caps[100];
int c;
extern char *tgetstr();
if (set_column == 0)
if (clear_tabs && set_tab) {
}
if (set_tab) {
for (c=0; c<columns; c += 8) {
/* get to that column. */
if (set_column)
if (*tg_out != 'O')
else if (c != 0) {
}
/* set the tab */
}
prc('\r');
return (1);
}
return (0);
}
int flag;
/* flag serves several purposes:
* if called as the result of a signal, flag will be > 0.
* if called from terminal init, flag == -1 means reset "oldmode".
* called with flag == 0 at end of normal mode processing.
*/
{
int i;
if (flag < 0) { /* unconditionally reset oldmode (called from init) */
if (istermios < 0) {
for(i = 0; i < NCC; i++)
} else
} else {
if (istermios < 0) {
for(i = 0; i < NCC; i++)
for(i = 0; i < NCC; i++)
sizeof modes))
}
if (ttymode)
{
} else if (ttymodes) {
}
if (flag > 0) /* trapped signal */
exit(1);
}
void
{
char o;
char n;
char *p;
char buf[32];
char *bufp;
extern char *tgetstr();
if (BeQuiet)
return;
o = old;
n = new;
if (o == n && n == def)
return;
if (o == n)
prs(" is ");
else
prs(" set to ");
prs("Backspace\n");
else if (n == 0177)
prs("Delete\n");
else
{
if (n < 040)
{
prs("Ctrl-");
n ^= 0100;
}
p = "x\n";
p[0] = n;
prs(p);
}
flush();
}
void
{
int i;
struct delay *p;
extern short ospeed;
/* see if this capability exists at all */
if (i < 0)
i = 0;
/* No padding at speeds below PadBaud */
i = 0;
/* clear out the bits, replace with new ones */
/* scan dtab for first entry with adequate delay */
{
if (p->d_delay >= i)
{
p++;
break;
}
}
/* use last entry if none will do */
}
void
prs(char *s)
{
while (*s != '\0')
prc(*s++);
}
char OutBuf[256];
int OutPtr;
void
prc(char c)
{
flush();
}
void
flush(void)
{
if (OutPtr > 0)
OutPtr = 0;
}
void
{
int fd;
int i;
if (fd < 0)
{
prs("Cannot open ");
prs("\n");
flush();
return;
}
}
void
{
char *p, *q;
int i;
i = length;
p = from;
q = to;
while (i-- > 0)
*q++ = *p++;
}
int
{
char *p, *q;
int i;
i = len;
p = a;
q = b;
while ((*p == *q) && --i > 0)
{
p++; q++;
}
return ((*p == *q) && i >= 0);
}
int
sequal(char *a, char *b) /* must be same thru NULL */
{
char *p = a, *q = b;
while (*p && *q && (*p == *q))
{
p++; q++;
}
return (*p == *q);
}
void
{
int i;
char *a;
char *b;
b = buf;
i = 1;
while (*b && *b != ':') {
if (*b == '|') {
*a++ = NULL;
Alias[i++] = a;
b++;
}
else
*a++ = *b++;
}
*a = NULL;
# ifdef DEB
# endif
}
int
isalias(char *ident) /* is ident same as one of the aliases? */
{
char **a = Alias;
if (*a)
while (*a)
if (sequal(ident, *a))
return(YES);
else
a++;
return(NO);
}
/*
* routine to output the string for the environment TERMCAP variable
*/
int ncap = 0;
void
{
char *p = buf;
char *tp;
char *putbuf();
/* discard names with blanks */
/** May not be desireable ? **/
if (*bp == '|') {
tp++;
}
if (space) {
continue;
}
}
*p++ = *bp++;
}
/**/
while (*bp) {
switch (*bp) {
case ':': /* discard empty, cancelled or dupl fields */
tp++;
}
continue;
}
break;
case ' ': /* no spaces in output */
p = putbuf(p, "\\040");
bp++;
continue;
case '!': /* the shell thinks this is history */
p = putbuf(p, "\\041");
bp++;
continue;
case ',': /* the shell thinks this is history */
p = putbuf(p, "\\054");
bp++;
continue;
case '"': /* no quotes in output */
p = putbuf(p, "\\042");
bp++;
continue;
case '\'': /* no quotes in output */
p = putbuf(p, "\\047");
bp++;
continue;
case '`': /* no back quotes in output */
p = putbuf(p, "\\140");
bp++;
continue;
case '\\':
case '^': /* anything following is OK */
*p++ = *bp++;
}
*p++ = *bp++;
}
*p++ = ':'; /* we skipped the last : with the : lookahead hack */
}
int
{
int i;
for (i = 0; i < ncap; i++)
{
return (YES);
}
/* delete a second occurrance of the same capability */
ncap++;
}
char *
char *ptr;
char *str;
{
char buf[20];
while (*str) {
switch (*str) {
case '\033':
str++;
break;
default:
if (*str <= ' ') {
str++;
} else
}
}
return (ptr);
}
int
baudrate(char *p)
{
char buf[8];
int i = 0;
buf[i++] = *p++;
return (-1);
}
char *
char *type;
{
extern short ospeed;
int match;
# ifdef DEB
prmap();
# endif
{
{
{
case ANY: /* no test specified */
case ALL:
break;
case GT:
break;
case GE:
break;
case EQ:
break;
case LE:
break;
case LT:
break;
case NE:
break;
}
if (match)
}
Map++;
}
/* no match found; return given type */
return (type);
}
# ifdef DEB
prmap()
{
{
printf ("%s t:%d s:%d %s\n",
Map++;
}
}
# endif
char *
int argc;
char *argv[];
{
if (argc <= 0)
if (*(*++argv) == '-')
return (*argv);
}
void
{
prc ('\n');
flush();
exit(1);
}
/*
*/
char
char ch;
int def;
{
return def;
return ch;
}