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: }