nagel@ics.uci.edu (Mark Nagel) (12/29/89)
Here is a termid query program I use all the time. It works real nice, but sometimes gets confused since many terminals seem to use arbitrary responses. Good enough though, and has a few more nice features than the one just posted. Enjoy! #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: getterm.1 Makefile getterm.c # Wrapped by nagel@wintermute.ics.uci.edu on Thu Dec 28 16:04:32 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'getterm.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'getterm.1'\" else echo shar: Extracting \"'getterm.1'\" \(1915 characters\) sed "s/^X//" >'getterm.1' <<'END_OF_FILE' X.TH GETTERM LOCAL X.SH NAME Xgetterm \- get terminal type X.SH SYNOPSIS X.B getterm X[-dfuv] [default] X.SH DESCRIPTION X.PP X.I Getterm Xprints out the terminal type of the user's terminal by using the standard Xterminal inquiry code, <ESC>Z, to obtain the terminal's response. This Xresponse is then checked against the entries in the file .gtrc in the user's Xhome directory and then against a table of builtin entries. If the .gtrc Xfile does not exist, only the builtin entries are checked. Entries in Xthe .gtrc file override builtin entries. The entries in .gtrc must be Xformatted such that the terminal name appears at the beginning of the line Xfollowed by spaces and/or tabs followed by the terminal response code. The Xresponse code may have any character in it and the program will recognize Xthe sequence %e or %E as the escape (<ESC>) character. You may place Xany comments you like at the end of each line, assuming there is at least Xone space or tab between the response code and the comment. X.PP XExample: X.PP Xvt52 %E/Z The terminal response code for vt52 terminals X.PP XSince the only value ever printed to the standard output is the final Xterminal type, X.I getterm Xis usually invoked such that its result is assigned to the terminal Xenvironment variable TERM. X.PP XIf the X.I default Xparameter is given, X.I getterm Xwill use this as the unknown terminal name if it cannot determine what Xthe name is. This parameter should be set to the name of a terminal Xthat is incapable of responding to the <ESC>Z query that you use frequently. X.SH OPTIONS X.I Getterm recognizes the following options: X.IP d Xprint out debugging information during execution. X.IP f Xforce X.I getterm Xto query the terminal even if it already knows its ID. X.IP u Xget user verification only if the type is unknown. X.IP v Xget user verification always. X.SH FILES X~/.gtrc contains alternate terminal response code entries. X.SH AUTHOR XMark D. Nagel END_OF_FILE if test 1915 -ne `wc -c <'getterm.1'`; then echo shar: \"'getterm.1'\" unpacked with wrong size! fi # end of 'getterm.1' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(1583 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X############################################################################# X# X# Makefile for getterm X# X# $Header$ X############################################################################# X# X# If you move this makefile, update the variable below X# or else depend won't work. X############################################################################# XMAKEFILE = Makefile XRCSDIR = ./RCS XCC = cc XCFILES = getterm.c XOFILES = getterm.o XMANFILE = getterm.1 XPROGRAM = getterm X############################################################################# X# Flags for Installation X############################################################################# XBINDIR = /usr/public XMANDIR = /usr/man/manp XCURSUFFIX = .1 XMANSUFFIX = .p X############################################################################# X XLIBS = XDFLAGS = -DUCI XOPTFLAGS = -O XCFLAGS = $(OPTFLAGS) $(DFLAGS) XLINTFLAGS = -bchxz X X$(PROGRAM): $(OFILES) X cc $(LDFLAGS) $(OFILES) -o $(PROGRAM) $(LIBS) X Xlint: X lint $(LINTFLAGS) $(CFILES) X Xinstall: $(PROGRAM) X install -c -m 755 $(PROGRAM) $(BINDIR) X Xinst-man: $(MANFILE) X install -m 644 $(MANFILE) $(MANDIR)/`basename $(MANFILE) $(CURSUFFIX)`$(MANSUFFIX) X Xinst-all: install inst-man X Xtags: $(CFILES) X ctags $(CFILES) > tags X Xcheckin: X @for i in $(CFILES) $(MANFILE) ; do \ X ( if test -f $$i ; then ( echo "Checking in $$i" ; ci -q $$i ) \ X ; fi ) ; done X Xcheckout: $(CFILES) $(MANFILE) X X$(CFILES) $(MANFILE): X co -l -q $@ X Xclean: X rm -f *.o *.out core X Xclean-all: checkin clean X rm -f $(PROGRAM) Xdepend: X /usr/ucb/mkdep -f $(MAKEFILE) getterm.c X END_OF_FILE if test 1583 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'getterm.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'getterm.c'\" else echo shar: Extracting \"'getterm.c'\" \(11044 characters\) sed "s/^X//" >'getterm.c' <<'END_OF_FILE' Xstatic char *header = "$Header: /cm/src_uci/usr/public/getterm/RCS/getterm.c,v 1.7 88/07/05 16:26:04 nagel Exp Locker: nagel $"; X X/* X * PROGRAM NAME: getterm X * X * AUTHOR(S): X * Originally by Dan Kleaver in 1985 (ccdank@ucdavis.ucdavis.edu) X * Rewritten by Mark Nagel in June, 1987 (nagel@ics.uci.edu) X * X * USAGE: X * getterm [-dfuv|-V] [default] X * X * DESCRIPTION: X * Produce the ID of the current terminal by sending a standard X * query string to the terminal and interpreting the response. The X * terminal type should be known by either the program or the user. The X * terminal might act strange if it does not understand <ESC>Z as the X * ID-request query string. X * X * $Log: getterm.c,v $ X * Revision 1.7 88/07/05 16:26:04 nagel X * Increased character timeout value to 200ms. X * X * Revision 1.6 88/07/05 13:58:33 nagel X * Fixed problem on sun3's (finally!) by flushing stderr after writing the X * terminal request. X * X * Revision 1.5 88/07/05 12:29:20 nagel X * Removed the entire FIONREAD setup in favor of using the select statement. X * Originally due to a problem with sun3's, but is actually much better X * in terms of busy waiting (none now) and complexity (code is much easier X * to understand now). X * X * Revision 1.4 88/07/04 19:06:59 nagel X * Fixed to make one last attempt to collect terminal response after timeout. X * X * Revision 1.3 88/07/04 18:40:04 nagel X * Added -V flag to print out version info. X * X * Revision 1.2 88/07/04 18:28:11 nagel X * Modified the method used to capture terminal response. Before, it X * would be forced to time out on terminals that didn't return a 'c' as X * the last response character. Now it just checks pending input until X * stable and then reads the whole thing in. X * X * Revision 1.1 88/07/04 16:31:02 nagel X * Initial revision X * X */ X X#include <stdio.h> X#include <sgtty.h> X#include <sys/time.h> X#include <sys/file.h> X#include <signal.h> X#include <ctype.h> X X/* define general constants and macros */ X#define MAXSTR 256 X#define TRUE 1 X#define FALSE 0 X#define ESC '\33' X#define EOS '\0' X#define lengthof(a) (sizeof(a) / sizeof(a[0])) X#define strrel(s,op,t) (strcmp((s),(t)) op 0) X X/* initial timeout and subsequent timeout values (ms) */ X#define TIMEOUT1 2000 X#define TIMEOUT2 200 X X/* customization file */ X#define GTRC_FILE ".gtrc" /* name of terminal definition file in $HOME */ X X X X/* Initialize the built-in terminal definition table */ Xstruct { X char *name, *response; X} built_in[] = { X { "z19", "\33/K"}, /* Microterm Mime-2A */ X { "vt52", "\33/Z"}, /* DEC vt52 */ X { "vt100", "\33[?1;0c"}, /* DEC vt100 */ X { "vt102", "\33[?1;2c"}, /* DEC vt102 */ X { "vt102", "\33[?1;11c"}, /* DEC vt102 */ X { "vt102", "\33[?6c"} /* DEC vt102 */ X}; X X X/* other global variables */ Xstruct sgttyb tty; /* terminal attributes structure */ Xint verify = FALSE; /* true -> verify the terminal type always */ Xint unknown = FALSE; /* true -> verify only if type is unknown */ Xint debug = FALSE; /* true -> print out debugging information */ Xchar response[MAXSTR]; /* terminal response buffer */ Xchar def_name[MAXSTR]; /* default terminal name, usually "unknown" */ XFILE *gt_fp = NULL; /* user-written definition file */ X X/* external functions used */ Xextern char *getenv(); X X X/* print a string with control characters nicely */ Xctrl_print(fp, s) XFILE *fp; Xchar *s; X{ X while (*s != EOS) { X if (isprint(*s)) { X putc(*s, fp); X } else { X putc('^', fp); X putc(*s + 64, fp); X } X s++; X } X} X X X/* open the user definition file if there is one */ Xgt_open() X{ X char *home, gt_name[MAXSTR]; X X if (gt_fp == NULL) { X if ((home = getenv("HOME")) == NULL) { X perror("gt_open"); X exit(-1); X } X sprintf(gt_name, "%s/%s", home, GTRC_FILE); X if (access(gt_name,F_OK) < 0) { X if (debug) { X fprintf(stderr,"DEBUG: no \"%s\" in %s\n", GTRC_FILE, home); X } X return(FALSE); X } X if ((gt_fp = fopen(gt_name,"r")) == NULL) { X perror("gt_open"); X exit(-1); X } X } X if (debug) { X fprintf(stderr, "DEBUG: \"%s\" found in %s and opened\n", GTRC_FILE, home); X } X return(TRUE); X} X X X/* Retrieve an entry from the user-written terminal definition file */ Xgt_entry(name, expected) Xchar *name, *expected; X{ X char td_entry[MAXSTR], *cp; X char *np, *ep; X X np = name; X ep = expected; X X /* get the next entry */ X if (fgets(td_entry, MAXSTR-1, gt_fp) == NULL) { X if (debug) { X fprintf(stderr,"DEBUG: no more entries in \"%s\"\n", GTRC_FILE); X } X fclose(gt_fp); X gt_fp = NULL; X if (debug) { X fprintf(stderr,"DEBUG: \"%s\" closed\n", GTRC_FILE); X } X return(FALSE); X } X X /* skip any leading whitespace */ X cp = td_entry; X while (isspace(*cp)) { X cp++; X } X X /* get the terminal name */ X while (*cp != ' ' && *cp != '\t') { X *np++ = *cp++; X } X *np = EOS; X if (debug) { X fprintf(stderr,"DEBUG: retrieved an entry name <%s>\n", name); X } X X /* skip to the terminal response code */ X while (*cp == ' ' || *cp == '\t') { X *cp++; X } X X /* get the expected terminal response code */ X while (!isspace(*cp)) { X if (*cp == '%') { X cp++; X switch (*cp) { X case 'e': case 'E': X *ep++ = ESC; X break; X default: X *ep = '%'; X --cp; X break; X } X } else { X *ep++ = *cp; X } X cp++; X } X *ep = EOS; X if (debug) { X fprintf(stderr,"DEBUG: retrieved it's response code <"); X ctrl_print(stderr, expected); X fprintf(stderr,">\n"); X } X X return(TRUE); X} X X X/* close the user-written terminal definition file */ Xgt_close() X{ X if (gt_fp != NULL) { X fclose(gt_fp); X if (debug) { X fprintf(stderr,"DEBUG: \"%s\" closed during search\n", GTRC_FILE); X } X } X} X X X/* this function interprets the terminal response */ Xtell_type() X{ X extern int verify; X extern int unknown; X int i; X char *type; X static char name[MAXSTR], expected[MAXSTR]; X X /* restore normal terminal state */ X reset_ttymodes(); X X if (debug) { X fprintf(stderr, "DEBUG: got terminal response <"); X ctrl_print(stderr, response); X fprintf(stderr, ">\n"); X } X X /* assume the type is unknown to begin with */ X type = def_name; /* usually "unknown" unless user wants otherwise */ X verify |= unknown; X X /* check if the user has a tailored definition for this terminal */ X if (gt_open()) { X while (gt_entry(name, expected)) { X if (strrel(expected, ==, response)) { X if (debug) { X fprintf(stderr, "DEBUG: found terminal in \"%s\"\n", GTRC_FILE); X } X type = name; X verify &= ~unknown; X if (verify) { X verify_type(type); X } else { X printf("%s\n", type); X } X gt_close(); X exit(0); X } X } X } X X /* no such luck, so try the built-in table */ X for (i = 0; i < lengthof(built_in); i++) { X if (strrel(built_in[i].response, ==, response)) { X if (debug) { X fprintf(stderr,"DEBUG: found terminal in built-in table\n"); X } X type = built_in[i].name; X verify &= ~unknown; X break; X } X } X X if (verify) { X verify_type(type); X } else { X printf("%s\n",type); X } X exit(0); X} X X X/* main program */ Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X char *cp; X struct timeval timeout; X extern struct sgttyb tty; X extern char response[]; X X int n = 0; X int r, readfds; X char term[MAXSTR]; /* what do we think the terminal is */ X char *prog; /* remember program name */ X int force = FALSE; X char *s; X extern int verify; X extern int unknown; X X prog = argv[0]; X while (*++argv != NULL && **argv == '-') { X for (s = *argv + 1; *s; s++) { X switch (*s) { X case 'v': X verify = TRUE; X break; X case 'f': X force = TRUE; X break; X case 'u': X unknown = TRUE; X break; X case 'd': X debug = TRUE; X break; X case 'V': X printf("%s\n", header); X exit(0); X default: X fprintf(stderr, "Usage: %s [-dfuv|-V] [default]\n", prog); X exit(1); X } X } X } X X if (debug) { X fprintf(stderr,"DEBUG: options = [DEBUG"); X if (force) fprintf(stderr,", FORCE"); X if (unknown) fprintf(stderr,", UNKNOWN"); X if (verify) fprintf(stderr,", VERIFY"); X fprintf(stderr,"]\n"); X } X X /* determine default terminal name */ X if (*argv != NULL) { X strcpy(def_name, *argv); X } else { X strcpy(def_name, "unknown"); X } X X if (debug) { X fprintf(stderr,"DEBUG: unknown <-> \"%s\"\n", def_name); X } X X /* find out the current terminal name */ X if ((cp = getenv("TERM")) != NULL) { X strcpy(term, cp); X } else { X strcpy(term, ""); X } X X if (debug) { X fprintf(stderr,"DEBUG: TERM = \"%s\"\n", term); X } X X /* ask the terminal its name if we have to */ X if (force || strrel(term, ==, "unknown") || strrel(term, ==, "network") || X strrel(term, ==, "dialup") || strrel(term, ==, "")) { X X if (debug) { X fprintf(stderr,"DEBUG: requesting ID from terminal...\n"); X } X X /* request terminal ID */ X set_ttymodes(); X fprintf(stderr, "%cZ", ESC); X fflush(stderr); X X if (debug) { X fprintf(stderr,"DEBUG: completed ID request.\n"); X } X X /* read the response from the terminal */ X n = 0; X readfds = 1 << fileno(stdin); X timeout.tv_sec = (long) (TIMEOUT1 / 1000); X timeout.tv_usec = (long) (1000 * (TIMEOUT1 % 1000)); X if ((r = select(1, &readfds, (int *) NULL, (int *) NULL, &timeout)) == 1) { X if (debug) { X fprintf(stderr, "DEBUG: at least one character available\n"); X } X timeout.tv_sec = (long) (TIMEOUT2 / 1000); X timeout.tv_usec = (long) (1000 * (TIMEOUT2 % 1000)); X do { X read(fileno(stdin), &response[n++], 1); X readfds = 1 << fileno(stdin); X } while (select(1, &readfds, (int *) NULL, (int *) NULL, &timeout) == 1); X } else { X if (debug) { X fprintf(stderr, "DEBUG: no reponse after %d milliseconds\n", TIMEOUT1); X } X if (r < 0) { X perror("select"); X exit(1); X } X } X response[n] = EOS; X tell_type(); X } else if (verify) { X if (debug) { X fprintf(stderr, "DEBUG: terminal name already known\n"); X } X verify_type(term); X } else { X if (debug) { X fprintf(stderr, "DEBUG: terminal name already known\n"); X } X printf("%s\n", term); X } X} X X X/* turn off echo and character preprocessing */ Xset_ttymodes() X{ X extern struct sgttyb tty; X X gtty(0, &tty); X tty.sg_flags &= ~ECHO; X tty.sg_flags |= CBREAK; X X if (debug) { X fprintf(stderr,"DEBUG: echo off, cbreak on\n"); X } X X stty(0, &tty); X} X X X/* turn on echo and character preprocessing */ Xreset_ttymodes() X{ X extern struct sgttyb tty; X X tty.sg_flags |= ECHO; X tty.sg_flags &= ~CBREAK; X stty(0, &tty); X X if (debug) { X fprintf(stderr,"DEBUG: echo on, cbreak off\n"); X } X} X X X/* make sure the user knows what's happening and allow him change it */ Xverify_type(type) Xchar *type; X{ X char answer[MAXSTR]; X int n = 0; X X if (debug) { X fprintf(stderr,"DEBUG: verifying terminal type...\n"); X } X X fprintf(stderr, "TERM = (%s) ", type); X gets(answer); X X if (debug) { X fprintf(stderr,"DEBUG: answer = \""); X ctrl_print(stderr,answer); X fprintf(stderr,"\"\n"); X } X X if (answer[0] == EOS) { X printf("%s\n", type); X } else { X printf("%s\n", answer); X } X exit (0); X} END_OF_FILE if test 11044 -ne `wc -c <'getterm.c'`; then echo shar: \"'getterm.c'\" unpacked with wrong size! fi # end of 'getterm.c' fi echo shar: End of shell archive. exit 0 -- Mark Nagel UC Irvine Department of ICS +----------------------------------------+ ARPA: nagel@ics.uci.edu | Charisma doesn't have jelly in the | UUCP: ucbvax!ucivax!nagel | middle. -- Jim Ignatowski |