termid -- identify terminal type (answerback) [comp.unix.wizards]

Mark Nagel nagel at ics.uci.edu
Fri Dec 29 11:09:05 AEST 1989


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 at 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 at ucdavis.ucdavis.edu)
X *	Rewritten by Mark Nagel in June, 1987 (nagel at 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 at ics.uci.edu       | Charisma doesn't have jelly in the     |
UUCP: ucbvax!ucivax!nagel     | middle.  -- Jim Ignatowski             |



More information about the Comp.unix.wizards mailing list