#define __INTERNAL_CAPS_VISIBLE
#include <progs.priv.h>
#include <errno.h>
#include <stdio.h>
#include <termcap.h>
#include <fcntl.h>
#if HAVE_GETTTYNAM && HAVE_TTYENT_H
#include <ttyent.h>
#endif
#ifdef NeXT
char *ttyname(int fd);
#endif
#ifdef linux
# include <sys/ioctl.h>
#endif
#if NEED_PTEM_H
#include <sys/stream.h>
#include <sys/ptem.h>
#endif
#include <curses.h>
#include <dump_entry.h>
#include <transform.h>
MODULE_ID("$Id: tset.c,v 1.1.1.1 2001/11/29 20:40:59 jevans Exp $")
extern char **environ;
#undef CTRL
#define CTRL(x) ((x) & 0x1f)
const char *_nc_progname = "tset";
static TTY mode, oldmode, original;
static bool can_restore = FALSE;
static bool isreset = FALSE;
static int terasechar = -1;
static int intrchar = -1;
static int tkillchar = -1;
static int tlines, tcolumns;
#define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c))
static int
CaselessCmp(const char *a, const char *b)
{
while (*a && *b) {
int cmp = LOWERCASE(*a) - LOWERCASE(*b);
if (cmp != 0)
break;
a++, b++;
}
return LOWERCASE(*a) - LOWERCASE(*b);
}
static void
exit_error(void)
{
if (can_restore)
SET_TTY(STDERR_FILENO, &original);
(void) fprintf(stderr, "\n");
fflush(stderr);
exit(EXIT_FAILURE);
}
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);
exit_error();
}
static void
failed(const char *msg)
{
char temp[BUFSIZ];
perror(strncat(strcpy(temp, "tset: "), msg, sizeof(temp) - 10));
exit_error();
}
static void
cat(char *file)
{
FILE *fp;
size_t nr;
char buf[BUFSIZ];
if ((fp = fopen(file, "r")) == 0)
failed(file);
while ((nr = fread(buf, sizeof(char), sizeof(buf), fp)) != 0)
if (fwrite(buf, sizeof(char), nr, stderr) != nr)
failed("write to stderr");
fclose(fp);
}
static int
outc(int c)
{
return putc(c, stderr);
}
static const char *
askuser(const char *dflt)
{
static char answer[256];
char *p;
if (feof(stdin) || ferror(stdin)) {
(void) fprintf(stderr, "\n");
exit_error();
}
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) {
exit_error();
}
return (dflt);
}
if ((p = strchr(answer, '\n')) != 0)
*p = '\0';
if (answer[0])
return (answer);
if (dflt != 0)
return (dflt);
}
}
#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;
const char *porttype;
const char *type;
int conditional;
int speed;
} 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},
#ifdef B19200
{"19200", B19200},
#endif
#ifdef B38400
{"38400", B38400},
#endif
#ifdef B19200
{"19200", B19200},
#endif
#ifdef B38400
{"38400", B38400},
#endif
#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;
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);
}
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) {
mapp->type = mapp->porttype;
mapp->porttype = 0;
goto done;
}
if (arg == mapp->porttype)
termp = mapp->porttype = 0;
else
termp = base = arg;
for (;; ++arg) {
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 '=':
mapp->conditional |= EQ;
break;
case '!':
mapp->conditional |= NOT;
break;
default:
goto next;
}
}
next:
if (*arg == ':') {
if (mapp->conditional)
goto badmopt;
++arg;
} else {
arg = strchr(p = arg, ':');
if (arg == 0)
goto badmopt;
*arg++ = '\0';
mapp->speed = tbaudrate(p);
}
if (arg == (char *) 0)
goto badmopt;
mapp->type = arg;
if (termp != 0)
*base = '\0';
if (mapp->conditional & NOT)
mapp->conditional = ~mapp->conditional & (EQ | GT | LT);
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
}
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:
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);
}
return (type);
}
static const char *
get_termcap_entry(char *userarg)
{
int errret;
char *p;
const char *ttype;
#if HAVE_GETTTYNAM
struct ttyent *t;
#else
FILE *fp;
#endif
char *ttypath;
if (userarg) {
ttype = userarg;
goto found;
}
if ((ttype = getenv("TERM")) != 0)
goto map;
if ((ttypath = ttyname(STDERR_FILENO)) != 0) {
p = _nc_basename(ttypath);
#if HAVE_GETTTYNAM
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(UChar(*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
}
ttype = "unknown";
map:ttype = mapped(ttype);
found:if ((p = getenv("TERMCAP")) != 0 && *p != '/') {
int n;
for (n = 0; environ[n] != 0; n++) {
if (!strncmp("TERMCAP=", environ[n], 8)) {
while ((environ[n] = environ[n + 1]) != 0) {
n++;
}
break;
}
}
}
if (ttype[0] == '?') {
if (ttype[1] != '\0')
ttype = askuser(ttype + 1);
else
ttype = askuser(0);
}
while (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");
#endif
return (ttype);
}
#undef CEOF
#undef CERASE
#undef CINTR
#undef CKILL
#undef CLNEXT
#undef CRPRNT
#undef CQUIT
#undef CSTART
#undef CSTOP
#undef CSUSP
#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);
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
SET_TTY(STDERR_FILENO, &mode);
}
#ifdef TERMIOS
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;
}
#endif
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
}
static void
set_conversions(void)
{
#ifdef __OBSOLETE__
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
#ifdef TERMIOS
#ifdef ONLCR
mode.c_oflag |= ONLCR;
#endif
mode.c_iflag |= ICRNL;
mode.c_lflag |= ECHO;
#ifdef OXTABS
mode.c_oflag |= OXTABS;
#endif
if (newline != (char *) 0 && newline[0] == '\n' && !newline[1]) {
#ifdef ONLCR
mode.c_oflag &= ~ONLCR;
#endif
mode.c_iflag &= ~ICRNL;
}
#ifdef __OBSOLETE__
if (tgetflag("HD"))
mode.c_lflag &= ~ECHO;
#endif
#ifdef OXTABS
if (has_hardware_tabs)
mode.c_oflag &= ~OXTABS;
#endif
mode.c_lflag |= (ECHOE | ECHOK);
#endif
}
static void
set_init(void)
{
char *p;
bool settle;
#ifdef __OBSOLETE__
if (pad_char != (char *) 0)
PC = pad_char[0];
#endif
#ifdef TAB3
if (oldmode.c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) {
oldmode.c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET);
SET_TTY(STDERR_FILENO, &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;
}
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);
}
}
static bool
set_tabs()
{
if (set_tab && clear_all_tabs) {
int c;
(void) putc('\r', stderr);
tputs(clear_all_tabs, 0, outc);
for (c = 8; c < tcolumns; c += 8) {
(void) fputs(" ", stderr);
tputs(set_tab, 0, outc);
}
putc('\r', stderr);
return (TRUE);
}
return (FALSE);
}
#ifdef TERMIOS
static void
report(const char *name, int which, unsigned def)
{
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");
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
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 [-IQVrs] [-] [-e ch] [-i ch] [-k ch] [-m mapping] [terminal]", pname);
exit_error();
}
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;
if (GET_TTY(STDERR_FILENO, &mode) < 0)
failed("standard error");
can_restore = TRUE;
original = oldmode = mode;
#ifdef TERMIOS
ospeed = cfgetospeed(&mode);
#else
ospeed = mode.sg_ospeed;
#endif
p = _nc_rootname(*argv);
if (!strcmp(p, PROG_RESET)) {
isreset = TRUE;
reset_mode();
}
obsolete(argv);
noinit = noset = quiet = Sflag = sflag = showterm = 0;
while ((ch = getopt(argc, argv, "a:d:e:Ii:k:m:np:qQSrsV")) != EOF) {
switch (ch) {
case 'q':
noset = 1;
break;
case 'a':
add_mapping("arpanet", optarg);
break;
case 'd':
add_mapping("dialup", optarg);
break;
case 'e':
terasechar = arg_to_char();
break;
case 'I':
noinit = 1;
break;
case 'i':
intrchar = arg_to_char();
break;
case 'k':
tkillchar = arg_to_char();
break;
case 'm':
add_mapping(0, optarg);
break;
case 'n':
break;
case 'p':
add_mapping("plugboard", optarg);
break;
case 'Q':
quiet = 1;
break;
case 'S':
Sflag = 1;
break;
case 'r':
showterm = 1;
break;
case 's':
sflag = 1;
break;
case 'V':
puts(curses_version());
return EXIT_SUCCESS;
case '?':
default:
usage(*argv);
}
}
argc -= optind;
argv += optind;
if (argc > 1)
usage(*argv);
ttype = get_termcap_entry(*argv);
if (!noset) {
tcolumns = columns;
tlines = lines;
#if defined(TIOCGWINSZ) && defined(TIOCSWINSZ)
(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();
if (memcmp(&mode, &oldmode, sizeof(mode))) {
SET_TTY(STDERR_FILENO, &mode);
}
}
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);
#ifdef TERMIOS
if (!quiet) {
report("Erase", VERASE, CERASE);
report("Kill", VKILL, CINTR);
report("Interrupt", VINTR, CKILL);
}
#endif
}
if (Sflag)
err("The -S option is not supported under terminfo.");
if (sflag) {
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);
}
return EXIT_SUCCESS;
}