home *** CD-ROM | disk | FTP | other *** search
/ The UNIX CD Bookshelf / OREILLY_TUCB_UNIX_CD.iso / upt / examples / SOURCES / QTERM / PART01.Z / PART01 / qterm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-07-24  |  24.2 KB  |  1,063 lines

  1. #ifndef lint
  2. static char *RCSid = "$Header: /src/common/usc/bin/qterm/RCS/qterm.c,v 5.4 1991/03/21 02:09:40 mcooper Exp $";
  3. #endif
  4.  
  5. /*
  6.  * Copyright (c) 1990 Michael A. Cooper.
  7.  * This software may be freely distributed provided it is not sold for 
  8.  * profit and the author is credited appropriately.
  9.  */
  10.  
  11. /*
  12.  *------------------------------------------------------------------
  13.  *
  14.  * $Source: /src/common/usc/bin/qterm/RCS/qterm.c,v $
  15.  * $Revision: 5.4 $
  16.  * $Date: 1991/03/21 02:09:40 $
  17.  * $State: Exp $
  18.  * $Author: mcooper $
  19.  * $Locker:  $
  20.  *
  21.  *------------------------------------------------------------------
  22.  *
  23.  * Michael A. Cooper
  24.  * Research and Development Group
  25.  * University Computing Services 
  26.  * University of Southern California
  27.  * (mcooper@usc.edu)
  28.  *
  29.  *------------------------------------------------------------------
  30.  *
  31.  * $Log: qterm.c,v $
  32.  * Revision 5.4  1991/03/21  02:09:40  mcooper
  33.  * Fix memory buffer problem with some C
  34.  * compilers.  (tp@vtold.vtol.fi)
  35.  *
  36.  * Revision 5.3  1991/03/16  05:36:30  mcooper
  37.  * Fix casting of (char) NULL problem.
  38.  *
  39.  * Revision 5.2  1991/03/12  00:46:24  mcooper
  40.  * Change CMASK to CHAR_CMASK to avoid conflict
  41.  * under AIX 3.1.
  42.  *
  43.  * Revision 5.1  1991/02/20  02:23:33  mcooper
  44.  * Cleanup #ifdef USG5 as part of port
  45.  * to UTS 2.1 (System V.3).
  46.  *
  47.  * Revision 5.0  1990/12/15  18:30:41  mcooper
  48.  * Version 5.
  49.  *
  50.  * Revision 4.13  90/12/15  18:14:23  mcooper
  51.  * Add copywrite.
  52.  * 
  53.  * Revision 4.12  90/11/13  16:00:03  mcooper
  54.  * Convert OptInt's to OptBool's where needed.
  55.  * 
  56.  * Revision 4.11  90/11/13  15:38:28  mcooper
  57.  * Make OLD_SepArg include both
  58.  * SepArg and StickyArg.
  59.  * 
  60.  * Revision 4.10  90/11/08  15:41:08  mcooper
  61.  * Make sure qt_fullname is not 0 length.
  62.  * 
  63.  * Revision 4.9  90/11/08  13:02:06  mcooper
  64.  * Fix bug that closes the tty when an error
  65.  * occurs during command line parsing.
  66.  * 
  67.  * Revision 4.8  90/11/06  13:19:40  mcooper
  68.  * Changed command line options to new 
  69.  * longer names.
  70.  * 
  71.  * Revision 4.7  90/11/05  17:09:30  mcooper
  72.  * Update option help messages and option names
  73.  * to be more mnemonic.
  74.  * 
  75.  * Revision 4.6  90/11/05  16:44:35  mcooper
  76.  * - Converted to use new ParseOptions() for
  77.  *   command line parsing.
  78.  * - Major de-linting.
  79.  * - Convert dprintf() to use varargs (if
  80.  *   HAS_VARARGS is defined).
  81.  * - Lots of misc. cleanup.
  82.  * 
  83.  * Revision 4.5  89/10/20  22:50:49  mcooper
  84.  * Changed code indention to current local
  85.  * standard of 4.  (This should also mess up
  86.  * everybody trying to do diff's from older versions!)
  87.  * 
  88.  * Revision 4.4  89/10/20  14:03:48  mcooper
  89.  * Fixed command line parsing of "-f -q".
  90.  * 
  91.  * Revision 4.3  88/06/16  19:43:46  mcooper
  92.  * - Added -T flag to wait until timeout when
  93.  *   listening for response string.  This solves
  94.  *   problem when the first entry in a table
  95.  *   doesn't have a response string with a
  96.  *   common ending character to look for.
  97.  * - Added -I flag for "intense" query mode.
  98.  * - Cleaned up debugging a bit.
  99.  * 
  100.  * Revision 4.2  88/06/08  15:30:53  mcooper
  101.  * Cleanup pass including removing
  102.  * extraneous debugging messages.
  103.  * 
  104.  * Revision 4.1  88/04/25  13:24:38  mcooper
  105.  * Added -S option to print send and recieve
  106.  * strings as they are sent and recieved as
  107.  * suggested by David W. Sanderson
  108.  * (dws@attunix.att.com).
  109.  * 
  110.  * Revision 4.0  88/03/08  19:30:59  mcooper
  111.  * Version 4.
  112.  * 
  113.  * Revision 3.7  88/03/08  19:28:32  mcooper
  114.  * Major rewrite.
  115.  * 
  116.  * Revision 3.6  88/03/08  15:31:35  mcooper
  117.  * General cleanup time.
  118.  * 
  119.  * Revision 3.5  88/03/08  13:59:39  mcooper
  120.  * - Catch signals and fix terminal modes.
  121.  * - Don't allow alarm times of 0.
  122.  * - Support for HP-UX machines and cleaner
  123.  *   listen() code from Zenon Fortuna, 
  124.  *   HP-UX Support, Hewlett-Packard Vienna.
  125.  * 
  126.  * Revision 3.4  87/10/07  15:16:17  mcooper
  127.  * - Beautify code a bit.
  128.  * - Add -w <N> option to set the wait time.
  129.  * 
  130.  * Revision 3.3  87/08/24  19:25:32  mcooper
  131.  * The following based on code from Frank Crawford 
  132.  * <frank@teti.qhtours.OZ>:
  133.  * - Use $TERM as output string when the terminal
  134.  *   type is not known instead of "dumb".
  135.  * - Regular Expressions are now supported.  RE are
  136.  *   started with a leading `\'.
  137.  * - Octal values may now be used in send/recieve strings.
  138.  * 
  139.  * Revision 3.1  87/08/03  15:21:07  mcooper
  140.  * As pointed out by Scott H. Robinson <shr@cetus.ece.cmu.edu>,
  141.  * the -F switch does work.  Problem was that it never read
  142.  * in the ~/.qterm file.
  143.  * 
  144.  * Revision 3.0  87/06/30  19:07:59  mcooper
  145.  * Release of version 3.
  146.  * 
  147.  * Revision 2.4  87/04/29  19:28:35  mcooper
  148.  * In readtabfile() we now do special
  149.  * things when opening "file" fails
  150.  * depending on the bequiet flag.
  151.  * 
  152.  * Revision 2.3  87/04/29  13:11:37  mcooper
  153.  * - No more "internal" table.  The master
  154.  *   table is read from a file (TABFILE).
  155.  *   This makes ~/.qterm stuff much cleaner.
  156.  * - Error handling for qtermtab files is
  157.  *   much more informative now.
  158.  * - More things I can't remember.
  159.  * 
  160.  * Revision 2.2  87/03/05  21:01:28  mcooper
  161.  * Fixed system V compiler problem.
  162.  * 
  163.  * Revision 2.1  87/03/01  19:43:22  mcooper
  164.  * Be more intelligent about the size of 
  165.  * the default terminal table.
  166.  * 
  167.  * Revision 2.0  87/03/01  19:20:00  mcooper
  168.  * General cleanup.
  169.  * 
  170.  *------------------------------------------------------------------
  171.  */
  172.  
  173.  
  174. /*
  175.  * qterm - Query Terminal
  176.  *
  177.  * qterm is used to query a terminal to determine the name of the terminal.
  178.  * This is done by sending a fairly universal string "\33Z" to the terminal,
  179.  * reading in a response, and comparing it against a master table of responses
  180.  * and names.  The "name" printed to standard output should be one found in
  181.  * the termcap(5) database.
  182.  *
  183.  * Putting a line in your ".login" file such as:
  184.  *
  185.  *    setenv TERM `qterm`
  186.  *
  187.  * or the following lines in your ".profile" file:
  188.  *
  189.  *    TERM=`qterm`
  190.  *    export TERM
  191.  *
  192.  * will set your terminal type automagically.
  193.  * 
  194.  * If you add a terminal to the master table, please also send me a copy
  195.  * so that I may put it into my version.
  196.  *
  197.  * Michael Cooper
  198.  * Internet:     mcooper@usc.edu
  199.  * UUCP:     ...!rutgers!usc!mcooper
  200.  * BITNET:    mcooper@gamera
  201.  */
  202.  
  203. #include <stdio.h>
  204. #include <ctype.h>
  205. #include <pwd.h>
  206. #include <signal.h>
  207. #include <sys/ioctl.h>
  208. #include <setjmp.h>
  209. #ifdef USG5
  210. # include <termio.h>
  211. #else /*USG5*/
  212. # include <sys/file.h>
  213. # include <sgtty.h>
  214. #endif /*USG5*/
  215. #include "qterm.h"
  216. #include "options.h"
  217. #ifdef HAS_VARARGS
  218. #include <varargs.h>
  219. #endif /*HAS_VARARGS*/
  220.  
  221. #ifdef USG5
  222. struct termio _ntty, _otty;
  223. #else
  224. struct sgttyb _tty;
  225. #endif
  226. int _tty_ch = 2;
  227. char recvbuf[SIZE];
  228. char *progname;
  229. char *termfile = NULL;
  230.  
  231. int debug = FALSE;        /* Debug mode */
  232. int use_alt_str = FALSE;    /* Alternate string */
  233. int towait = FALSE;        /* Time out wait flag */
  234. int always_send = FALSE;    /* Intense query mode */
  235. int longname = FALSE;        /* Print long terminal name */
  236. int sent_chars = FALSE;        /* Print strings sent from the terminal */
  237. int watch_chars = FALSE;    /* Watch strings as they are sent and recv. */
  238. int quiet = FALSE;        /* Quiet mode */
  239. int do_usrtabfile = FALSE;    /* Use user's own .qtermtab file */
  240. int do_systabfile = TRUE;    /* Use the system's qterm tab file */
  241. int almwait = WAIT;        /* Wait (timeout) interval */
  242.  
  243. /*
  244.  * Old options should not be visable in help and usage messages.
  245.  */
  246. #ifdef OPT_COMPAT
  247. #define OLD_NoArg    NoArg|ArgHidden
  248. #define OLD_SepArg    SepArg|StickyArg|ArgHidden
  249. #define fFLAG         "-f"
  250. #define FFLAG        "-F"
  251. #endif
  252.  
  253. /*
  254.  * Command line options table.
  255.  */
  256. OptionDescRec opts[] = {
  257. #ifdef OPT_COMPAT
  258.     {"-a",     OLD_NoArg,    OptInt,    (caddr_t) &use_alt_str,        "1",
  259.      (char *)NULL,    "Use alternate query string"},
  260.     {"-s",    OLD_NoArg,    OptInt,    (caddr_t) &sent_chars,        "1",
  261.      (char *)NULL,    "Display the characters the terminal sent"},
  262.     {"-t", ArgHidden|OLD_NoArg,    OptInt,    (caddr_t) &sent_chars,        "1",
  263.      (char *)NULL,    "Display the characters the terminal sent"},
  264.     {"-I",    OLD_NoArg,    OptInt,    (caddr_t) &always_send,        "1",
  265.      (char *)NULL,    "Always send the terminal query string"},
  266.     {"-T",    OLD_NoArg,    OptInt,    (caddr_t) &towait,        "1",
  267.      (char *)NULL,    "Enable time out wait"},
  268.     {"-S",    OLD_NoArg,    OptInt,    (caddr_t) &watch_chars,        "1",
  269.      (char *)NULL,    "Print strings as they are sent and received"},
  270.     {"-q",    OLD_NoArg,    OptInt,    (caddr_t) &quiet,        "1",
  271.      (char *)NULL,    "Enable quite mode"},
  272.     {"-f",    OLD_SepArg,    OptStr,    (caddr_t) &termfile,  fFLAG,
  273.      "<tabfile>",  "Try <tabfile>, then ~/.qtermtab, then system tabfile"},
  274.     {"-F",    OLD_SepArg,    OptStr,    (caddr_t) &termfile,  FFLAG,
  275.      "<tabfile>",    "Try <tabfile>, then ~/.qtermtab"},
  276.     {"-l",    OLD_NoArg,    OptInt,    (caddr_t) &longname,        "1",
  277.      (char *)NULL,    "Output only the long (verbose) terminal name"},
  278.     {"-d",     OLD_NoArg,    OptInt,    (caddr_t) &debug,        "1",
  279.      (char *)NULL,    "Enable debug mode"},
  280.     {"-w",    OLD_SepArg,    OptInt,    (caddr_t) &almwait,        __ NULL,
  281.      "<interval>",    "Wait (timeout) period (in seconds)"},
  282. #endif /*OPT_COMPAT*/
  283.     {"+alt",     NoArg,        OptBool, (caddr_t) &use_alt_str,    "1",
  284.      (char *)NULL,    "Use alternate query string"},
  285.     {"-alt",     NoArg,        OptBool, (caddr_t) &use_alt_str,    "0",
  286.      (char *)NULL,    "Don't use alternate query string"},
  287.     {"+always",    NoArg,        OptBool, (caddr_t) &always_send,    "1",
  288.      (char *)NULL,    "Always send the terminal query string"},
  289.     {"-always",    NoArg,        OptBool, (caddr_t) &always_send,    "0",
  290.      (char *)NULL,    "Don't always send the terminal query string"},
  291.     {"-file",    SepArg,        OptStr,    (caddr_t) &termfile,          __ NULL,
  292.      "<tabfile>",   "Use <tabfile> to query terminal"},
  293.     {"+longname",NoArg,        OptBool, (caddr_t) &longname,        "1",
  294.      (char *)NULL,    "Output only the long (verbose) terminal name"},
  295.     {"-longname",NoArg,        OptBool, (caddr_t) &longname,        "0",
  296.      (char *)NULL,    "Don't output the long (verbose) terminal name"},
  297.     {"+quiet",    NoArg,        OptBool, (caddr_t) &quiet,        "1",
  298.      (char *)NULL,    "Enable quiet mode"},
  299.     {"-quiet",    NoArg,        OptBool, (caddr_t) &quiet,        "0",
  300.      (char *)NULL,    "Disable quiet mode"},
  301.     {"+sent",    NoArg,        OptBool, (caddr_t) &sent_chars,        "1",
  302.      (char *)NULL,    "Display the characters the terminal sent"},
  303.     {"-sent",    NoArg,        OptBool, (caddr_t) &sent_chars,        "0",
  304.      (char *)NULL,    "Don't display the characters the terminal sent"},
  305.     {"+timeout",NoArg,        OptBool, (caddr_t) &towait,        "1",
  306.      (char *)NULL,    "Enable time out wait"},
  307.     {"-timeout",NoArg,        OptBool, (caddr_t) &towait,        "0",
  308.      (char *)NULL,    "Disable time out wait"},
  309.     {"+usrtab",    NoArg,        OptBool, (caddr_t) &do_usrtabfile,    "1",
  310.      (char *)NULL,    "Enable using ~/.qtermtab"},
  311.     {"-usrtab",    NoArg,        OptBool, (caddr_t) &do_usrtabfile,    "0",
  312.      (char *)NULL,    "Disable using ~/.qtermtab"},
  313.     {"-wait",    SepArg,        OptInt,    (caddr_t) &almwait,        __ NULL,
  314.      "<interval>",    "Wait (timeout) period (in seconds)"},
  315.     {"+watch",    NoArg,        OptBool, (caddr_t) &watch_chars,    "1",
  316.      (char *)NULL,    "Watch the characters sent and recieved"},
  317.     {"-watch",    NoArg,        OptBool, (caddr_t) &watch_chars,    "0",
  318.      (char *)NULL,    "Don't watch the characters sent and recieved"},
  319.     {"+systab",    NoArg,        OptBool, (caddr_t) &do_usrtabfile,    "1",
  320.      (char *)NULL,    "Enable using system qtermtab file"},
  321.     {"-systab",    NoArg,        OptBool, (caddr_t) &do_systabfile,    "0",
  322.      (char *)NULL,    "Disable using system qtermtab file"},
  323.     {"-debug", ArgHidden|NoArg,    OptInt,    (caddr_t) &debug,        "1",
  324.      (char *)NULL,    "Enable debug mode"},
  325. };
  326.  
  327. FILE *fopen();
  328. char *decode();
  329. char *getenv();
  330. char *malloc();
  331. char *re_comp();
  332. char *strcat();
  333. char *xmalloc();
  334. int alarm();
  335. int found = FALSE;
  336. int modes_set = FALSE;
  337. jmp_buf env;
  338. struct termtable *compare();
  339. struct passwd *getpwuid();
  340. void catch();
  341. void done();
  342. void dprintf();
  343. void exit();
  344. void myperror();
  345. void mktable();
  346. void notrecognized();
  347. void proctab();
  348. void wakeup();
  349. #ifdef USG5
  350. char *regcmp();
  351. #endif /* USG5 */
  352.  
  353. main(argc, argv)
  354.      int argc;
  355.      char **argv;
  356. {
  357.     config(argc, argv);
  358.     setmodes();
  359.     mktable();
  360.     proctab((struct termtable *)NULL);
  361.     resetmodes();
  362.     
  363.     if (!found) {
  364.     notrecognized();
  365.     }
  366.  
  367.     exit(0);
  368. }
  369.  
  370. /*
  371.  * Config() - Perform configuration operations.
  372.  */
  373. config(argc, argv)
  374.      int argc;
  375.      char **argv;
  376. {
  377.     progname = argv[0];
  378.  
  379.     /*
  380.      * Parse command line args
  381.      */
  382.     if (ParseOptions(opts, Num_Opts(opts), argc, argv) < 0) {
  383.     done(1);
  384.     /*NOTREACHED*/
  385.     }
  386.  
  387.     /*
  388.      * Check results of command line parsing and perform any
  389.      * needed post processing.
  390.      */
  391.  
  392.     if (longname)
  393.     quiet = TRUE;
  394.  
  395.     if (almwait == 0) {
  396.     (void) fprintf(stderr, 
  397.               "%s: Alarm (wait) time must be greater than 0.\n",
  398.                progname);
  399.     done(1);
  400.     /*NOTREACHED*/
  401.     }
  402.  
  403. #ifdef OPT_COMPAT
  404.     /*
  405.      * Kludgy stuff to be backwards compatable for command line options.
  406.      */
  407.     if (termfile) {
  408.     if (strcmp(termfile, fFLAG) == 0) {
  409.         do_usrtabfile = TRUE;
  410.         do_systabfile = TRUE;
  411.         termfile = NULL;
  412.     } else if (strcmp(termfile, FFLAG) == 0) {
  413.         do_usrtabfile = TRUE;
  414.         do_systabfile = FALSE;
  415.         termfile = NULL;
  416.     }
  417.     }
  418. #endif /*OPT_COMPAT*/
  419.  
  420.     dprintf("[ %s debug mode enabled ]\n\n", progname);
  421. }
  422.  
  423. /*
  424.  * Set signal catches and terminal modes
  425.  */
  426. setmodes()
  427. {
  428.     if (!isatty(0)) {
  429.     (void) fprintf(stderr, "%s: Not a tty.\n", progname);
  430.     done(0);
  431.     /*NOTREACHED*/
  432.     }
  433.     
  434.     /*
  435.      * Set output buffers
  436.      */
  437.     setbuf(stdout, (char *)0);
  438.     if (debug)
  439.     setbuf(stderr, (char *)0);
  440.     
  441.     /*
  442.      * Cleanup terminal modes & such if we are killed
  443.      */
  444.     (void) signal(SIGINT, catch);
  445.     (void) signal(SIGHUP, catch);
  446.     (void) signal(SIGTERM, catch);
  447.     
  448.     /*
  449.      * Set terminal modes
  450.      */
  451. #ifdef USG5
  452.     if (ioctl(_tty_ch, TCGETA, &_otty) < 0)
  453. #else
  454.     if (ioctl(_tty_ch, TIOCGETP, &_tty) < 0)
  455. #endif /* USG5 */
  456.     {
  457.     myperror("gtty");
  458.     done(1);
  459.     /*NOTREACHED*/
  460.     }
  461. #ifdef USG5
  462.     _ntty = _otty;
  463. #endif /* USG5 */
  464.  
  465.     if (crmode() < 0) {
  466.     myperror("crmode");
  467.     done(1);
  468.     /*NOTREACHED*/
  469.     }
  470.     
  471.     if (noecho() < 0) {
  472.     myperror("noecho");
  473.     done(1);
  474.     /*NOTREACHED*/
  475.     }
  476.     modes_set = TRUE;
  477. }
  478.  
  479. /*
  480.  * Reset terminal modes
  481.  */
  482. resetmodes()
  483. {
  484.     if (modes_set) {
  485.     (void) nocrmode();
  486.     (void) echo();
  487.     }
  488. }
  489.  
  490. /*
  491.  * Print info about terminal structure t.
  492.  */
  493. prinfo(t, what)
  494.      struct termtable *t;
  495.      int what;
  496. {
  497.     int len = 0;
  498.     int st = FALSE;
  499.     
  500.     if (t && t->qt_termname && (recvbuf[0] != (char) NULL)) {
  501.     if (debug || sent_chars) {
  502.         len = strlen(recvbuf);
  503.         (void) fprintf(stderr, "%s received %d character%s:", 
  504.                progname, len, (len == 1) ? "" : "s");
  505.         (void) fprintf(stderr, " %s\n", decode(recvbuf));
  506.     }
  507.     
  508.     if (!quiet) {
  509.         (void) fprintf(stderr, "Terminal recognized as %s", 
  510.                t->qt_termname);
  511.         if (t->qt_fullname && t->qt_fullname[0])
  512.         (void) fprintf(stderr, " (%s)\n", t->qt_fullname);
  513.         else
  514.         (void) fprintf(stderr, "\n");
  515.     }
  516.     
  517.     if (longname) {
  518.         if (t->qt_fullname && t->qt_fullname[0])
  519.         (void) printf("%s\n", t->qt_fullname);
  520.         else
  521.         (void) fprintf(stderr, "%s: No full terminal name for %s.\n",
  522.                    progname, t->qt_termname);
  523.     } else {
  524.         (void) printf("%s\n", t->qt_termname);
  525.     }
  526.     
  527.     found = TRUE;
  528.     done(0);
  529.     /*NOTREACHED*/
  530.     } else {
  531.     found = FALSE;
  532.     
  533.     if (what) {
  534.         notrecognized();
  535.         done(1);
  536.         /*NOTREACHED*/
  537.     }
  538.     }
  539.     
  540.     return(st);
  541. }
  542.  
  543. /*
  544.  * compare - actually compare what we received against the table.
  545.  */
  546. struct termtable *compare(str)
  547.      char *str;
  548. {
  549. #ifdef USG5
  550.     register char *reexp;
  551. #endif /* USG5 */
  552.     register struct termtable *t;
  553.     char buf[BUFSIZ];
  554.  
  555.     dprintf("compare %s\n", (str && str[0]) ? decode(str) : "nothing");
  556.     (void) alarm((unsigned)0);
  557.     
  558.     if (strlen(str) == 0)
  559.     return(NULL);
  560.     
  561.     for (t = termtab; t != NULL; t = t->nxt) {
  562.     dprintf("  with %s ", decode(t->qt_recvstr));
  563.     (void) sprintf(buf, "^%s$", t->qt_recvstr);
  564.     
  565. #ifdef USG5
  566.     if ((reexp = regcmp(buf, NULL)) == NULL) {
  567. #else
  568.     if (re_comp((char *)buf) != NULL) {
  569. #endif /* USG5 */
  570.         (void) fprintf(stderr, "%s: bad regular expression: \"%s\"\n", 
  571.                progname, t->qt_recvstr);
  572.         done(1);
  573.         /*NOTREACHED*/
  574.     }
  575.  
  576. #ifdef USG5
  577.     if (regex(reexp, str) != NULL) {
  578. #else
  579.     if (re_exec(str) == 1) {
  580. #endif /* USG5 */
  581.         found = TRUE;
  582.         dprintf("\tOK\n");
  583.         return(t);
  584.     }
  585.  
  586.     dprintf("\tNOPE\n");
  587. #ifdef USG5
  588.     (void) free(reexp);
  589. #endif /* USG5 */
  590.     }
  591.     found = FALSE;
  592.  
  593.     return(NULL);
  594. }
  595.  
  596. /*
  597.  * getch - read in a character at a time.
  598.  */
  599. getch()
  600. {
  601.     char c;
  602.     
  603.     (void) read(0, &c, 1);
  604.     
  605.     return(c & CHAR_MASK);
  606. }
  607.  
  608. /*
  609.  * decode - print str in a readable fashion
  610.  */
  611. char *decode(str)
  612.      char *str;
  613. {
  614.     register int len;
  615.     static char buf[BUFSIZ];
  616.     char tmp[10];
  617.     
  618.     if (!str)
  619.       return("(null)");
  620.     
  621.     (void) strcpy(buf, "");
  622.     while (*str) {
  623.     if (*str == ESC) {
  624.         (void) strcat(buf, "<esc> ");
  625.     } else if ((*str <= 33) || (*str >= 127)) {
  626.         (void) sprintf(tmp,"\\%#o ", (unsigned) *str);
  627.         (void) strcat(buf, tmp);
  628.     } else {
  629.         (void) sprintf(tmp,"%c ", *str);
  630.         (void) strcat(buf, tmp);
  631.     }
  632.     ++str;
  633.     }
  634.     
  635.     len = strlen(buf);
  636.     if (len && buf[len - 1] == ' ') {
  637.     buf[len - 1] = (char) NULL;
  638.     }
  639.     
  640.     return(buf);
  641. }
  642.  
  643. /*
  644.  * Make a termtab table
  645.  */
  646. void mktable()
  647. {
  648.     char file[BUFSIZ];
  649.     struct passwd *pwd;
  650.     char *home;
  651.  
  652.     dprintf("[ initilizing term table... ]\n");
  653.     
  654.     if (termfile != NULL) {
  655.     (void) readtabfile(termfile, FALSE);
  656.     }
  657.  
  658.     if (do_usrtabfile) {
  659.     /*
  660.      * Try to read the user's own table
  661.      */
  662.     if ((home = getenv("HOME")) == NULL) {
  663.         if ((pwd = getpwuid(getuid())) == NULL) {
  664.         (void) fprintf(stderr, 
  665.                    "%s: Cannot find user info for uid %d.\n",
  666.                    progname, getuid());
  667.         done(1);
  668.         /*NOTREACHED*/
  669.         }
  670.         home = pwd->pw_dir;
  671.     }
  672.  
  673.     (void) sprintf(file, "%s/%s", home, USRFILE);
  674.     if (readtabfile(file, TRUE) < 0) {
  675.         (void) sprintf(file, "%s/%s", home, OLDUSRFILE);
  676.         (void) readtabfile(file, TRUE);
  677.     }
  678.     }
  679.     
  680.     if (do_systabfile)
  681.     (void) readtabfile(TABFILE, FALSE);
  682.     
  683.     dprintf("[ mktable done ]\n");
  684. }
  685.  
  686. int readtabfile(file, bequiet)
  687.      char *file;
  688.      int bequiet;
  689. {
  690.     static int line = 0;
  691.     char lbuf[4][BUFSIZ];
  692.     char buf[BUFSIZ];
  693.     FILE *fd;
  694.     char *p, *fixctl();
  695.     char *errmsg = NULL;
  696.     struct termtable *t;
  697.     
  698.     if ((fd = fopen(file, "r")) == NULL) {
  699.     if (bequiet) {
  700.         dprintf("[ tab file '%s' can not read ]\n", file);
  701.         return(-1);
  702.     }
  703.     myperror(file);
  704.     done(1);
  705.     /*NOTREACHED*/
  706.     }
  707.  
  708.     dprintf("[ Read tab file '%s' ]\n", file);
  709.  
  710.     line = 0;
  711.     while (fgets(buf, sizeof(buf), fd)) {
  712.     ++line;
  713.     
  714.     if (buf[0] == '#' || buf[0] == '\n')
  715.         continue;
  716.     
  717.     lbuf[0][0] = lbuf[1][0] = lbuf[2][0] = lbuf[3][0] = (char) NULL;
  718.     
  719.     (void) sscanf(buf, "%s%s%s\t%[^\n]", 
  720.               lbuf[0], lbuf[1], lbuf[2], lbuf[3]);
  721.     
  722.     if (lbuf[0][0] == (char) NULL)
  723.         continue;
  724.     
  725.     if (lbuf[1][0] == (char) NULL)
  726.         errmsg = "receive string";
  727.     
  728.     if (lbuf[2][0] == (char) NULL)
  729.         errmsg = "terminal name";
  730.     
  731.     if (errmsg) {
  732.         (void) fprintf(stderr, "%s: Line %d of %s: Error parsing %s.\n", 
  733.                progname, line, file, errmsg);
  734.         done(1);
  735.         /*NOTREACHED*/
  736.     }
  737.     
  738.     t = (struct termtable *) xmalloc(sizeof(struct termtable));
  739.     
  740.     if (use_alt_str)
  741.         p = fixctl(ALTSEND, 0);
  742.     else
  743.         p = fixctl(lbuf[0], 0);
  744.     
  745.     t->qt_sendstr = (char *) xmalloc(strlen(p)+1);
  746.     (void) strcpy(t->qt_sendstr, p);
  747.     
  748.     p = fixctl(lbuf[1], 1);
  749.     t->qt_recvstr = (char *) xmalloc(strlen(p)+1);
  750.     (void) strcpy(t->qt_recvstr, p);
  751.     
  752.     t->qt_termname = (char *) xmalloc(strlen(lbuf[2])+1);
  753.     (void) strcpy(t->qt_termname, lbuf[2]);
  754.     
  755.     t->qt_fullname = (char *) xmalloc(strlen(lbuf[3])+1);
  756.     (void) strcpy(t->qt_fullname, lbuf[3]);
  757.     
  758.     dprintf("\n  Send String = %s\n", decode(t->qt_sendstr));
  759.     dprintf("Expect String = %s\n", decode(t->qt_recvstr));
  760.     dprintf("     Terminal = '%s'\n", t->qt_termname);
  761.     dprintf("    Full Name = '%s'\n", t->qt_fullname);
  762.     
  763.     (void) addterm(t);
  764.     }
  765.     
  766.     return(0);
  767. }
  768.  
  769. /*
  770.  * Add termtab (n) entry to main termtab.
  771.  */
  772. int addterm(n)
  773.      struct termtable *n;
  774. {
  775.     register struct termtable *t;
  776.     
  777.     if (!n)
  778.       return(-1);
  779.     
  780.     n->nxt = NULL;
  781.     
  782.     if (termtab == NULL) {
  783.     termtab = n;
  784.     } else {
  785.     t = termtab;
  786.     while(t && t->nxt)
  787.       t = t->nxt;
  788.     t->nxt = n;
  789.     }
  790.     
  791.     return(0);
  792. }
  793.  
  794. /*
  795.  * Listen for a response.
  796.  */
  797. void qterm_listen(q)
  798.      struct termtable *q;
  799. {
  800.     static int i, len;
  801.     register char c;
  802.     char end;
  803.     
  804.     (void) alarm((unsigned)0);
  805.     (void) strcpy(recvbuf, "");
  806.     i = 0;
  807.     
  808.     len = strlen(q->qt_recvstr);
  809.     
  810.     if (len) {
  811.     end = q->qt_recvstr[len - 1];
  812.     } else {
  813.     end = 'c'; /* Fairly standard ANSI default */
  814.     }
  815.     
  816.     dprintf("\nlisten for %s\t [ len = %d, end = `%c' ]\n", 
  817.         decode(q->qt_recvstr), len, end);
  818.     
  819.     /*
  820.      * If we don't get an initial character, bounce out
  821.      * of here and finish with done(0).
  822.      */
  823.     if (setjmp(env)) {
  824.     if (found) {
  825.         done(0);
  826.         /*NOTREACHED*/
  827.     }
  828.     (void) fflush(stdin);
  829.     proctab(q->nxt);
  830.     } else {
  831.     (void) signal(SIGALRM, wakeup);
  832.     (void) alarm((unsigned)almwait);
  833.     recvbuf[0] = getch();
  834.     (void) alarm((unsigned)0);
  835.     }
  836.     
  837.     /*
  838.      * Read in remaining response.  Loop until ending character
  839.      * is received or until alarm goes off.  If towait is set,
  840.      * then let alarm go off.
  841.      */
  842.     for (i = 1, c = -1; (!towait && (c != end)) || towait; ) {
  843.     if (setjmp(env))  {
  844.         recvbuf[i] = (char) NULL;
  845.         return;
  846.     } else {
  847.         (void) signal(SIGALRM, wakeup);
  848.         (void) alarm((unsigned)almwait);
  849.         c = getch();
  850.         (void) alarm((unsigned)0);
  851.     }
  852.     recvbuf[i++] = c;
  853.     }
  854.     recvbuf[i] = (char) NULL;
  855.     
  856.     dprintf("listen done.  read %d chars.\n\n", i);
  857. }
  858.  
  859. /*
  860.  * Print a message since we didn't recognize this terminal.
  861.  */
  862. void notrecognized()
  863. {
  864.     char *envterm;
  865.     
  866.     if ((envterm = getenv("TERM")) == NULL)
  867.     envterm = "dumb";
  868.     
  869.     if (!quiet)
  870.     (void) fprintf(stderr, 
  871.                "Terminal NOT recognized - defaults to \"%s\".\n",
  872.                envterm);
  873.     
  874.     puts(envterm);
  875. }
  876.  
  877. /*
  878.  * Process entries in the termtable.
  879.  */
  880. void proctab(t)
  881.      struct termtable *t;
  882. {
  883.     int st = FALSE;
  884.     static int firsttime = TRUE;
  885.     static struct termtable *lastt;
  886.     
  887.     dprintf("\n[ Processing entries ] \n");
  888.     
  889.     if (firsttime) {
  890.     t = termtab;
  891.     lastt = NULL;
  892.     }
  893.     
  894.     while ((!found || do_usrtabfile) && t && t->qt_sendstr && !st) {
  895.     /*
  896.      * If this is our first time or the sendstr is the same as
  897.      * last time, don't send it again.
  898.      */
  899.     if (always_send || firsttime || lastt == NULL || 
  900.         strcmp(t->qt_sendstr, lastt->qt_sendstr) != 0) {
  901.         
  902.         if (firsttime)
  903.         firsttime = FALSE;
  904.         
  905.         if (watch_chars)
  906.         (void) printf("Send: %s\n", decode(t->qt_sendstr));
  907.  
  908.         (void) fflush(stdin);
  909.         (void) fprintf(stderr, "%s", t->qt_sendstr);
  910.         (void) fflush(stderr);
  911.         
  912.         lastt = t;
  913.         (void) qterm_listen(t);
  914.         
  915.         if (watch_chars)
  916.         (void) printf("\tRead: %s\n", decode(recvbuf));
  917.     }
  918.     
  919.     st = prinfo(compare(recvbuf), FALSE);
  920.     
  921.     lastt = t;
  922.     t = t->nxt;
  923.     }
  924.     
  925.     if (!found)
  926.     notrecognized();
  927.     
  928.     done(0);
  929.     /*NOTREACHED*/
  930. }
  931.  
  932. char *fixctl(str, rex)
  933.      char *str;
  934.      int rex;
  935. {
  936.     register int i;
  937.     static char buf[BUFSIZ];
  938.     
  939.     for (i = 0; str && *str; ) {
  940.     switch (*str) {
  941.         
  942.       case '\\':
  943.         if (isdigit(*++str)) {
  944.         buf[i] = 0;
  945.         while (isdigit(*str))
  946.             buf[i] = (char) (((int)buf[i] * 8) + 
  947.                      (int)*str++ - (int) '0');
  948.         i++;
  949.         } else
  950.         buf[i++] = *str++;
  951.         continue;
  952.         
  953.       case '^':
  954.         switch (*++str) {
  955.           case '?':
  956.         buf[i++] = '\177';
  957.         break;
  958.           default:
  959.         buf[i++] = *str & 037;
  960.         break;
  961.         }
  962.         break;
  963.         
  964.         /* Special R.E. symbols */
  965.       case '[':
  966.       case '*':
  967.       case '.':
  968.       case '$':
  969.       case '{':
  970.       case '(':
  971.         if (rex)
  972.           buf[i++] = '\\';
  973.         
  974.       default:
  975.         buf[i++] = *str;
  976.     }
  977.     *++str;
  978.     }
  979.     
  980.     buf[i] = (char) NULL;
  981.     
  982.     return(buf);
  983. }
  984.  
  985. /*
  986.  * xmalloc - Do a malloc with error checking.
  987.  */
  988. char *xmalloc(size)
  989.      int size;
  990. {
  991.     char *p;
  992.     
  993.     if ((p = malloc((unsigned) size)) == NULL) {
  994.     myperror("malloc");
  995.     done(1);
  996.     /*NOTREACHED*/
  997.     }
  998.     
  999.     return(p);
  1000. }
  1001.  
  1002. #ifdef HAS_VARARGS
  1003. void dprintf(va_alist)
  1004.      va_dcl
  1005. {
  1006.     va_list args;
  1007.     char *fmt;
  1008.  
  1009.     if (!debug)
  1010.     return;
  1011.  
  1012.     va_start(args);
  1013.     fmt = (char *) va_arg(args, char *);
  1014.     (void) vprintf(fmt, args);
  1015.     va_end(args());
  1016.     (void) fflush(stdout);
  1017. }
  1018.  
  1019. #else /*HAS_VARARGS*/
  1020.  
  1021. void dprintf(fmt, a1, a2, a3, a4, a5, a6)
  1022.      char *fmt;
  1023. {
  1024.     if (!debug)
  1025.     return;
  1026.  
  1027.     (void) printf(fmt, a1, a2, a3, a4, a5, a6);
  1028.     (void) fflush(stdout);
  1029. }
  1030. #endif /*HAS_VARARGS*/
  1031.  
  1032. /*
  1033.  * Catch kill signals and cleanup.
  1034.  */
  1035. void catch()
  1036. {
  1037.     done(2);
  1038.     /*NOTREACHED*/
  1039. }
  1040.  
  1041. void wakeup()
  1042. {
  1043.     dprintf("wakeup called\n");
  1044.     longjmp(env, 1);
  1045. }
  1046.  
  1047. void myperror(msg)
  1048.      char *msg;
  1049. {
  1050.     (void) fprintf(stderr, "%s: ", progname);
  1051.     perror(msg);
  1052. }
  1053.  
  1054. /*
  1055.  * Reset terminal and exit with status s.
  1056.  */
  1057. void done(s)
  1058.      int s;
  1059. {
  1060.     resetmodes();
  1061.     exit(s);
  1062. }
  1063.