rsalz@uunet.UU.NET (Rich Salz) (10/20/87)
Submitted-by: Craig Bishop <charlie.oz.au!craig> Posting-number: Volume 12, Issue 25 Archive-name: qterm.alt [ This program sends an Escape sequence to your terminal, and interprets the result to see what kind of terminal you have. It's table-driven, so adding new terminals is easy. It is a rewrite of the qterm program published in Volume 10. --r$ ] # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # This archive contains: # README Makefile qtermtab gstty.h qterm.h qterm.c query.c table.c echo x - README cat > "README" << '//E*O*F README//' This is a rewrite of the qterm program which came across the net in comp.sources.unix the title was v10i072: Query terminal for its type. I decided to use the program before looking at the code. Eventually I decided to rewrite it. It is completely compatible with the original so if you are already using it I suggest you change to this one. There are couple more flags added to the program which are useful when testing a terminals responses to the various standard query sequences. Qterm is particularly valuable in port selector and terminal server environments. Most terminals can respond to the standard ANSI query sequences, others which cannot will often do answerback. If you find any bugs please report them to me. Craig Bishop ACSNET: craig@charlie.oz ARPA: craig%charlie.oz.au@uunet.uu.net UUCP: ...!uunet!munnari!charlie.oz!craig //E*O*F README// echo x - Makefile cat > "Makefile" << '//E*O*F Makefile//' # # Makefile for qterm. # # If this is a USG system then add the flag -DUSG to the # CFLAGS variable. # DESTDIR = /usr/local/bin CFLAGS = -O -DTABLEFILE=\"$(TABLEDIR)/$(TABLE)\" SOURCES = qterm.c query.c table.c OBJECTS = qterm.o query.o table.o HDRS = gstty.h qterm.h BINARY = qterm LIBS = MAN = qterm.1 MANDIR = /usr/local/man/man1 TABLE = qtermtab TABLEDIR = /usr/local/lib .DEFAULT:; co -q $< all: $(BINARY) $(BINARY): $(OBJECTS) cc -o $(BINARY) $(OBJECTS) $(LIBS) $(OBJECTS): $(HDRS) install:; cp $(BINARY) $(DESTDIR) chown bin $(DESTDIR)/$(BINARY) chmod 751 $(DESTDIR)/$(BINARY) cp $(MAN) $(MANDIR) chown man $(MANDIR)/$(MAN) chmod 644 $(MANDIR)/$(MAN) install_table:; cp $(TABLE) $(TABLEDIR) chown lib $(TABLEDIR)/$(TABLE) chmod 644 $(TABLEDIR)/$(TABLE) clean:; rm -f $(BINARY) $(OBJECTS) print:; pr Makefile $(HDRS) $(SOURCES) | lpr //E*O*F Makefile// echo x - qtermtab cat > "qtermtab" << '//E*O*F qtermtab//' # #SendStr ReceiveStr TermName FullTermName # ^[Z ^[iBO h29 Zenith z29 in zenith mode ^[Z ^[/K h29 Zenith z29 in zenith mode ^[Z ^[[?1;0c vt100 Base vt100 ^[Z ^[[?1;1c vt100 vt100 with STP ^[Z ^[[?1;2c vt100 ANSI/VT100 Clone ^[Z ^[[?1;3c vt100 vt100 with AVO and STP ^[Z ^[[?1;4c vt100 vt100 with GPO ^[Z ^[[?1;5c vt100 vt100 with GPO and STP ^[Z ^[[?1;6c vt100 vt100 with GPO and AVO ^[Z ^[[?1;7c vt100 vt100 with GPO, STP, and AVO ^[Z ^[[?6c vt100 Generic vt100 ^[Z ^[[?8c vt100 TeleVideo 970 ^[Z ^[[0n vt100 AT&T Unix PC 7300 ^[Z ^[[?l;0c vt100 AT&T Unix PC 7300 ^[Z ^[[?12c vt100 Concept from Pro 350/UNIX ^[Z ^[[?;c vt100 Concept From Pro 350/UNIX ^[Z ^[[=1;1c avt-4p-s Concept with 4 pages memory ^[Z ^[[=1;2c avt-8p-s Concept with 8 pages memory ^[Z ^[/Z vt52 Generic vt52 ^[Z ^[[?10c la120 DEC Writer III ^[Z ^[[?1;11c cit101e CIE CIT-101 Enhanced w/Graphics #^[Z ^[[?1;11c xt100+ Northern Tech LANPARSCOPE ^[Z ^[[?12;7;0;102c vt125 DEC Pro 350 in vt125 mode ^[Z ^[[?62;1;2;6;7;8;9c vt220 DEC VT220 ^[Z ^[[?62;1;4;6;7;8;9;15c vt200-sb Microvax II VMS ^[Z ^[[62;1;2;6;8c f220 Freedom 220 DEC clone ^[Z ^[[?63;1;2;6;7;8c tvi9220 TeleVideo 9220 //E*O*F qtermtab// echo x - gstty.h cat > "gstty.h" << '//E*O*F gstty.h//' /* ** gstty.h - defines and declarations for the getting and setting ** of terminal parameters on BSD and USG systems. ** ** Author: Craig Bishop ** loosly based on original program by Michael Cooper. ** ** $Header: gstty.h,v 1.1 87/09/30 15:25:46 craig Exp $ */ #include <sys/ioctl.h> #ifdef USG #include <termio.h> struct termio otty; struct termio ntty; #define setterm() (\ (void)ioctl(0, TCGETA, &otty),\ ntty = otty,\ ntty.c_lflag &= ~ICANON,\ ntty.c_lflag &= ~ECHO,\ ntty.c_cc[VMIN] = 1,\ ntty.c_cc[VTIME] = 0,\ (void)ioctl(0, TCSETAF, &ntty)\ ) #define fixterm() (void)ioctl(0, TCSETAF, &otty) #else struct sgttyb otty; struct sgttyb ntty; #define setterm() (\ (void)ioctl(0, TIOCGETP, &otty),\ ntty = otty,\ ntty.sg_flags |= CBREAK,\ ntty.sg_flags &= ~ECHO,\ (void)ioctl(0, TIOCSETP, &ntty)\ ) #define fixterm() (void)ioctl(0, TIOCSETP, &otty) #endif //E*O*F gstty.h// echo x - qterm.h cat > "qterm.h" << '//E*O*F qterm.h//' /* ** qterm.h - defines and declarations for the qterm modules. ** ** Author: Craig Bishop ** loosly based on original program by Michael Cooper. ** ** $Header: qterm.h,v 1.1 87/09/30 15:25:52 craig Exp $ */ /* defines for qterm */ #define ABSEND "\05" /* Answerback string */ #define ALTSEND "\033[c" /* Alternative string */ #define DFLTSEND "\033Z" /* Default string */ #define CMASK 0177 /* Character mask */ #define ESC '\033' /* Escape in octal */ #define MAXTERMS 100 /* Maximum # of terminals for qterm */ #define STRFILE ".qterm" /* File containing terminal strings */ /* ** This is the number of seconds for alarm timeouts. Some slower systems ** may need to increase this value to 2 or maybe 3 seconds. This value ** effects how long qterm will run, because for each query string sent ** the program must wait at least this long. */ #define WAIT 1 #define TRUE 1 #define FALSE 0 #define NULLSTR (char *) NULL #define NULLQT (struct qt *) NULL #define STREQUAL (0) #define FIELDSIZ 30 #define FULLSIZ 80 /* define the query terminal structure */ struct qt { char sendstr[FIELDSIZ]; /* String to send to terminal */ char recvstr[FIELDSIZ]; /* String expected in response */ char termname[FIELDSIZ]; /* Terminal name */ char fullname[FULLSIZ]; /* Full terminal name & description */ }; /* declare externals */ extern int Fflag; extern int fflag; extern int qflag; extern int sflag; extern char querystr[]; extern char recvbuf[]; extern char *name; extern struct qt *termtab[]; /* declare procedures */ int mktable(), readtabfile(); char getch(); char *decode(), *fixctl(), *getenv(), *lalloc(), *listen(), *malloc(); void catchint(), dumb(), prinfo(), wakeup(); FILE *fopen(); struct qt *compare(), *dotab(); //E*O*F qterm.h// echo x - qterm.c cat > "qterm.c" << '//E*O*F qterm.c//' /* ** qterm.c - Qterm is used to query a terminal to determine the name ** of the terminal. This is done by sending a equiry string ** to the terminal and reading in a response. ** ** Author: Craig Bishop ** loosly based on original program by Michael Cooper. */ static char rcsid[] = "$Header: qterm.c,v 1.1 87/09/30 15:25:55 craig Exp $"; #include <stdio.h> #include <signal.h> #include "gstty.h" #include "qterm.h" /* declare flags */ int fflag = FALSE; /* use user's own .qterm file */ int Fflag = FALSE; /* same as above, but don't add our own table */ int qflag = FALSE; /* quiet mode */ int sflag = FALSE; /* print strings */ /* declare global variables */ char decodebuf[BUFSIZ]; char querystr[FIELDSIZ]; char recvbuf[FIELDSIZ]; char *name; char usage[] = "usage: %s [ -a ] [ -d ] [ -e ] [ -f ] [ -F ] [ -q ] [ -s ]\n"; struct qt *termtab[MAXTERMS]; int main(argc, argv) int argc; char *argv[]; { register int i; register int j; struct qt *qtp; name = argv[0]; for ( i = 1; i < argc; i++ ) { if ( argv[i][0] != '-' ) { (void)fprintf(stderr, usage, name); return 1; } for ( j = 1; argv[i][j] != NULL; j++ ) switch ( argv[i][j] ) { case 'a': (void)strcpy(querystr, ALTSEND); break; case 'd': (void)strcpy(querystr, DFLTSEND); break; case 'e': (void)strcpy(querystr, ABSEND); break; case 'f': fflag = TRUE; break; case 'F': Fflag = TRUE; break; case 'q': qflag = TRUE; break; case 's': sflag = TRUE; break; default: (void)fprintf(stderr, usage, name); return 1; } } if ( mktable() == FALSE ) return 1; if ( !isatty(0) ) { (void)fprintf(stderr, "%s: Not a tty.\n", name); return 1; } setterm(); (void)signal(SIGHUP, catchint); (void)signal(SIGINT, catchint); (void)signal(SIGQUIT, catchint); (void)setbuf(stdout, 0); (void)setbuf(stderr, 0); qtp = dotab(); fixterm(); if ( qtp != NULLQT ) prinfo(qtp); else dumb(); return 0; } void catchint() { fixterm(); exit(1); } void prinfo(ttent) struct qt *ttent; { register int len; register struct qt *qtp = ttent; if ( sflag ) { len = strlen(recvbuf); (void)fprintf ( stderr, "%s receives %d character%s: %s\n", name, len, (len == 1) ? "" : "s", decode(recvbuf) ); } if ( !qflag ) if ( *qtp->fullname != NULL ) (void)fprintf ( stderr, "Terminal recognized as %s (%s)\n", qtp->termname, qtp->fullname ); else (void)fprintf ( stderr, "Terminal recognized as %s\n", qtp->termname ); (void)puts(qtp->termname); } void dumb() { register int len; if ( sflag ) { len = strlen(recvbuf); (void)fprintf ( stderr, "%s receives %d character%s", name, len, (len == 1) ? "" : "s" ); if ( len ) (void)fprintf(stderr, ": %s\n", decode(recvbuf)); else (void)fputs(".\n", stderr); } if ( !qflag ) (void)fprintf ( stderr, "Terminal NOT recognized - defaults to \"dumb\".\n" ); (void)puts("dumb"); } char * decode(str) char *str; { register char *dbp = decodebuf; register char *cp1 = str; register char *cp2; char tmp[10]; for ( *dbp = NULL, cp2 = tmp; *cp1 != NULL; cp1++ ) { if ( *cp1 == ESC ) { (void)strcat(dbp, "<esc> "); continue; } if ( (*cp1 <= 33) || (*cp1 == 127) ) { (void)sprintf(cp2, "\\%o ", *cp1); (void)strcat(dbp, cp2); continue; } (void)sprintf(cp2, "%c ", *cp1); (void)strcat(dbp, cp2); } return dbp; } //E*O*F qterm.c// echo x - query.c cat > "query.c" << '//E*O*F query.c//' /* ** query.c - send query strings to the terminal and listen for ** the answering strings. ** ** Author: Craig Bishop ** loosely based on a the original program by Michael Cooper. */ static char rcsid[] = "$Header: query.c,v 1.1 87/09/30 15:25:58 craig Exp $"; #include <stdio.h> #include <setjmp.h> #include <signal.h> #include "qterm.h" /* declare global variables */ jmp_buf env; struct qt * dotab() { static int firsttime = TRUE; register struct qt **qtpp = termtab; register struct qt *lastqtp; struct qt *term; (void) fflush(stdin); for ( ; *qtpp != NULLQT; lastqtp = *qtpp++ ) { if ( firsttime || strcmp((*qtpp)->sendstr, lastqtp->sendstr) != STREQUAL ) { firsttime = FALSE; (void)fputs((*qtpp)->sendstr, stderr); if ( listen() == NULLSTR ) continue; } else continue; if ( (term = compare(recvbuf, qtpp)) != NULLQT ) return term; } return NULLQT; } char * listen() { register int i; if ( setjmp(env) ) { (void)fflush(stdin); return recvbuf; } (void)signal(SIGALRM, wakeup); for ( i = 0; i < FIELDSIZ - 1; recvbuf[++i] = NULL ) { (void)alarm(WAIT); recvbuf[i] = getch(); (void)alarm(0); } (void)fflush(stdin); return recvbuf; } struct qt * compare(str, ttstart) char *str; struct qt **ttstart; { register char *cp1 = str; register char *cp2 = (*ttstart)->sendstr; register struct qt **qtpp = ttstart; for ( ; *qtpp != NULLQT && strcmp(cp2, (*qtpp)->sendstr) == STREQUAL; qtpp++ ) if ( strcmp(cp1, (*qtpp)->recvstr) == STREQUAL ) return *qtpp; return NULLQT; } char getch() { char c; (void)read(0, &c, 1); return (c & CMASK); } void wakeup() { longjmp(env, 1); } //E*O*F query.c// echo x - table.c cat > "table.c" << '//E*O*F table.c//' /* ** table.c - read in the terminal query table(s). ** ** Author: Craig Bishop ** loosely based on a the original program by Michael Cooper. */ static char rcsid[] = "$Header: table.c,v 1.1 87/09/30 15:26:01 craig Exp $"; #include <stdio.h> #include <pwd.h> #include "qterm.h" #define talloc(T) (T*) lalloc(sizeof(T)) /* declare global variables */ char fixbuf[FIELDSIZ + 1]; /* declare procedures */ struct passwd *getpwuid(); int mktable() { char file[BUFSIZ]; char *home; struct passwd *pwd; if ( fflag || Fflag ) { if ( (home = getenv("HOME")) == NULLSTR ) { if ( (pwd = getpwuid(getuid())) == (struct passwd *) NULL ) { (void)fprintf ( stderr, "%s: Who the hell are you????\n", name ); return FALSE; } home = pwd->pw_dir; } (void)sprintf(file, "%s/%s", home, STRFILE); if ( readtabfile(file) == FALSE ) return FALSE; } if ( !Fflag ) return readtabfile(TABLEFILE); return TRUE; } int readtabfile(file) char *file; { register int line; register char *cp; register struct qt **qtpp = termtab; char buf[BUFSIZ]; FILE *fp; if ( (fp = fopen(file, "r")) == (FILE *) NULL ) { (void)fprintf ( stderr, "%s: Cannot read input file %s.\n", name, file ); return FALSE; } for ( *qtpp = talloc(struct qt), line = 1, cp = buf; qtpp < &termtab[MAXTERMS - 1] && fgets(cp, BUFSIZ, fp); line++ ) { if ( *cp == '#' || *cp == '\n') continue; (void)sscanf ( cp, "%s%s%s\t%[^\n]", (*qtpp)->sendstr, (*qtpp)->recvstr, (*qtpp)->termname, (*qtpp)->fullname ); if ( *(*qtpp)->sendstr == NULL ) continue; if ( *(*qtpp)->recvstr == NULL || *(*qtpp)->termname == NULL ) { (void)fprintf ( stderr, "%s: Error parsing qterm file %s on line %d.\n", name, file, line ); (void)fclose(fp); return FALSE; } if ( querystr[0] != NULL ) (void)strcpy((*qtpp)->sendstr, querystr); else (void)strcpy((*qtpp)->sendstr, fixctl((*qtpp)->sendstr)); (void)strcpy((*qtpp)->recvstr, fixctl((*qtpp)->recvstr)); *++qtpp = talloc(struct qt); } (void)fclose(fp); (void)free(*qtpp); *qtpp = NULLQT; if ( qtpp == &termtab[MAXTERMS - 1] ) { (void)fprintf ( stderr, "%s: Greater than maximum terminals of %d.", name, MAXTERMS ); return FALSE; } return TRUE; } char * fixctl(str) char *str; { register char *cp1 = str; register char *cp2 = fixbuf; for ( *cp2 = NULL; *cp1 != NULL; cp1++, cp2++ ) { if ( *cp1 == '^') *cp2 = *++cp1 & 037; else *cp2 = *cp1; } *cp2 = NULL; return(fixbuf); } char * lalloc(size) int size; { register char *cp; if ( (cp = malloc((unsigned)size)) == NULLSTR ) { (void)fprintf ( stderr, "%s: No memory for malloc(%d)\n", name, size ); exit(1); } return cp; } //E*O*F table.c// exit 0