|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T q
Length: 16314 (0x3fba)
Types: TextFile
Names: »qterm.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
└─⟦this⟧ »EUUGD11/euug-87hel/sec1/qterm/qterm.c«
#ifndef lint
static char *RCSid = "$Header: qterm.c,v 1.23 87/01/09 13:55:44 mcooper Locked $";
#endif
/*
*------------------------------------------------------------------
*
* $Source: /big/src/usc/bin/qterm/RCS/qterm.c,v $
* $Revision: 1.23 $
* $Date: 87/01/09 13:55:44 $
* $State: Exp $
* $Author: mcooper $
* $Locker: mcooper $
*
*------------------------------------------------------------------
*
* Michael A. Cooper
* University Computing Services,
* University of Southern California
* (mcooper@usc-oberon.arpa)
*
*------------------------------------------------------------------
* $Log: qterm.c,v $
* Revision 1.23 87/01/09 13:55:44 mcooper
* Fixed bug with -s option that caused "ers:"
* to be printed instead of the string
* received from the terminal.
*
* Revision 1.22 86/12/11 15:57:16 mcooper
* Should now work under System V thanks to Brian L. Matthews
* (cxsea!blm).
*
* Revision 1.21 86/10/13 12:52:54 mcooper
* Fixed bug that caused problems with
* send strings not being sent from .qterm
* files.
*
* Revision 1.20 86/08/25 15:45:58 mcooper
* BUG FIX: When the -f flag was specified and the user's
* tables did not produce the terminal's entry,
* the internal terminal tables where not tried
* as documented.
*
* Revision 1.19 86/08/12 15:31:22 mcooper
* Fixed bug that caused terminals to wedge due
* to qterm failing to match receive strings
* from the .qterm file.
*
* Revision 1.18 86/08/11 13:49:42 mcooper
* Fixed bug that caused qterm to wedge. Problem
* due to alarms not being set correctly.
*
* Revision 1.17 86/08/08 14:40:09 mcooper
* - Only send/listen for strings if the previously sent string
* is not the same as the current string.
* - Fixed -s option.
*
* Revision 1.16 86/08/08 13:16:05 mcooper
* Major re-write: Added ~/.qterm file that contains
* the users own copy of terminal tables.
*
* Revision 1.15 86/07/21 12:35:54 mcooper
* Now works under System V (Define USG5).
*
* Revision 1.14 86/07/01 22:57:45 mcooper
* Moved terminal table to seperate
* file (table.c).
*
* Revision 1.13 86/06/30 11:17:53 mcooper
* More terminals to main table...
*
* Revision 1.12 86/06/19 13:57:51 mcooper
* Added responses for concept from a Pro running
* 2.9bsd.
*
* Revision 1.11 86/06/18 15:58:45 mcooper
* Cleanup for release.
*
* Revision 1.10 86/06/17 23:06:55 mcooper
* Added Unix PC responses.
*
* Revision 1.9 86/06/16 14:19:09 mcooper
* Added vt100 responses from vt100 manual.
*
* Revision 1.8 86/06/16 13:23:40 mcooper
* Print additional information about
* what the actual terminal is.
*
* Revision 1.7 86/06/12 10:59:27 mcooper
* *** empty log message ***
*
* Revision 1.6 86/06/11 19:48:35 mcooper
* Added alternate string and table entries for concepts.
*
* Revision 1.5 86/05/19 12:30:32 mcooper
* General clean up.
*
* Revision 1.4 86/05/18 17:56:11 mcooper
* Added another vt100. This one is for when you rlogin
* from a Pro 2.9bsd host on a HDS Concept.
*
* Revision 1.3 86/05/08 09:24:13 mcooper
* Added another vt100 description.
*
* Revision 1.2 86/05/06 18:23:35 mcooper
* More cleanup - de-linted (almost).
*
* Revision 1.1 86/05/06 14:56:57 mcooper
* Initial revision
*
*------------------------------------------------------------------
*/
/*
* [Edit with tabstop=4]
*
* qterm - Query Terminal
*
* qterm is used to query a terminal to determine the name of the terminal.
* This is done by sending a fairly universal string "\33Z" to the terminal,
* reading in a response, and comparing it against a master table of responses
* and names. The "name" printed to standard output should be one found in
* the termcap(5) database.
*
* Putting a line in your ".login" file such as:
*
* setenv TERM `qterm`
*
* or the following lines in your ".profile" file:
*
* TERM=`qterm`
* export TERM
*
* will set your terminal type automagically.
*
* If you add a terminal to the master table, please also send me a copy
* so that I may put it into my version.
*
* Michael Cooper
* ARPA: mcooper@usc-oberon.ARPA
* UUCP: mcooper@usc-oberon.UUCP
* BITNET: mcooper@uscvaxq
*/
#include <stdio.h>
#include <pwd.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <setjmp.h>
#ifdef USG5
# include <termio.h>
#else
# include <sys/file.h>
# include <sgtty.h>
#endif
#define SEND "\033Z" /* send this to query terminal */
#define ALTSEND "\033[c" /* alternate string */
#define STRFILE ".qterm" /* file containing terminal strings */
#define dprintf if(debug)printf
#define MAXTERMS 100
#define TRUE 1
#define FALSE 0
#define T_STR 0
#define T_NAME 1
#define T_LNAME 2
int tabmark = 0;
int tabtotal = 0;
int has_set = 0;
#define BUF 666
struct qt {
char qt_sendstr[BUFSIZ]; /* String to send to terminal */
char qt_recvstr[BUFSIZ]; /* String expected in response */
char qt_termname[BUFSIZ]; /* Terminal name */
char qt_fullname[BUFSIZ]; /* Full terminal name & description */
};
struct qt *compare();
static struct qt termtab[MAXTERMS];
#ifdef USG5
struct termio _ntty, _otty;
#else
struct sgttyb _tty;
#endif
int _tty_ch = 2;
#ifdef USG5
# define crmode() (_ntty.c_lflag &= ~ICANON,\
_ntty.c_cc[VMIN] = 1, _ntty.c_cc[VTIME] = 0,\
ioctl(_tty_ch, TCSETAF, &_ntty))
# define nocrmode() (_ntty.c_lflag |= ICANON,\
_ntty.c_cc[VMIN] = _otty.c_cc[VMIN],\
_ntty.c_cc[VTIME] = _otty.c_cc[VTIME],\
ioctl(_tty_ch, TCSETAF, &_ntty))
# define echo() (_ntty.c_lflag |= ECHO,\
ioctl(_tty_ch, TCSETAF, &_ntty))
# define noecho() (_ntty.c_lflag &= ~ECHO,\
ioctl(_tty_ch, TCSETAF, &_ntty))
#else
# define crmode() (_tty.sg_flags |= CBREAK,\
ioctl(_tty_ch, TIOCSETP, &_tty))
# define nocrmode() (_tty.sg_flags &= ~CBREAK,\
ioctl(_tty_ch, TIOCSETP, &_tty))
# define echo() (_tty.sg_flags |= ECHO, \
ioctl(_tty_ch, TIOCSETP, &_tty))
# define noecho() (_tty.sg_flags &= ~ECHO, \
ioctl(_tty_ch, TIOCSETP, &_tty))
#endif
#define SIZE 512
#define CMASK 0377
#define ESC '\033'
static char recvbuf[SIZE];
static char *progname;
int debug; /* debug mode */
int aflag; /* alternate string */
int sflag; /* print strings */
int qflag; /* quiet mode */
int fflag; /* use strings file */
int found = FALSE;
int index = 0;
int finish(), wakeup(), done();
char *decode();
jmp_buf env;
main(argc, argv)
char *argv[];
{
register int x;
progname = argv[0];
for (x = 1; x < argc; x++) {
if (argv[x][0] != '-')
break;
switch (argv[x][1]) {
case 'a':
aflag = TRUE;
break;
case 't':
case 's':
sflag = TRUE;
break;
case 'q':
qflag = TRUE;
break;
case 'f':
fflag = TRUE;
break;
case 'd':
debug = TRUE;
break;
default:
usage();
exit(1);
}
}
setbuf(stdout, 0);
if(debug)
setbuf(stderr, 0);
dprintf("[ %s debug mode enabled ]\n\n", progname);
if(!isatty(0))
fprintf(stderr,"Not a tty.\n");
#ifdef USG5
if(ioctl(_tty_ch, TCGETA, &_otty) < 0)
#else
if(ioctl(_tty_ch, TIOCGETP, &_tty) < 0)
#endif
{
perror("gtty");
exit(1);
}
#ifdef USG5
_ntty = _otty;
#endif
if(crmode() < 0) {
perror("crmode");
exit(1);
}
if(noecho() < 0) {
perror("noecho");
exit(1);
}
dprintf("[ initilizing term table... ]\n");
mktable();
dprintf("[ table done ]\n");
if(!fflag) {
dointernal();
} else
dprintf("!fflag. not doing dointernal().\n");
index = tabmark;
dprintf("main: we'll do a dotab()\n");
dotab();
dprintf("main: dotab done\n");
putc('\r', stderr);
(void) nocrmode();
(void) echo();
if(!found) {
dprintf("end of main\n");
notrecognized();
}
}
done()
{
putc('\r', stderr);
(void) nocrmode();
(void) echo();
exit(0);
}
/*
* finish - clean things up.
*/
finish()
{
dprintf("finish called\n");
putc('\r', stderr);
(void) nocrmode();
(void) echo();
if(recvbuf[0] != NULL)
(void) prinfo(compare(recvbuf, 0, tabtotal), 1);
dprintf("finish done\n");
if(!found)
notrecognized();
exit(0);
}
prinfo(t, what)
struct qt *t;
int what;
{
int len = 0;
int st = FALSE;
dprintf("prinfo startup\n");
if((t->qt_termname[0] != NULL) && (recvbuf[0] != NULL)) {
if(debug || sflag) {
len = strlen(recvbuf);
fprintf(stderr, "%s receives %d character%s:",
progname, len, (len == 1) ? "" : "s");
fprintf(stderr, " %s\n", decode(recvbuf));
}
if(!qflag)
if(t->qt_fullname[0] != NULL)
fprintf(stderr, "Terminal recognized as %s (%s)\n",
t->qt_termname, t->qt_fullname);
else
fprintf(stderr, "Terminal recognized as %s\n",
t->qt_termname);
printf("%s\n", t->qt_termname);
found = TRUE;
done();
/*NOTREACHED*/
} else {
found = FALSE;
if(what) {
dprintf("prinfo(): doing notrecognized()\n");
notrecognized();
done();
/*NOTREACHED*/
}
}
dprintf("prinfo done\n");
return(st);
}
/*
* compare - actually compare what we received against the table.
*/
struct qt *
compare(str, start, stop)
char *str;
int start;
int stop;
{
register int i = 0;
int len;
dprintf("compare(%s, %d, %d) startup.\n", decode(str), start, stop);
alarm(0);
i = start;
while(i <= stop) {
dprintf("compare(): tr = '%s'\n", decode(termtab[i].qt_recvstr));
if(strncmp(str, termtab[i].qt_recvstr,
strlen(termtab[i].qt_recvstr)) == 0) {
found = TRUE;
return(&termtab[i]);
}
++i;
}
found = FALSE;
}
/*
* getch - read in a character at a time.
*/
getch()
{
char c;
(void) read(0, &c, 1);
return(c & CMASK);
}
/*
* decode - print str in a readable fashion
*/
char *
decode(str)
char *str;
{
char buf[BUFSIZ];
char tmp[10];
strcpy(buf, "");
while(*str) {
if (*str == ESC) {
strcat(buf, "<esc> ");
} else if((*str <= 33) || (*str >= 127)) {
sprintf(tmp,"\\%o ", *str);
strcat(buf, tmp);
} else {
sprintf(tmp,"%c ", *str);
strcat(buf, tmp);
}
*++str;
}
return(buf);
}
usage()
{
fprintf(stderr, "usage: %s [ -asq ]\n", progname);
}
mktable()
{
register int i, z;
FILE *fd, *fopen();
char file[BUFSIZ];
char buf[BUFSIZ];
char lbuf[4][BUFSIZ];
char *home, *msg, *fixctl();
int iserr = 0;
extern char *terms[];
struct passwd *pwd;
i = z = 0;
/*
* Copy internal table
*/
while(terms[z] != NULL && i < MAXTERMS) {
(void) strcpy(termtab[i].qt_sendstr, (aflag) ? ALTSEND : SEND);
(void) strcpy(termtab[i].qt_recvstr, terms[z + T_STR]);
(void) strcpy(termtab[i].qt_termname, terms[z + T_NAME]);
(void) strcpy(termtab[i].qt_fullname, terms[z + T_LNAME]);
z += 3;
++i;
}
tabmark = i;
/*
* Try and read the user's own table
*/
if((home = (char *) getenv("HOME")) == NULL) {
if((pwd = (struct passwd *) getpwuid(getuid())) == NULL) {
fprintf(stderr, "Who the hell are you????\n");
exit(1);
}
home = pwd->pw_dir;
}
dprintf("home = '%s'\n", home);
sprintf(file, "%s/%s", home, STRFILE);
dprintf("strfile = '%s'\n", file);
if(fflag && (fd = fopen(file, "r")) != NULL) {
while(fgets(buf, sizeof(buf), fd) && i < MAXTERMS) {
if(buf[0] == '#' || buf[0] == '\n')
continue;
lbuf[0][0] = NULL;
lbuf[1][0] = NULL;
lbuf[2][0] = NULL;
lbuf[3][0] = NULL;
(void) sscanf(buf, "%s%s%s\t%[^\n]",
lbuf[0], lbuf[1], lbuf[2], lbuf[3]);
if(lbuf[0][0] == NULL)
continue;
if(lbuf[1][0] == NULL) {
iserr = TRUE;
msg = "receive string";
}
if(lbuf[2][0] == NULL) {
iserr = TRUE;
msg = "terminal name";
}
if(iserr) {
fprintf(stderr, "%s: Error parsing %s.\n", file, msg);
exit(1);
}
(void) strcpy(termtab[i].qt_sendstr, fixctl(lbuf[0]));
(void) strcpy(termtab[i].qt_recvstr, fixctl(lbuf[1]));
(void) strcpy(termtab[i].qt_termname, lbuf[2]);
(void) strcpy(termtab[i].qt_fullname, lbuf[3]);
dprintf("entry %d:\n", i);
dprintf("qt_sendstr = %s\n", decode(termtab[i].qt_sendstr));
dprintf("qt_recvstr = %s\n", decode(termtab[i].qt_recvstr));
dprintf("qt_termname = '%s'\n", termtab[i].qt_termname);
dprintf("qt_fullname = '%s'\n", termtab[i].qt_fullname);
++i;
}
}
tabtotal = i;
dprintf("termtab total = %d\n", tabtotal);
dprintf("termtab mark = %d\n", tabmark);
}
listen(q)
struct qt *q;
{
register int i;
register char c;
char end, begin;
dprintf("listen startup\n");
alarm(0);
dprintf("listen: listening for '%s'\n", decode(q->qt_recvstr));
if (q->qt_recvstr[0] == NULL) {
begin = ESC;
end = 'c';
} else {
begin = q->qt_recvstr[0];
end = q->qt_recvstr[strlen(q->qt_recvstr)-1];
}
dprintf("listen: read initial character...\n");
if(setjmp(env)) {
dprintf("listen: setjmp TRUE\n");
if(found)
done();
++index;
(void) fflush(stdin);
dprintf("listen: dotab()\n");
dotab();
} else {
dprintf("listen: setjmp FALSE...set alarm\n");
signal(SIGALRM, wakeup);
alarm(3);
dprintf("listen: read char\n");
recvbuf[0] = getch();
alarm(0);
dprintf("recvbuf[0] = '\\%o'\n", recvbuf[0]);
}
i = 0;
if(recvbuf[0] == begin) {
dprintf("listen begin\n");
while(c != end) {
if(setjmp(env)) {
dprintf("listen: setjmp (2) return\n");
return;
} else {
signal(SIGALRM, wakeup);
alarm(2);
dprintf("listen: read (2) char\n");
c = getch();
alarm(0);
dprintf("recvbuf[0] = '\\%o'\n", recvbuf[0]);
}
recvbuf[++i] = c;
}
if(debug)
fprintf(stderr,"\n[ Received terminator. ]\n");
} else {
dprintf("listen: Not Recognized. exiting...\n");
notrecognized();
putc('\r',stderr);
(void) nocrmode();
(void) echo();
exit(1);
}
dprintf("listen done\n");
}
notrecognized()
{
if(!qflag)
fprintf(stderr,
"Terminal NOT recognized - defaults to \"dumb\".\n");
puts("dumb");
}
wakeup()
{
dprintf("wakeup called\n");
longjmp(env, 1);
dprintf("wakeUP: done\n");
}
dotab()
{
int wakeup();
int st = FALSE;
static int firsttime = TRUE;
dprintf("dotab startup\n");
dprintf("index = %d\n", index);
if(index > tabtotal) {
/*
* if we haven't reset things yet, do so.
* now try the internal tables if the user's
* tables failed.
*/
if(!has_set) {
tabtotal = tabmark;
index = 0;
has_set = 1;
dprintf("dotab(): has_set now true.\n");
dotab();
}
dprintf("dotab(): index > tabtotal\n");
finish();
}
if(!found || fflag) {
while(!found && termtab[index].qt_sendstr[0] != NULL && !st) {
dprintf("dotab: termtab PASS %d\n", index);
dprintf("dotab: sending str %s\n",
decode(termtab[index].qt_sendstr));
(void) fflush(stdin);
if(firsttime || strncmp(termtab[index].qt_sendstr,
termtab[index-1].qt_sendstr,
strlen(termtab[index].qt_sendstr))) {
firsttime = FALSE;
dprintf("dotab(): sendstr's didn't match.\n");
dprintf("dotab: str1 %s\n",
decode(termtab[index].qt_sendstr));
dprintf("dotab: str2 %s\n",
decode(termtab[index-1].qt_sendstr));
fprintf(stderr, termtab[index].qt_sendstr);
(void) fflush(stdout);
(void) fflush(stderr);
(void) listen(&termtab[index]);
} else {
dprintf("dotab(): sendstr's DID match. No str sent.\n");
dprintf("dotab: str1 %s\n",
decode(termtab[index].qt_sendstr));
dprintf("dotab: str2 %s\n",
decode(termtab[index-1].qt_sendstr));
}
firsttime = FALSE;
dprintf("dotab(): recbuf = '%s'\n", decode(recvbuf));
dprintf("dotab(): qt_rec = '%s'\n",
decode(termtab[index].qt_recvstr));
st = prinfo(compare(recvbuf, tabmark, tabtotal), !fflag);
dprintf("st = %d\n", st);
++index;
}
dprintf("dotab(): mark 1\n");
}
dprintf("i'm here (2)\n");
if(!found) {
dprintf("end of dotab\n");
dointernal();
if(!found) {
dprintf("dotab: dointernal failed.\n");
notrecognized();
}
}
done();
}
dointernal()
{
struct qt q;
dprintf("DOINTERNAL startup.\n");
(void) fflush(stdin);
fprintf(stderr, (aflag) ? ALTSEND : SEND);
(void) fflush(stdout);
(void) fflush(stderr);
q.qt_recvstr[0] = NULL;
(void) listen(&q);
(void) prinfo(compare(recvbuf, 0, tabmark), 1);
if(found)
done();
dprintf("dointernal end.\n");
}
char *
fixctl(str)
char *str;
{
register int i;
char buf[BUFSIZ];
i = 0;
while(*str) {
if(*str == '^')
buf[i++] = *++str & 037;
else
buf[i++] = *str;
*++str;
}
buf[i] = NULL;
return(buf);
}