home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Other Langs / Tickle-4.0 (tcl) / tcl / expecTerm / expect.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-17  |  55.5 KB  |  2,051 lines  |  [TEXT/MPS ]

  1. /* expect.c - expect and trap commands
  2. ***************************************************************************** 
  3. expecTerm version 1.0 beta
  4. Mark Weissman
  5. Christopher Matheus
  6. Copyright 1992 by GTE Laboratories Incorporated.
  7.  
  8. Portions of this work are in the public domain.  Permission to use,
  9. copy, modify, and distribute this software and its documentation for
  10. any purpose and without fee is hereby granted, provided that the above
  11. copyright notice appear in all copies and that both the copyright
  12. notice and warranty disclaimer appear in supporting documentation, and
  13. that the names of GTE Laboratories or any of their entities not be
  14. used in advertising or publicity pertaining to distribution of the
  15. software without specific, written prior permission.
  16.  
  17. GTE disclaims all warranties with regard to this software, including
  18. all implied warranties of merchantability and fitness for a particular
  19. purpose, even if GTE Laboratories Incorporated knows about the
  20. purpose.  In no event shall GTE be liable for any special, indirect or
  21. consequential damages or any damages whatsoever resulting from loss of
  22. use, data or profits, whether in an action of contract, negligence or
  23. other tortuous action, arising out of or in connection with the use or
  24. performance of this software.
  25.  
  26. This code is based on and may include parts of Don Libes' expect code:
  27.   expect written by: Don Libes, NIST, 2/6/90
  28.   Design and implementation of expect was paid for by U.S. tax
  29.   dollars.  Therefore it is public domain.  However, the author and NIST
  30.   would appreciate credit if this program or parts of it are used.
  31. ******************************************************************************
  32.  
  33.  
  34. Written by: Don Libes, NIST, 2/6/90
  35. Modified by: Mark Weissman 9/92
  36.  
  37. Design and implementation of this program was paid for by U.S. tax
  38. dollars.  Therefore it is public domain.  However, the author and NIST
  39. would appreciate credit if this program or parts of it are used.
  40.  
  41. $Revision: 1.1 $
  42. $Date: 1992/02/21 19:05:45 $
  43.  
  44. */
  45. /* #define DEBUG_QUIET */    /* MDW: Mon Jun 22 13:27:21 1992 */
  46.  
  47. #include <sys/types.h>
  48. #include <stdio.h>
  49. #include <signal.h>
  50. #ifndef NO_STRING_H
  51. #include <string.h>
  52. #else
  53. char *strchr();
  54. #endif
  55. #include <varargs.h>
  56. #include <errno.h>
  57. #ifdef EXTERN_ERRNO
  58. extern int errno;
  59. #endif
  60. #include <ctype.h>    /* for isspace */
  61. #include <time.h>    /* for time(3) */
  62. #include <sys/wait.h>
  63. #include <sys/time.h>
  64. #include <tcl.h>
  65. extern char *tclRegexpError;    /* declared in tclInt.h */
  66. #include <regexp.h>
  67. #include "translate.h"
  68. #include "global.h"
  69. #include "command.h"
  70. #include "scrinfo.h"
  71. extern double TimevalToDouble(), TimeOfDay();
  72. extern void sigint_handler();
  73.  
  74. /* initial length of strings that we can guarantee patterns can match */
  75. int default_match_max =        2000;
  76. #define INIT_EXPECT_TIMEOUT    "10.0"    /* in seconds */
  77.  
  78. /* user variable names */
  79. #define EXPECT_TIMEOUT        "timeout"
  80. #define EXPECT_MATCH_MAX    "match_max"
  81. #define EXPECT_OUT        "expect_out"
  82. #define SPAWN_ID_ANY_VARNAME    "any_spawn_id"
  83. #define SPAWN_ID_ANY_LIT    "-1"
  84. #define SPAWN_ID_ANY        -1
  85.  
  86. int i_read_errno;    /* place to save errno, if i_read() == -1, so it
  87.                doesn't get overwritten before we get to read it */
  88. static double timeout;    /* seconds */
  89.  
  90. void lowmemcpy();
  91.  
  92. /* 1 ecase struct is reserved for each case in the expect command.  Note that
  93. eof/timeout don't use any of theirs, but the algorithm is simpler this way. */
  94.  
  95. struct ecase {    /* case for expect command */
  96.     int m;        /* master */
  97.     int n;        /* number of patterns */
  98.     char *pat;    /* original pattern spec */
  99.     char **patn;    /* if multiple patterns in list */
  100.     char *body;    /* ptr to body to be executed upon match */
  101. #define PAT_EOF        1
  102. #define PAT_TIMEOUT    2
  103. #define PAT_DEFAULT    3
  104. #define PAT_FULLBUFFER    4
  105. #define PAT_GLOB    5 /* glob-style pattern list */
  106. #define PAT_RE        6 /* regular expression */
  107.     int use;    /* PAT_XXX */
  108.     int glob_start;    /* start of string matched when use == PAT_GLOB */
  109.     int transfer;    /* if false, leave matched chars in input stream */
  110. #define CASE_UNKNOWN    0
  111. #define CASE_NORM    1
  112. #define CASE_LOWER    2
  113.     int Case;    /* convert case before doing match? */
  114.     regexp *re;    /* if this is 0, then pattern match via glob */
  115. #define EXP_BEFORE    1
  116. #define EXP_DURING    2
  117. #define EXP_AFTER    3
  118.     int owner;    /* one of the above */
  119.     float quiet;    /* MDW: Mon Jun 22 13:34:44 1992 */
  120.     float quiet_total;    /* MDW: Mon Jun 22 13:34:47 1992 */
  121.     float quiet_after_noise;    /* MDW: Mon Jun 22 13:34:48 1992 */
  122.     ScreenRegion scr; /* MDW; Fri May 15 11:26:03 1992 */
  123. };
  124.  
  125. /* data structures for support expect_before/after */
  126. struct expect_special {
  127.     char *prefix;        /* command plus blank to shove in front */
  128.                 /* of args upon finding 1 arg and recursing */
  129.     struct ecase *ecases;
  130.     int ecount;        /* count of cases */
  131.     int *masters;
  132.     int mcount;        /* number of masters */
  133.     int me;
  134. } before = {"expect_before ", NULL, 0, NULL, 0, EXP_BEFORE},
  135.   after  = {"expect_after ",  NULL, 0, NULL, 0, EXP_AFTER};
  136.  
  137. /* remove nulls from s.  Initially, the number of chars in s is c, */
  138. /* not strlen(s).  This count does not include the trailing null. */
  139. /* returns number of nulls removed. */
  140. int
  141. rm_nulls(s,c)
  142. char *s;
  143. int c;
  144. {
  145.     char *s2 = s;    /* points to place in original string to put */
  146.             /* next non-null character */
  147.     int count = 0;
  148.     int i;
  149.  
  150.     for (i=0;i<c;i++,s++) {
  151.         if (0 == *s) {
  152.             count++;
  153.             continue;
  154.         }
  155.         if (count) *s2 = *s;
  156.         s2++;
  157.     }
  158.     return(count);
  159. }
  160.  
  161. /* generate printable versions of random ASCII strings.  This is used by */
  162. /* cmdExpect when -d forces it to print strings it is examining. */
  163. char *
  164. printify(s)
  165. char *s;
  166. {
  167.     static int destlen = 0;
  168.     static char *dest = 0;
  169.     char *d;        /* ptr into dest */
  170.     unsigned int need;
  171.  
  172.     if (s == 0) return("<null>");
  173.  
  174.     /* worst case is every character takes 3 to printify */
  175.     need = strlen(s)*3 + 1;
  176.     if (need > destlen) {
  177.         if (dest) free(dest);
  178.         if (!(dest = (char *)malloc(need))) {
  179.             destlen = 0;
  180.             return("malloc failed in printify");
  181.         }
  182.         destlen = need;
  183.     }
  184.  
  185.     for (d = dest;*s;s++) {
  186.         if (*s == '\r') {
  187.             strcpy(d,"\\r");        d += 2;
  188.         } else if (*s == '\n') {
  189.             strcpy(d,"\\n");        d += 2;
  190.         } else if (*s == '\t') {
  191.             strcpy(d,"\\t");        d += 2;
  192.         } else if ((unsigned)*s < 0x20) { /* unsigned strips parity */
  193.             sprintf(d,"\\C%c",*s + '`');    d += 3;
  194.         } else if (*s == 0x7f) {
  195.             /* not syntactically correct, but you get the point */
  196.             strcpy(d,"\\7f");        d += 3;
  197.         } else {
  198.             *d = *s;            d += 1;
  199.         }
  200.     }
  201.     *d = '\0';
  202.     return(dest);
  203. }
  204.  
  205. /* free up any argv structures in the ecases */
  206. void
  207. free_ecases(ecases,ecases_inuse,me)
  208. struct ecase *ecases;
  209. int ecases_inuse;
  210. int me;
  211. {
  212.     int i;
  213.     struct ecase *ec;
  214.  
  215.     for (ec=ecases,i=0;i<ecases_inuse;i++,ec++) {
  216.         if (ec->re) free((char *)ec->re);
  217.         /* individual elements of each list don't have to be freed */
  218.         /* because SplitList allocates them all from single blocks! */
  219.         if (ec->patn) free((char *)ec->patn);
  220.         if (me != EXP_DURING) {
  221.             if (ec->pat) free(ec->pat);
  222.             if (ec->body) free(ec->body);
  223.         }
  224.     }
  225.     if (ecases) free((char *)ecases);
  226. }
  227.  
  228. /* no standard defn for this, and some systems don't even have if, so avoid */
  229. /* the whole quagmire by calling it something else */
  230. char *Strdup(s)
  231. char *s;
  232. {
  233.     char *news = (char *)malloc(strlen(s) + 1);
  234.     if (news) strcpy(news,s);
  235.     return(news);
  236. }
  237.  
  238. /* In many places, there is no need to malloc a copy of a string, since it */
  239. /* will be freed before we return to Tcl */
  240. int
  241. save_str(lhs,rhs,me)
  242. char **lhs;    /* left hand side */
  243. char *rhs;    /* right hand side */
  244. int me;
  245. {
  246.     if ((me == EXP_DURING) || (rhs == 0)) *lhs = rhs;
  247.     else if (!(*lhs = Strdup(rhs))) {
  248.         tcl_error("no space to save: %s",rhs);
  249.         return(TCL_ERROR);
  250.     }
  251.     return(TCL_OK);
  252. }
  253.  
  254. /* union together two arrays, ending up with unique array (b) */
  255. void
  256. union_arrays(a,b,acount,bcount)
  257. int *a, *b;
  258. int acount, *bcount;
  259. {
  260.     int i, j;
  261.  
  262.     for (i=0; i < acount ;i++) {
  263.         /* check this one against all so far */
  264.         for (j=0;j < *bcount;j++) {
  265.             if (a[i] == b[j]) break;
  266.         }
  267.         /* if not found, add to array */
  268.         if (j== *bcount) {
  269.             b[j] = a[i];
  270.             (*bcount)++;
  271.         }
  272.     }
  273. }
  274.  
  275. /* called to execute a command of only one argument - a hack to commands */
  276. /* to be called with all args surrounded by an outer set of braces */
  277. /* returns TCL_whatever */
  278. /*ARGSUSED*/
  279. int
  280. exp_eval_with_one_arg(clientData,argc,argv)
  281. ClientData clientData;
  282. int argc;
  283. char **argv;
  284. {
  285.     char *buf;
  286.     int rc;
  287.     char *a;
  288.  
  289.     /* + 2 is for blank separating cmd and null at end */
  290.     if (!(buf = (char *)malloc(strlen(argv[0]) + strlen(argv[1]) + 2))) {
  291.         tcl_error("%s: no space to save arguments",argv[0]);
  292.         return(TCL_ERROR);
  293.     }
  294.     /* replace top-level newlines with blanks */
  295.     for (a=argv[1];*a;) {
  296.         extern char *TclWordEnd();
  297.  
  298.         for (;isspace(*a);a++) {
  299.             if (*a == '\n') *a = ' ';
  300.         }
  301.         a = TclWordEnd(a,0);
  302.     }
  303.  
  304.     /* recreate statement */
  305.     sprintf(buf,"%s %s",argv[0],argv[1]);
  306.  
  307.     rc = Tcl_Eval(interp,buf,0,(char **)NULL);
  308.     free(buf);
  309.     return(rc);
  310.  
  311. #if 0
  312.     int i;
  313.     int len = strlen(argv[0]) + strlen(argv[1]) + 2;
  314.  
  315.     for (i=0;i<len;i++) {
  316.         if (buf[i] == '{' && just_saw_space) {
  317.             for (;i<len;i++) {
  318.                 if (buf[i] == '}') break;
  319.             }
  320.         } else if (buf[i] == '[') {
  321.             for (;i<len;i++) {
  322.                 if (buf[i] == ']') break;
  323.             }
  324.         } else if (buf[i] == '"' && just_saw_space) {
  325.             for (;i<len;i++) {
  326.                 if (buf[i] == '"') break;
  327.         } else {
  328.             if (is_space(buf[i])) {
  329.                 int just_saw_space = TRUE;
  330.                 if (buf[i] == '\n') buf[i] = ' ';
  331.             }
  332.         } else just_saw_space = FALSE;
  333.     }
  334.  
  335.  
  336.     rc = Tcl_Eval(interp,buf,0,(char **)NULL);
  337.     free(buf);
  338.     return(rc);
  339. #endif
  340. }
  341.  
  342. extern int expectCD_user;
  343. #define EXPECT_USER    (clientData == &expectCD_user)
  344.  
  345. /* parse the arguments to expect or it's variants */
  346. /* returns TCL_OK or TCL_ERROR */
  347. int
  348. parse_expect_args(clientData,argc,argv,ecases,ecases_inuse,masters,mcount,me)
  349. ClientData clientData;
  350. int argc;
  351. char **argv;
  352. struct ecase **ecases;
  353. int *ecases_inuse;
  354. int **masters;        /* array of unique masters in ecases */
  355. int *mcount;        /* count of masters */
  356. int me;
  357. {
  358.     int size;
  359.     int i;
  360.     struct ecase *ec;
  361.     int m;
  362.  
  363. #if 0
  364.     char **oldargv = 0;    /* save original argv here if we split it */
  365. #endif
  366.     int case_master;    /* master to assign to next case as it is */
  367.                 /* being parsed */
  368.  
  369.     argv++;
  370.     argc--;
  371.  
  372.     /* if we want to listen to user, use fd 0 */
  373.     if (EXPECT_USER) m = 0;    /* true if we were called as expect_user */
  374.     else {
  375. /*        if (0 == update_master(&m)) return(TCL_ERROR);*/
  376.         update_master(&m);    /* it'll be checked later, if used */
  377.     }
  378.  
  379.     *ecases_inuse = (1+argc)/2;    /* estimate of number of patterns */
  380.  
  381.     /* This takes into account optional final action */
  382.     /* If flags are used, this will be too high but it's not worth the */
  383.     /* trouble of making two passes, so we'll just adjust the count */
  384.     /* later when we find out the real amount */
  385.  
  386.     if (*ecases) free((char *)*ecases);    /* for before/after cases */
  387.     if (*ecases_inuse) {
  388.         if (0 == (*ecases = (struct ecase *)malloc(*ecases_inuse *
  389.                         sizeof(struct ecase)))) {
  390.             tcl_error("malloc(%d ecases)",*ecases_inuse);
  391.             goto error;
  392.         }
  393.     } else *ecases = NULL;
  394.  
  395.     /* zero them out so that just in case we get an error in the middle */
  396.     /* of SplitList, we can deallocate them cleanly */
  397.     for (i = 0, ec = *ecases;i<argc;i+=2,ec++) {
  398.         ec->patn = 0;    /* necessary? - makes freeing code simpler? */
  399.         ec->pat = 0;
  400.         ec->body = 0;
  401.         ec->transfer = 1;
  402.         ec->re = 0;
  403.         ec->Case = CASE_NORM;
  404.         ec->owner = me;
  405.         ec->use = PAT_GLOB;
  406.         ec->quiet = -1.0;    /* MDW: Mon Jun 22 13:34:26 1992 */
  407.         ec->quiet_total = -1.0;    /* MDW: Mon Jun 22 13:35:12 1992 */
  408.         ec->quiet_after_noise = -1.0;    /* MDW: Mon Jun 22 13:35:12 1992 */
  409.         init_screen_region(&(ec->scr));    /* MDW: Mon Jun 22 13:35:14 1992 */
  410.     }
  411.  
  412.     /* forget old estimate of cases and prepare to calculate true number */
  413.     *ecases_inuse = 0;
  414.     ec = *ecases;
  415.     case_master = m;
  416.     for (i = 0;i<argc;i++) {
  417.       if (streq(argv[i],"-n")) {
  418.         ec->transfer = 0;
  419.         continue;
  420.       /* additional options for emulation mode CJM: May 20 10:12:37 1992 */
  421.       } else if (parse_screen_argument(argc, argv, &i, &(ec->scr))) {
  422.         if (i == -1) return TCL_ERROR;
  423.         continue;
  424.       } else if (streq(argv[i],"-re")) {
  425.         ec->use = PAT_RE;
  426.         continue;
  427.       } else if (streq(argv[i],"-nocase")) {
  428.         ec->Case = CASE_LOWER;
  429.         continue;
  430.       } else if (streq(argv[i],"-quiet")) {    /* MDW: Mon Jun 22 13:36:24 1992 */
  431.         extern double atof();
  432.         i++;
  433.         if (i>=argc) {
  434.           tcl_error("-quiet requires following quiet_required");
  435.           goto error;
  436.         }
  437.         ec->quiet = (double)atof(argv[i]);
  438.         continue;
  439.       } else if (streq(argv[i],"-quiet_total")) {    /* MDW: Mon Jun 22 13:36:27 1992 */
  440.         extern double atof();
  441.         i++;
  442.         if (i>=argc) {
  443.           tcl_error("-quiet_total requires following quiet_required");
  444.           goto error;
  445.         }
  446.         ec->quiet_total = (double)atof(argv[i]);
  447.         continue;
  448.       } else if (streq(argv[i],"-quiet_after_noise")) {    /* MDW: Mon Jun 22 13:36:31 1992 */
  449.         extern double atof();
  450.         i++;
  451.         if (i>=argc) {
  452.           tcl_error("-quiet_after_noise requires following quiet_required");
  453.           goto error;
  454.         }
  455.         ec->quiet_after_noise = (double)atof(argv[i]);
  456.         continue;
  457.       } else if (streq(argv[i],"-i")) {
  458.         i++;
  459.         if (i>=argc) {
  460.           tcl_error("-i requires following spawn_id");
  461.           goto error;
  462.         }
  463.         case_master = atoi(argv[i]);
  464.         continue;
  465.       }
  466.  
  467.       ec->m = case_master;
  468.  
  469.       /* save original pattern spec */
  470.       if (TCL_ERROR == save_str(&ec->pat,argv[i],me)) goto error;
  471.  
  472.       if (save_str(&ec->body,argv[i+1],me) == TCL_ERROR)
  473.         goto error;
  474.  
  475.       if (streq(argv[i],"timeout")) {
  476.         ec->use = PAT_TIMEOUT;
  477.       } else if (streq(argv[i],"eof")) {
  478.         ec->use = PAT_EOF;
  479.       } else if (streq(argv[i],"full_buffer")) {
  480.         ec->use = PAT_FULLBUFFER;
  481.       } else if (streq(argv[i],"default")) {
  482.         ec->use = PAT_DEFAULT;
  483.       } else {        /* pattern */
  484.         if (ec->use == PAT_RE) {
  485.           ec->n = 1;
  486.           tclRegexpError = 0;
  487.           if (!(ec->re = regcomp(argv[i]))) {
  488.         tcl_error("bad regular expression: %s",
  489.               tclRegexpError);
  490.         goto error;
  491.           }
  492.         } else {
  493.           ec->use = PAT_GLOB;
  494.           /* break up list into individual patterns */
  495.               
  496.           if (TCL_OK != Tcl_SplitList(interp,argv[i],
  497.                       &ec->n,&ec->patn)) {
  498.         errorlog("%s\r\n",interp->result);
  499.         tcl_error("expect*: failed to parse pattern: %s",argv[i]);
  500.         goto error;
  501.           }
  502.         }
  503.       }
  504.       i++; ec++; (*ecases_inuse)++;
  505.     }
  506.  
  507.     /* build a list of masters for later use by ready() */
  508.     size = *ecases_inuse;
  509.     if (me == EXP_DURING) {
  510.         size += 1 + before.mcount + after.mcount;
  511.         /* "+1" just in case no patterns given, need placeholder */
  512.         /* for default pattern */
  513.     }
  514.     if (0 == (*masters = (int *)malloc(size*sizeof(int)))) {
  515.         tcl_error("malloc(%d spawn_id's)",size);
  516.         goto error;
  517.     }
  518.  
  519.     *mcount = 0;    /* initially empty */
  520.     for (i=0,ec= *ecases; i < *ecases_inuse ;i++,ec++) {
  521.         int j;
  522.  
  523.         if (ec->m == SPAWN_ID_ANY) continue;
  524.  
  525.         /* check this one against all so far */
  526.         for (j=0;j < *mcount;j++) {
  527.             if (ec->m == (*masters)[j]) break;
  528.         }
  529.         /* if not found, add to array */
  530.         if (j== *mcount) {
  531.             (*masters)[j] = ec->m;
  532.             (*mcount)++;
  533.         }
  534.     }
  535.  
  536.     /* add before/after masters to candidates to pass to ready */
  537.     if (me == EXP_DURING) {
  538.         if (*mcount == 0) {
  539.             /* if no patterns given, force pattern default */
  540.             /* with current master */
  541.             (*masters)[0] = m;
  542.             (*mcount)++;
  543.         }
  544.         union_arrays(before.masters,*masters,before.mcount,mcount);
  545.         union_arrays(after.masters,*masters,after.mcount,mcount);
  546.     }
  547.  
  548.     return(TCL_OK);
  549.  
  550.  error:
  551.     if (*ecases) free_ecases(*ecases,*ecases_inuse,me);
  552.     *ecases = NULL;
  553.     *ecases_inuse = 0;
  554. #if 0
  555.     /* if we split argv ourselves, cleanup */
  556.     if (oldargv) free((char *)argv);
  557. #endif
  558.     return(TCL_ERROR);
  559. }
  560.  
  561. #define EXP_EOF        0
  562. #define EXP_ABEOF    -1    /* abnormal eof */
  563. #define EXP_TIMEOUT    -2
  564. #define EXP_TCLERROR    -3
  565. #define EXP_FULLBUFFER    -5
  566. #define EXP_MATCH    -6
  567. #define EXP_NOMATCH    -7
  568. #define EXP_IS_DEFAULT(x)    ((x) == EXP_TIMEOUT || (x) == EXP_EOF)
  569.  
  570. static char yes[] = "yes\r\n";
  571. static char no[] = "no\r\n";
  572.  
  573. struct eval_out {
  574.     struct ecase *e;        /* ecase */
  575.     struct f *f;
  576.     char *buffer;
  577.     int match;
  578. };
  579.  
  580. /* like eval_cases, but handles only a single cases that needs a real */
  581. /* string match */
  582. /* returns EXP_X where X is MATCH, NOMATCH, FULLBUFFER, TCLERRROR */
  583. int
  584. eval_case_string(e,m,o,last_f,last_case)
  585. struct ecase *e;
  586. int m;
  587. struct eval_out *o;        /* 'output' - i.e., final case of interest */
  588. /* next two args are for debugging, when they change, reprint buffer */
  589. struct f **last_f;
  590. int *last_case;
  591. {
  592.   struct f *f = fs + m;
  593.   char *buffer;
  594.  
  595.   if (!f->buffer) {
  596.     /* not even initialized */
  597.     /* even though no I/O has yet occurred, force a buffer to */
  598.     /* exist so that "expect *" will succeed */
  599.     if (TCL_ERROR == f_adjust(f)) return(EXP_TCLERROR);
  600. #if 0
  601.     return(EXP_NOMATCH);
  602. #endif
  603.   }
  604.  
  605.   /* if -nocase, use the lowerized buffer */
  606.   buffer = ((e->Case == CASE_NORM)?f->buffer:f->lower);
  607.  
  608. #ifdef DEBUG_QUIET 
  609.   fprintf(stderr,"Quiet Start      : %f, End: %f, Delta: %f\n", f->quiet_start, f->quiet_end, (f->quiet_end-f->quiet_start));
  610.   fprintf(stderr,"Quiet total Start: %f, End: %f, Delta: %f\n", f->quiet_total_start, f->quiet_end, (f->quiet_end-f->quiet_total_start));
  611.   fprintf(stderr,"Quiet noise Start: %f, End: %f, Delta: %f\n", f->quiet_after_noise_start, f->quiet_end, (f->quiet_end-f->quiet_after_noise_start));
  612.   fflush(stderr);
  613. #endif
  614.   if (((e->quiet != -1.0) &&
  615.        ((f->quiet_start == 0.0) || ((f->quiet_end - f->quiet_start) < e->quiet))) ||
  616.       ((e->quiet_total != -1.0) &&
  617.        ((f->quiet_total_start == 0.0) || ((f->quiet_end - f->quiet_total_start) < e->quiet_total))) ||
  618.       ((e->quiet_after_noise != -1.0) &&
  619.        ((f->quiet_after_noise_start == 0.0) || ((f->quiet_end - f->quiet_after_noise_start) < e->quiet_after_noise)))) {
  620. #ifdef DEBUG_QUIET    
  621.     fprintf(stderr,"NOQUIET\n"); fflush(stderr);
  622. #endif
  623.     return(EXP_NOMATCH);
  624.   }
  625.     
  626.  
  627.   /* MDW; Fri May 15 11:54:04 1992 */    
  628.   if (((e->scr.match_type == SCR_CONTENTS) || (e->scr.match_type == SCR_ATTRIBUTES))
  629.       && SetSessionFromFD(m)) {
  630.     if (e->scr.breaksp == SCR_NOBREAKS && SessionBufferedInputP())
  631.       return(EXP_NOMATCH);
  632.     else buffer = get_screen_region(&(e->scr));
  633.   }
  634.  
  635.   /* if master or case changed, redisplay debug-buffer */
  636.   if ((f != *last_f) || e->Case != *last_case) {
  637.     debuglog("expect: does {%s} (spawn_id %d) match pattern ",
  638.          dprintify(buffer),f-fs);
  639.     *last_f = f;
  640.     *last_case = e->Case;
  641.   }
  642.  
  643.   if (e->use == PAT_RE) {
  644.     debuglog("{%s}? ",dprintify(e->pat));
  645.     tclRegexpError = 0;
  646.     if (buffer && regexec(e->re,buffer)) {
  647.       o->e = e;
  648.       o->match = e->re->endp[0]-buffer;
  649.       o->buffer = ((e->scr.match_type == SCR_SERIAL && e->Case != CASE_NORM) ? f->buffer : buffer);
  650.       o->f = f;
  651.       debuglog(yes);
  652. #ifdef DEBUG_QUIET
  653.       fprintf(stderr,"OK\n"); fflush(stderr);
  654. #endif
  655.       return(EXP_MATCH);
  656.     } else {
  657.       debuglog(no);
  658.       if (tclRegexpError) {
  659.     tcl_error("r.e. match failed: %s",tclRegexpError);
  660.     return(EXP_TCLERROR);
  661.       }
  662.     }
  663.   } else if (e->use == PAT_GLOB) {
  664.     int j;
  665.     int match;            /* # of chars that matched */
  666.  
  667.     for (j=0;j<e->n;j++) {
  668.       /* skip place-holder for spawn_id with no pattern */
  669.       if (e->patn[j][0] == '\0') continue;
  670.       debuglog("{%s}? ",dprintify(e->patn[j]));
  671.       if (buffer && (-1 != (match = Exp_StringMatch(buffer,e->patn[j],&e->glob_start)))) {
  672.     o->e = e;
  673.     o->match = match;
  674.     o->buffer = ((e->scr.match_type == SCR_SERIAL && e->Case != CASE_NORM) ? f->buffer : buffer);
  675.     o->f = f;
  676.     debuglog(yes);
  677. #ifdef DEBUG_QUIET
  678.     fprintf(stderr,"OK\n"); fflush(stderr);
  679. #endif
  680.     return(EXP_MATCH);
  681.       } else debuglog(no);
  682.     }
  683.   } else if ((f->size == f->msize) && (f->size > 0)) {
  684.     debuglog("%s? ",e->pat);
  685.     o->e = e;
  686.     o->match = f->umsize;
  687.     o->buffer = f->buffer;
  688.     o->f = f;
  689.     return(EXP_FULLBUFFER);
  690.   }
  691. #ifdef DEBUG_QUIET
  692.   fprintf(stderr,"NOMATCH\n"); fflush(stderr);
  693. #endif
  694.   return(EXP_NOMATCH);
  695. }
  696.  
  697. /* sets o.e if successfully finds a matching pattern, eof, timeout or deflt */
  698. /* returns original status arg or EXP_TCLERROR */
  699. eval_cases(ecs_in,ecases_inuse,m,o,last_f,last_case,status,masters,mcount)
  700. struct ecase *ecs_in;
  701. int ecases_inuse;
  702. int m;
  703. struct eval_out *o;        /* 'output' - i.e., final case of interest */
  704. /* next two args are for debugging, when they change, reprint buffer */
  705. struct f **last_f;
  706. int *last_case;
  707. int status;
  708. int *masters;
  709. int mcount;
  710. {
  711.     int i;
  712.     struct ecase *e;
  713.  
  714.     if (o->e || status == EXP_TCLERROR) return(status);
  715.  
  716.     if (status == EXP_TIMEOUT) {
  717.         for (i=0, e=ecs_in;i<ecases_inuse;i++,e++) {
  718.             if (e->use == PAT_TIMEOUT || e->use == PAT_DEFAULT) {
  719.                 o->e = e;
  720.                 break;
  721.             }
  722.         }
  723.         return(status);
  724.     } else if (status == EXP_EOF) {
  725.         for (i=0, e=ecs_in;i<ecases_inuse;i++,e++) {
  726.             if (e->use == PAT_EOF || e->use == PAT_DEFAULT) {
  727.                 if (e->m == SPAWN_ID_ANY || e->m == m) {
  728.                     o->e = e;
  729.                     break;
  730.                 }
  731.             }
  732.         }
  733.         return(status);
  734.     }
  735.  
  736.     /* the top loops are split from the bottom loop only because I can't */
  737.     /* split'em further. */
  738.  
  739.     /* The bufferful condition does not prevent a pattern match from */
  740.     /* occurring and vice versa, so it is scanned with patterns */
  741.     for (i=0, e=ecs_in;i<ecases_inuse;i++,e++) {
  742.         int j;
  743.  
  744.         if (e->use == PAT_TIMEOUT ||
  745.             e->use == PAT_DEFAULT ||
  746.             e->use == PAT_EOF) continue;
  747.  
  748.         /* if m == SPAWN_ID_ANY, then we have not yet read from any */
  749.         /* master, so check every case against its master */
  750.         /* if e->m == ditto, then user is explicitly asking every */
  751.         /* case to be checked against every master */
  752.         if (e->m == SPAWN_ID_ANY) {
  753.             /* test against each spawn_id */
  754.             for (j=0;j<mcount;j++) {
  755.                 status = eval_case_string(e,masters[j],o,last_f,last_case);
  756.                 if (status != EXP_NOMATCH) return(status);
  757.             }
  758.         } else if (m == SPAWN_ID_ANY) {
  759.             /* test against its own spawn_id */
  760.             status = eval_case_string(e,e->m,o,last_f,last_case);
  761.             if (status != EXP_NOMATCH) return(status);
  762.         } else {
  763.             /* reject things immediately from wrong spawn_id */
  764.             if (e->m != m) continue;
  765.  
  766.             status = eval_case_string(e,m,o,last_f,last_case);
  767.             if (status != EXP_NOMATCH) return(status);
  768.         }
  769.     }
  770.     return(EXP_NOMATCH);
  771. }
  772.  
  773. /* This function handles the work of Expect_Before and After depending */
  774. /* upon the first argument */
  775. /*ARGSUSED*/
  776. int
  777. cmdExpectGlobal(clientData, interp, argc, argv)
  778. ClientData clientData;
  779. Tcl_Interp *interp;
  780. int argc;
  781. char **argv;
  782. {
  783.     struct expect_special *e;
  784.  
  785.     if (argc == 2 && strchr(argv[1],'\n')) {
  786.         return(exp_eval_with_one_arg(clientData,argc,argv));
  787.     }
  788.  
  789.     e = (struct expect_special *) clientData;
  790.     return(parse_expect_args(clientData,argc,argv,&e->ecases,
  791.         &e->ecount,&e->masters,&e->mcount,e->me));
  792. }
  793.  
  794. /* adjusts file according to user's size request */
  795. /* return TCL_ERROR or TCL_OK */
  796. int
  797. f_adjust(f)
  798. struct f *f;
  799. {
  800.     int new_msize;
  801.     char *new_buf;
  802.  
  803.     /* get the latest buffer size.  Double the user input for */
  804.     /* two reasons.  1) Need twice the space in case the match */
  805.     /* straddles two bufferfuls, 2) easier to hack the division */
  806.     /* by two when shifting the buffers later on.  The extra  */
  807.     /* byte is just space for a null we can slam on the end.  It */
  808.     /* makes the logic easier later.*/
  809.     new_msize = f->umsize*2 + 1;
  810.     if (new_msize != f->msize) {
  811.         if (!f->buffer) {
  812.             /* allocate buffer space for 1st time */
  813.             f->lower = (char *)malloc((unsigned)new_msize);
  814.             f->buffer = (char *)malloc((unsigned)new_msize);
  815.             if ((!f->buffer) && (!f->lower)) {
  816.                 tcl_error("out of space - failed to malloc initial match buffer");
  817.                 if (f->lower) {free(f->lower); f->lower = 0;}
  818.                 return(TCL_ERROR);
  819.             }
  820.             f->size = 0;
  821.         } else {
  822.             /* buffer already exists - resize */
  823.             if (0 == (new_buf = (char *)realloc(f->buffer,new_msize))) {
  824.                 tcl_error("failed to grow match buf to %d bytes",f->umsize);
  825.                 return(TCL_ERROR);
  826.             }
  827.             f->buffer = new_buf;
  828.             if (0 == (new_buf = (char *)realloc(f->lower,new_msize))) {
  829.                 tcl_error("failed to grow match buf to %d bytes",f->umsize);
  830.                 /* no need to free other buffer */
  831.                 /* - code will still work even if */
  832.                 /* buffer is larger than necessary */
  833.                 return(TCL_ERROR);
  834.             }
  835.             f->lower = new_buf;
  836.             /* if truncated, forget about some data */
  837.             if (f->size >= f->msize) f->size = f->msize-1;
  838.         }
  839.         f->msize = new_msize;
  840.         f->buffer[f->size] = '\0';
  841.     }
  842.     return(TCL_OK);
  843. }
  844.  
  845. /* these should really be local to i_read, however the longjmp could then */
  846. /* clobber them */
  847. #define cc i_read_cc
  848. static int cc;
  849. static int masters_max_out;
  850.  
  851. /* returns # of chars read or (non-positive) error of form EXP_XXX */
  852. int
  853. i_read(masters,masters_max,m,timeout)
  854. int *masters;
  855. int masters_max;
  856. int *m;                /* new master */
  857. double timeout;    /* MDW: Mon Jun 22 13:49:42 1992 */
  858. {
  859.   struct f *f;
  860.   struct timeval tv;    /* MDW: Mon Jun 22 13:49:39 1992 */
  861.  
  862.   cc = EXP_TIMEOUT;
  863.   masters_max_out = 1;        /* way to get address of 1 */
  864.   DoubleToTimeval(timeout,&tv);    /* MDW: Mon Jun 22 13:49:49 1992 */
  865.  
  866.   /* convert fs into ready list */
  867.   /* MDW: Mon Jun 22 13:50:46 1992; used tv instead of timeout */
  868.   if (TCL_ERROR == ready(masters,masters_max,m,&masters_max_out, &tv)) {
  869.     return(EXP_TCLERROR);
  870.   }
  871.   if (masters_max_out == 0) return(EXP_TIMEOUT); /* timed out */
  872.   
  873.   f = fs + *m;
  874.  
  875.   /* when buffer fills, copy second half over first and */
  876.   /* continue, so we can do matches over multiple buffers */
  877.   if (f->size == f->msize) {
  878.     int half = f->size/2;
  879.     memcpy(f->buffer,f->buffer+half,half);
  880.     memcpy(f->lower, f->lower +half,half);
  881.     f->size = half;
  882.   }
  883.   {    /* MDW: Mon Jun 22 13:52:38 1992 */
  884.     char *buf = f->buffer + f->size;
  885.     int n, size = f->msize-f->size;
  886.     if (SetSessionFromFD(*m))
  887.       cc = HandleSessionTerminal(buf, size, timeout);
  888.     else {
  889.       debuglog("read(fd=%d,buffer=%x,length=%d)",
  890.            *m,f->buffer+f->size, f->msize-f->size);
  891.       if (SetAlarm(timeout)) {
  892.     cc = read(*m, buf, size);
  893.     i_read_errno = errno;    /* errno can be overwritten by the */
  894.     /* time we return */
  895.       }
  896.       ClearAlarm();
  897.       debuglog("= %d, errno  = %d\r\n",cc, i_read_errno);
  898.     }
  899.   }
  900.   return(cc);
  901. }
  902.  
  903. /* variables predefined by expect are retrieved using this routine
  904. which looks in the global space if they are not in the local space.
  905. This allows the user to localize them if desired, and also to
  906. avoid having to put "global" in procedure definitions.
  907. */
  908. char *
  909. get_var(var)
  910. char *var;
  911. {
  912.     char *val;
  913.  
  914.     if (NULL != (val = Tcl_GetVar(interp,var,0 /* local */)))
  915.         return(val);
  916.     return(Tcl_GetVar(interp,var,TCL_GLOBAL_ONLY));
  917. }
  918.  
  919. double    /* MDW: Mon Jun 22 13:55:46 1992 */
  920. get_timeout()
  921. {
  922.     char *t;
  923.  
  924.     if (NULL != (t = get_var(EXPECT_TIMEOUT))) {
  925.       extern double atof();    /* MDW: Mon Jun 22 13:55:43 1992 */
  926.       timeout = atof(t);
  927.     }
  928.     return(timeout);
  929. }
  930.  
  931. void
  932. init_expect()
  933. {
  934.     Tcl_SetVar(interp,EXPECT_TIMEOUT,    INIT_EXPECT_TIMEOUT,0);
  935.     Tcl_SetVar(interp,SPAWN_ID_ANY_VARNAME,    SPAWN_ID_ANY_LIT,0);
  936.  
  937.     {
  938.         static void (*sigfuncs[32])();
  939.         init_expect_alarms(sigfuncs, (1<<SIGALRM)|(1<<SIGINT));    
  940.         /*
  941.         extern void sigalarm_handler(), sigint_handler();
  942.         signal(SIGALRM,sigalarm_handler);
  943.         signal(SIGINT,sigint_handler); 
  944.         */
  945.       }
  946. }
  947.  
  948. double WaitTime(delta0, masters, mcount, ecases, ecases_inuse) 
  949. int *masters;
  950. int mcount;
  951. struct ecase *ecases;
  952. int ecases_inuse;
  953. double delta0;
  954. {
  955.   static struct timeval tv = {0, 0};
  956.   double delta;
  957.   int m, i;
  958.   struct ecase *e;
  959.   extern int fd_max;
  960.   double qrem;
  961.  
  962.   delta = delta0;
  963.   for (m=0; m<mcount; ++m) {
  964.     struct f *f = fd_to_f(masters[m]);
  965.     for (i=0, e= ecases;i<ecases_inuse;i++,e++) 
  966.       if (e->m == SPAWN_ID_ANY || e->m == masters[m]) {
  967.     if (e->quiet != -1.0) {
  968.       qrem = e->quiet - (f->quiet_end - f->quiet_start);
  969.       if (qrem >= 0.0 && qrem < delta) delta = qrem;
  970.     }
  971.     if (e->quiet_total != -1.0) {
  972.       qrem = e->quiet_total - (f->quiet_end - f->quiet_total_start);
  973.       if (qrem >= 0.0 && qrem < delta) delta = qrem;
  974.     }
  975.     if (e->quiet_after_noise != -1.0) {
  976.       qrem = e->quiet_after_noise - (f->quiet_end - f->quiet_after_noise_start);
  977.       if (qrem >= 0.0 && qrem < delta) delta = qrem;
  978.     }
  979.       }
  980.     for (i=0, e= (before.ecases);i<before.mcount;i++,e++) 
  981.       if (e->m == SPAWN_ID_ANY || e->m == masters[m]) {
  982.     if (e->quiet != -1.0) {
  983.       qrem = e->quiet - (f->quiet_end - f->quiet_start);
  984.       if (qrem >= 0.0 && qrem < delta) delta = qrem;
  985.     }
  986.     if (e->quiet_total != -1.0) {
  987.       qrem = e->quiet_total - (f->quiet_end - f->quiet_total_start);
  988.       if (qrem >= 0.0 && qrem < delta) delta = qrem;
  989.     }
  990.     if (e->quiet_after_noise != -1.0) {
  991.       qrem = e->quiet_after_noise - (f->quiet_end - f->quiet_after_noise_start);
  992.       if (qrem >= 0.0 && qrem < delta) delta = qrem;
  993.     }
  994.       }
  995.     for (i=0, e= (after.ecases);i<after.mcount;i++,e++)
  996.       if (e->m == SPAWN_ID_ANY || e->m == masters[m]) {
  997.     if (e->quiet != -1.0) {
  998.       qrem = e->quiet - (f->quiet_end - f->quiet_start);
  999.       if (qrem >= 0.0 && qrem < delta) delta = qrem;
  1000.     }
  1001.     if (e->quiet_total != -1.0) {
  1002.       qrem = e->quiet_total - (f->quiet_end - f->quiet_total_start);
  1003.       if (qrem >= 0.0 && qrem < delta) delta = qrem;
  1004.     }
  1005.     if (e->quiet_after_noise != -1.0) {
  1006.       qrem = e->quiet_after_noise - (f->quiet_end - f->quiet_after_noise_start);
  1007.       if (qrem >= 0.0 && qrem < delta) delta = qrem;
  1008.     }
  1009.       }
  1010.   }
  1011.   if (delta <= 0.0) delta = delta0;
  1012.   return(delta);
  1013. }
  1014.  
  1015.  
  1016. /*ARGSUSED*/
  1017. int
  1018. cmdExpect(clientData, interp, argc, argv)
  1019. ClientData clientData;
  1020. Tcl_Interp *interp;
  1021. int argc;
  1022. char **argv;
  1023. {
  1024.   int cc;            /* number of chars returned in a single read */
  1025.   /* or negative EXP_whatever */
  1026.   int oldrc;
  1027.   int m;            /* before doing an actual read, attempt */
  1028.   /* to match upon any spawn_id */
  1029.   struct f *f;            /* file associated with master */
  1030.  
  1031.   int i;            /* trusty temporary */
  1032.   struct ecase *ecases;
  1033.   int ecases_inuse;        /* number of ecases to use */
  1034.   int *masters;            /* array of masters to watch */
  1035.   int mcount;            /* number of masters to watch */
  1036.  
  1037.   struct eval_out eo;        /* final case of interest */
  1038.  
  1039.   int result;            /* Tcl result */
  1040.  
  1041.   /* time_t current_time = 0;*/ /* current time (when we last looked)*/
  1042.   /* time_t end_time;    */    /* future time at which to give up */ 
  1043.   double end_time = -1.0;    /* MDW: Mon Jun 22 13:58:00 1992 */
  1044.  
  1045.   struct f *last_f;        /* for differentiating when multiple f's */
  1046.   /* to print out better debugging messages */
  1047.   int last_case;        /* as above but for case */
  1048.   int do_flush;            /* if anything queued up, should be flushed */
  1049.  
  1050.   /* MDW: Mon Jun 22 13:58:32 1992 */
  1051.   int master = -1;        /* Set to the current master for terminal updating */
  1052.  
  1053.   if (argc == 2 && strchr(argv[1],'\n')) {
  1054.     return(exp_eval_with_one_arg(clientData,argc,argv));
  1055.   }
  1056.  
  1057.  restart:
  1058.  
  1059.   if (!update_master(&master)) return(TCL_ERROR); /* MDW: Mon Jun 22 13:59:23 1992 */
  1060.  
  1061.   cc = EXP_NOMATCH;
  1062.   m = SPAWN_ID_ANY;
  1063.   ecases = 0;
  1064.   masters = 0;
  1065.   mcount = 0;
  1066.   result = TCL_OK;
  1067.   last_f = 0;
  1068.   do_flush = FALSE;
  1069.   /* end of restart code */
  1070.  
  1071.   eo.e = 0;            /* no final case yet */
  1072.   eo.f = 0;            /* no final file selected yet */
  1073.   eo.match = 0;            /* nothing matched yet */
  1074.  
  1075.   /* get the latest timeout */
  1076.   (void) get_timeout();
  1077.  
  1078.   /* make arg list for processing cases */
  1079.   /* do it dynamically, since expect can be called recursively */
  1080.   if (TCL_ERROR == parse_expect_args(clientData,argc,argv,
  1081.                      &ecases,&ecases_inuse,
  1082.                      &masters,&mcount,
  1083.                      EXP_DURING)) return(TCL_ERROR);
  1084.  
  1085.   /* if anything queued up unwritten (from interact), flush it */
  1086.   for (i=0;i<mcount;i++) {
  1087.     int write_count;
  1088.  
  1089.     if (!(f = fd_to_f(masters[i],"expect"))) return(TCL_ERROR);
  1090.     if (!f->buffer) continue;
  1091.  
  1092.     if (f->size) write_count = f->size - f->printed;
  1093.     else write_count = 0;
  1094.  
  1095.     if (write_count) {
  1096.       do_flush = TRUE;
  1097.  
  1098.       /* This logic is identical to what happens */
  1099.       /* after a read() below */
  1100.  
  1101.       if (logfile_all || (loguser && logfile)) {
  1102.     fwrite(f->buffer + f->printed,1,write_count,
  1103.            logfile);
  1104.       }
  1105.       /* don't write to user if they're seeing it already, */
  1106.       /* that is, typing it! */
  1107.       if (loguser && (m != 0)) { /* MDW: Mon Jun 22 14:02:39 1992 */
  1108.     if (SetSessionFromFD(master)) { /* MDW: Tue May 19 10:55:12 1992 */
  1109.       if (master == masters[i]) UpdateWindow(NULL);    /* MDW: Mon Jun 22 14:02:43 1992 */
  1110.     }
  1111.     else fwrite(f->buffer + f->printed, 1,write_count,stdout);
  1112.       }
  1113.       if (debugfile) fwrite(f->buffer + f->printed,
  1114.                 1,write_count,debugfile);
  1115.       f->printed = 0;
  1116.     }
  1117.   }
  1118.  
  1119.   /* if we wrote to any logs, flush them */
  1120.   if (do_flush && ((loguser && ((m!= 0) || logfile))
  1121.            || debugfile
  1122.            || logfile_all)) {
  1123.     flush_streams();
  1124.   }
  1125.  
  1126.   for (;;) {
  1127.     double now = TimeOfDay();
  1128.     double endtime = (end_time == -1.0) ? (end_time = (now + timeout)) : end_time;
  1129.     double delta0 = (timeout == TIME_INFINITY) ? 1000000.0 : endtime - now;
  1130.     double delta;
  1131.     for (i=0;i<mcount;i++) {
  1132.       if (!(f = fd_to_f(masters[i],"expect"))) return(TCL_ERROR);
  1133. #ifdef DEBUG_QUIET    
  1134.       fprintf(stderr,"Before0 Expect_read:\t\t\tFD: %d, Quiet Start: %f, End: %f, Delta: %f\n", masters[i], f->quiet_start, f->quiet_end, (f->quiet_end-f->quiet_start));
  1135.       fflush(stderr);
  1136. #endif
  1137.       f->quiet_end = now;
  1138.       if (f->quiet_start == 0.0) f->quiet_start = now;
  1139.       if (f->quiet_total_start == 0.0) f->quiet_total_start = now;
  1140.       if (m == SPAWN_ID_ANY) {
  1141.     f->quiet_start = now;
  1142.     f->quiet_after_noise_start = 0.0;
  1143.       }
  1144.       else if (CheckInput(masters[i],0.0,TRUE))
  1145.     f->quiet_after_noise_start = f->quiet_start = f->quiet_total_start= f->quiet_end;
  1146. #ifdef DEBUG_QUIET    
  1147.       fprintf(stderr,"Before Expect_read:\t\t\tFD: %d, Quiet Start: %f, End: %f, Delta: %f\n", masters[i], f->quiet_start, f->quiet_end, (f->quiet_end-f->quiet_start));
  1148.       fflush(stderr);
  1149. #endif
  1150.     }
  1151.     /* if first time thru, try to match against */
  1152.     /* old buffered data before reading more */
  1153.     if (m != SPAWN_ID_ANY) {
  1154.       /* MDW: Mon Jun 22 14:05:52 1992 */
  1155.       delta = WaitTime(delta0, masters, mcount, ecases, ecases_inuse);
  1156.       if (delta <= 0.0) cc = EXP_TIMEOUT; 
  1157.       else {
  1158. #ifdef DEBUG_QUIET
  1159.     fprintf(stderr,"Expect_read(%f)\n", delta); fflush(stderr);
  1160. #endif
  1161.     cc = i_read(masters,mcount,&m, delta);
  1162.     now = TimeOfDay();
  1163.     for (i=0;i<mcount;i++) {
  1164.       if (!(f = fd_to_f(masters[i],"expect"))) return(TCL_ERROR);
  1165.       f->quiet_end =now;
  1166.       if (CheckInput(masters[i],0.0,TRUE) || (m == masters[i] && f->quiet_start != f->quiet_end))
  1167.         f->quiet_after_noise_start = f->quiet_start = f->quiet_total_start= f->quiet_end;
  1168. #ifdef DEBUG_QUIET
  1169.       fprintf(stderr,"After Expect_read(%f)=> %d:\tFD: %d, Quiet Start: %f, End: %f, Delta: %f\n", delta, cc, masters[i], f->quiet_start, f->quiet_end, (f->quiet_end-f->quiet_start));
  1170.       fflush(stderr);
  1171. #endif
  1172.     }
  1173.     if ((delta != delta0) && cc == EXP_TIMEOUT) { 
  1174.       eo.e = 0;
  1175.       cc = EXP_NOMATCH; 
  1176.       m = SPAWN_ID_ANY;
  1177.     }
  1178.       }                /* MDW: Mon Jun 22 14:06:02 1992 */
  1179.  
  1180.       if (cc == EXP_EOF) {    /* normal EOF */
  1181.       } else if (cc == EXP_ABEOF) { /* abnormal EOF */
  1182.     /* On many systems, ptys produce EIO upon EOF - sigh */
  1183.     if (i_read_errno == EIO) {
  1184.       /* Sun, Cray, BSD, and others */
  1185.       cc = EXP_EOF;
  1186.     } else if (i_read_errno == 0) {
  1187.       /* AIX */
  1188.       cc = EXP_EOF;
  1189.     } else {
  1190.       if (i_read_errno == EBADF) {
  1191.         tcl_error("bad spawn_id (process died earlier?)");
  1192.       } else {
  1193.         tcl_error("i_read(spawn_id=%d): %s",
  1194.               m,sys_errlist[errno]);
  1195.         fd_close(m);
  1196.       }
  1197.       goto error;
  1198.     }
  1199.       } else if (cc == EXP_TIMEOUT) {
  1200.     debuglog("expect: timed out\r\n");
  1201.       } else if (cc == EXP_TCLERROR) {
  1202.     goto error;
  1203.       } else if (cc == EXP_NOMATCH) { /* MDW: Mon Jun 22 14:08:22 1992 */
  1204.       } else {
  1205.     /* got data! */
  1206.  
  1207.     f = fs + m;
  1208.     oldrc = f->size;
  1209.     f->size += cc;
  1210.  
  1211.     if (logfile_all || (loguser && logfile)) {
  1212.       fwrite(f->buffer + oldrc,1,cc,logfile);
  1213.     }
  1214.     /* don't write to user if they are seeing it */
  1215.     /* already, that is, typing it! */
  1216.     if (loguser && (m != 0))
  1217.       if (SetSessionFromFD(master)) { /* MDW: Tue May 19 10:55:12 1992 */
  1218.         if (master == m) UpdateWindow(NULL);
  1219.       }
  1220.       else fwrite(f->buffer + oldrc,1,cc,stdout);
  1221.     if (debugfile)
  1222.       fwrite(f->buffer + oldrc,1,cc,debugfile);
  1223.  
  1224.     /* if we wrote to any logs, flush them */
  1225.     if (   (loguser && ((m!= 0) || logfile))
  1226.         || debugfile
  1227.         || logfile_all) {
  1228.       flush_streams();
  1229.     }
  1230.  
  1231.     /* remove nulls from input, since there is no way */
  1232.     /* for Tcl to deal with such strings.  Doing it here */
  1233.     /* lets them be sent to the screen, just in case */
  1234.     /* they are involved in formatting operations */
  1235.     f->size -= rm_nulls(f->buffer + oldrc,cc);
  1236.     f->printed = f->size;    /* count'm even if not logging */
  1237.     f->buffer[f->size] = '\0';
  1238.     /* below here, cc as general status */
  1239.     cc = EXP_NOMATCH;
  1240.  
  1241.     /* copy to lowercase buffer */
  1242.     lowmemcpy(f->lower+oldrc,f->buffer+oldrc,1 + f->size - oldrc);
  1243.  
  1244.     /* force redisplay of buffer when debugging */
  1245.     last_f = 0;
  1246.       }
  1247.     }
  1248.  
  1249.     cc = eval_cases(before.ecases,before.ecount,
  1250.             m,&eo,&last_f,&last_case,cc,masters,mcount);
  1251.     cc = eval_cases(ecases,ecases_inuse,
  1252.             m,&eo,&last_f,&last_case,cc,masters,mcount);
  1253.     cc = eval_cases(after.ecases,after.ecount,
  1254.             m,&eo,&last_f,&last_case,cc,masters,mcount);
  1255.     if (cc == EXP_TCLERROR) goto error;
  1256.     /* special eof code that cannot be done in eval_cases */
  1257.     /* or above, because it would then be executed several times */
  1258.     if (cc == EXP_EOF) {
  1259.       eo.f = fs + m;
  1260.       eo.match = eo.f->size;
  1261.       eo.buffer = eo.f->buffer;
  1262.       debuglog("expect: read eof\r\n");
  1263.       break;
  1264.     } else if (cc == EXP_TIMEOUT) break;
  1265.     /* break out if timeout or eof and failed to find a case for it */
  1266.  
  1267.     if (eo.e) break;
  1268.  
  1269.     /* if we've not yet actually read anything, don't update time */
  1270.     /* this forces at least one read to be done if timeout > 0 */
  1271.     /* MDW: Mon Jun 22 14:11:08 1992
  1272.        if (m != SPAWN_ID_ANY)
  1273.        if (timeout != TIME_INFINITY) time(¤t_time);
  1274.        */
  1275.     m = 0;            /* as long as we assign m any non-SPAWN_ID_ANY value */
  1276.     /* we will be assured that the next time through the loop */
  1277.     /* it will indeed try to ready from all the masters */
  1278.     /* At that time, m will be given a new value */
  1279.   }
  1280.  
  1281.   goto done;
  1282.  
  1283.  error:
  1284.   result = TCL_ERROR;
  1285.  done:
  1286.   if (result != TCL_ERROR) {
  1287.     char *body = 0;
  1288.     char *buffer;        /* pointer to normal or lowercased data */
  1289.     struct ecase *e = 0;    /* points to current ecase */
  1290.     int match = -1;        /* characters matched */
  1291.     char match_char;        /* place to hold char temporarily */
  1292.     /* uprooted by a NULL */
  1293.  
  1294.     if (eo.e) {
  1295.       e = eo.e;
  1296.       eo.e = NULL;
  1297.       body = e->body;
  1298.       if (e->use != PAT_TIMEOUT) {
  1299.     f = eo.f;
  1300.     match = eo.match;
  1301.     buffer = eo.buffer;
  1302.       }
  1303.     } else if (cc == EXP_EOF) {
  1304.       /* read an eof but no user-supplied case */
  1305.       f = eo.f;
  1306.       match = eo.match;
  1307.       buffer = eo.buffer;
  1308.     }            
  1309.  
  1310.     if (match >= 0) {
  1311.       char name[20], value[20];
  1312.       char *str;
  1313. #define out(i,val)  debuglog("expect: set %s(%s) {%s}\r\n",EXPECT_OUT,i, \
  1314.                  dprintify(val)); \
  1315.                    if (!Tcl_SetVar2(interp,EXPECT_OUT,i,val,0)) \
  1316.                  {result = TCL_ERROR; goto cleanup;}
  1317.  
  1318.     if (e && e->use == PAT_RE) {
  1319.       for (i=0;i<NSUBEXP;i++) {
  1320.     int offset;
  1321.  
  1322.     if (e->re->startp[i] == 0) break;
  1323.  
  1324.     /* start index */
  1325.     sprintf(name,"%d,start",i);
  1326.     offset = e->re->startp[i]-buffer;
  1327.     sprintf(value,"%d",offset);
  1328.     out(name,value);
  1329.  
  1330.     /* start row and col CJM*/
  1331.     if (SetSessionFromFD(master)) {
  1332.       extern int get_start_row();
  1333.       extern int get_start_col();
  1334.                       
  1335.       sprintf(name,"%d,row",i);
  1336.       sprintf(value,"%d",get_start_row(&(e->scr),e->re->startp[i]-buffer));
  1337.       out(name,value);
  1338.       dprintify(value);
  1339.                       
  1340.       sprintf(name,"%d,col",i);
  1341.       sprintf(value,"%d",get_start_col(&(e->scr),e->re->startp[i]-buffer));
  1342.       out(name,value);
  1343.       dprintify(value);
  1344.     }
  1345.  
  1346.     /* end index */
  1347.     sprintf(name,"%d,end",i);
  1348.     sprintf(value,"%d",
  1349.         e->re->endp[i]-buffer-1);
  1350.     out(name,value);
  1351.  
  1352.     /* string itself */
  1353.     sprintf(name,"%d,string",i);
  1354.     /* str = f->buffer + offset; */
  1355.     str = buffer + offset;    /* MDW: Mon Jun 22 14:15:23 1992 */
  1356.     /* temporarily null-terminate in */
  1357.     /* middle */
  1358.     match_char = *e->re->endp[i];
  1359.     *e->re->endp[i] = 0;
  1360.     out(name,e->re->startp[i]);
  1361.     *e->re->endp[i] = match_char;
  1362.       }
  1363.       /* redefine length of string that */
  1364.       /* matched for later extraction */
  1365.       match = e->re->endp[0]-buffer;
  1366.     } else if (e && e->use == PAT_GLOB) {
  1367.       /* start index */
  1368.       sprintf(value,"%d",e->glob_start);
  1369.       out("0,start",value);
  1370.                 
  1371.       /* start row and col CJM */
  1372.       if (SetSessionFromFD(master)) {
  1373.     extern int get_start_row();
  1374.     extern int get_start_col();
  1375.                   
  1376.     sprintf(value,"%d",get_start_row(&(e->scr),e->glob_start));
  1377.     out("0,row",value);
  1378.     dprintify(value);
  1379.                   
  1380.     sprintf(value,"%d",get_start_col(&(e->scr),e->glob_start));
  1381.     out("0,col",value);
  1382.     dprintify(value);
  1383.       }
  1384.       
  1385.       /* end index */
  1386.       sprintf(value,"%d",e->glob_start + match - 1);
  1387.       out("0,end",value);
  1388.  
  1389.       /* string itself */
  1390.       /* str = f->buffer + e->glob_start; */
  1391.       str = buffer + e->glob_start; /* MDW: Mon Jun 22 14:15:38 1992 */
  1392.       /* temporarily null-terminate in middle */
  1393.       match_char = str[match];
  1394.       str[match] = 0;
  1395.       out("0,string",str);
  1396.       str[match] = match_char;
  1397.  
  1398.       /* redefine length of string that */
  1399.       /* matched for later extraction */
  1400.       match += e->glob_start;
  1401.     } else if (e && e->use == PAT_FULLBUFFER) {
  1402.       debuglog("expect: full buffer\r\n");
  1403.     }
  1404.   }
  1405.  
  1406.   /* this is broken out of (match > 0) (above) since it can */
  1407.   /* that an EOF occurred with match == 0 */
  1408.   if (eo.f) {
  1409.     char spawn_id[10];        /* enough for a %d */
  1410.  
  1411.     sprintf(spawn_id,"%d",f-fs);
  1412.     out("spawn_id",spawn_id);
  1413.  
  1414.     /* save buf[0..match] */
  1415.     /* temporarily null-terminate string in middle */
  1416.     /* match_char = f->buffer[match]; */
  1417.     match_char = buffer[match];    /* MDW: Mon Jun 22 14:17:23 1992 */
  1418.     /* f->buffer[match] = 0; */
  1419.     buffer[match] = 0;
  1420.     out("buffer",buffer);
  1421.     /* remove middle-null-terminator */
  1422.     /* f->buffer[match] = match_char; */
  1423.     buffer[match] = match_char;    /* MDW: Mon Jun 22 14:18:42 1992 */
  1424.  
  1425.     /* "!e" means no case matched - transfer by default */
  1426. /*    if (!e || (e->scr.match_type == SCR_SERIAL && e->transfer)) { */
  1427.     if (e && e->scr.match_type == SCR_SERIAL && e->transfer) {
  1428.       /* delete matched chars from input buffer */
  1429.       f->size -= match;
  1430.       f->printed -= match;
  1431.       if (f->size > 0) {    /* MDW: Mon Jun 22 14:29:03 1992 */
  1432.     memcpy(f->buffer,f->buffer+match,f->size);
  1433.     memcpy(f->lower,f->lower+match,f->size);
  1434.       }
  1435.       if (f->size >= 0) {    /* MDW: Mon Jun 22 14:29:06 1992 */
  1436.     f->buffer[f->size] = '\0';
  1437.     f->lower[f->size] = '\0';
  1438.       }
  1439.     }
  1440.  
  1441.     if (cc == EXP_EOF) fd_close(f - fs);
  1442.  
  1443.   }
  1444.  
  1445.   if (body) result = Tcl_Eval(interp,body,0,(char **) NULL);
  1446. }
  1447.  
  1448.  cleanup:
  1449.     free_ecases(ecases,ecases_inuse,EXP_DURING);
  1450.     if (masters) free((char *)masters);
  1451.  
  1452.     if (result == TCL_CONTINUE_EXPECT) {
  1453.         debuglog("expect: continuing expect\r\n");
  1454.         goto restart;
  1455.     }
  1456.  
  1457.     UpdateWindow(NULL);
  1458.     return(result);
  1459. }
  1460.  
  1461. /* lowmemcpy - like memcpy but it lowercases result */
  1462. void
  1463. lowmemcpy(dest,src,n)
  1464. char *dest;
  1465. char *src;
  1466. int n;
  1467. {
  1468.     for (;n>0;n--) {
  1469.         *dest = (isupper(*src)?tolower(*src):*src);
  1470.         src++;    dest++;
  1471.     }
  1472. }
  1473.  
  1474. /* The following functions implement expect's glob-style string matching */
  1475. /* Exp_StringMatch allow's implements the unanchored front (or conversely */
  1476. /* the '^') feature.  Exp_StringMatch2 does the rest of the work. */
  1477. int    /* returns # of chars that matched */
  1478. Exp_StringMatch(string, pattern,offset)
  1479. char *string;
  1480. char *pattern;
  1481. int *offset;    /* offset from beginning of string where pattern matches */
  1482. {
  1483.     char *s;
  1484.     int sm;    /* count of chars matched or -1 */
  1485.     int caret = FALSE;
  1486.  
  1487.     *offset = 0;
  1488.  
  1489.     if (pattern[0] == '^') {
  1490.         caret = TRUE;
  1491.         pattern++;
  1492.     }
  1493.  
  1494.     sm = Exp_StringMatch2(string,pattern);
  1495.     if (sm >= 0) return(sm);
  1496.  
  1497.     if (caret) return(-1);
  1498.  
  1499.     if (pattern[0] == '*') return(-1);
  1500.  
  1501.     for (s = string;*s;s++) {
  1502.          sm = Exp_StringMatch2(s,pattern);
  1503.         if (sm != -1) {
  1504.             *offset = s-string;
  1505.             return(sm);
  1506.         }
  1507.     }
  1508.     return(-1);
  1509. }
  1510.  
  1511. /* Exp_StringMatch2 --
  1512.  
  1513. Like Tcl_StringMatch except that
  1514. 1) returns number of characters matched, -1 if failed.
  1515.     (Can return 0 on patterns like "" or "$")
  1516. 2) does not require pattern to match to end of string
  1517. 3) Original code is stolen from Tcl_StringMatch
  1518. */
  1519.  
  1520. int Exp_StringMatch2(string,pattern)
  1521.     register char *string;    /* String. */
  1522.     register char *pattern;    /* Pattern, which may contain
  1523.                  * special characters. */
  1524. {
  1525.     char c2;
  1526.     int match = 0;    /* # of chars matched */
  1527.  
  1528.     while (1) {
  1529.     /* See if we're at the end of both the pattern and the string.
  1530.      * If so, we succeeded.  If we're at the end of the pattern
  1531.      * but not at the end of the string, we failed.
  1532.      */
  1533.     
  1534.     if (*pattern == 0) {
  1535.         /* removed test for end of string - DEL */
  1536.         return match;
  1537.     }
  1538.  
  1539.     if ((*string == 0) && (*pattern != '*')) {
  1540.         return -1;
  1541.     }
  1542.  
  1543.     /* Check for a "*" as the next pattern character.  It matches
  1544.      * any substring.  We handle this by calling ourselves
  1545.      * recursively for each postfix of string, until either we
  1546.      * match or we reach the end of the string.
  1547.      */
  1548.     
  1549.     if (*pattern == '*') {
  1550.         pattern += 1;
  1551.         if (*pattern == 0) {
  1552.         return(strlen(string)+match); /* DEL */
  1553.         }
  1554.         while (*string != 0) {
  1555.         int rc;                    /* DEL */
  1556.  
  1557.         if (-1 != (rc = Exp_StringMatch2(string, pattern))) {
  1558.             return rc+match;        /* DEL */
  1559.         }
  1560.         string += 1;
  1561.         match++;                /* DEL */
  1562.         }
  1563.         return -1;                    /* DEL */
  1564.     }
  1565.     
  1566.     /* Check for a "?" as the next pattern character.  It matches
  1567.      * any single character.
  1568.      */
  1569.  
  1570.     if (*pattern == '?') {
  1571.         goto thisCharOK;
  1572.     }
  1573.  
  1574.     /* Check for a "[" as the next pattern character.  It is followed
  1575.      * by a list of characters that are acceptable, or by a range
  1576.      * (two characters separated by "-").
  1577.      */
  1578.     
  1579.     if (*pattern == '[') {
  1580.         pattern += 1;
  1581.         while (1) {
  1582.         if ((*pattern == ']') || (*pattern == 0)) {
  1583.             return 0;
  1584.         }
  1585.         if (*pattern == *string) {
  1586.             break;
  1587.         }
  1588.         if (pattern[1] == '-') {
  1589.             c2 = pattern[2];
  1590.             if (c2 == 0) {
  1591.             return -1;        /* DEL */
  1592.             }
  1593.             if ((*pattern <= *string) && (c2 >= *string)) {
  1594.             break;
  1595.             }
  1596.             if ((*pattern >= *string) && (c2 <= *string)) {
  1597.             break;
  1598.             }
  1599.             pattern += 2;
  1600.         }
  1601.         pattern += 1;
  1602.         }
  1603.         while ((*pattern != ']') && (*pattern != 0)) {
  1604.         pattern += 1;
  1605.         }
  1606.         goto thisCharOK;
  1607.     }
  1608.     
  1609.     /* If the last pattern character is '$', verify that the entire
  1610.      * string has been matched. - DEL 
  1611.      */
  1612.  
  1613.     if ((*pattern == '$') && (pattern[1] == 0)) {
  1614.         if (*string == 0) return(0);
  1615.         else return(-1);        
  1616.     }
  1617.  
  1618.     /* If the next pattern character is '/', just strip off the '/'
  1619.      * so we do exact matching on the character that follows.
  1620.      */
  1621.     
  1622.     if (*pattern == '\\') {
  1623.         pattern += 1;
  1624.         if (*pattern == 0) {
  1625.         return -1;
  1626.         }
  1627.     }
  1628.  
  1629.     /* There's no special character.  Just make sure that the next
  1630.      * characters of each string match.
  1631.      */
  1632.     
  1633.     if (*pattern != *string) {
  1634.         return -1;
  1635.     }
  1636.  
  1637.     thisCharOK: pattern += 1;
  1638.     string += 1;
  1639.     match++;
  1640.     }
  1641. }
  1642.  
  1643.  
  1644. /* Tcl statements to execute upon various signals */
  1645. /* Each is handled by the same "generic_sighandler (below)" which */
  1646. /* looks them up here */
  1647. struct {    /* one per signal */
  1648.     char *action;        /* Tcl command to execute upon sig */
  1649.     char *name;        /* name of C macro */
  1650. } signals[NSIG];
  1651.  
  1652. void
  1653. init_trap()
  1654. {
  1655.     int i;
  1656.  
  1657.     for (i=0;i<NSIG;i++) {
  1658.         signals[i].name = 0;
  1659.         signals[i].action = 0;
  1660.     }
  1661.  
  1662.     /* defined by C standard */
  1663. #if defined(SIGABRT)
  1664.     /* unbelievable but some systems don't support this (e.g. SunOS 3.5) */
  1665.     signals[SIGABRT].name = "SIGABRT";
  1666. #endif
  1667.     signals[SIGFPE ].name = "SIGFPE";
  1668.     signals[SIGILL ].name = "SIGILL";
  1669.     signals[SIGINT ].name = "SIGINT";
  1670.     signals[SIGSEGV].name = "SIGSEGV";
  1671.     signals[SIGTERM].name = "SIGTERM";
  1672.  
  1673.     /* our own extension */
  1674.     signals[0].name = "ONEXIT";
  1675.  
  1676.     /* nonstandard but common */
  1677. #if defined(SIGHUP)        /* hangup */
  1678.     signals[SIGHUP ].name = "SIGHUP";
  1679. #endif
  1680. #if defined(SIGQUIT)        /* quit */
  1681.     signals[SIGQUIT].name = "SIGQUIT";
  1682. #endif
  1683. #if defined(SIGTRAP)        /* trace trap (not reset when caught) */
  1684.     signals[SIGTRAP].name = "SIGTRAP";
  1685. #endif
  1686. #if defined(SIGIOT)        /* IOT instruction */
  1687.     signals[SIGIOT ].name = "SIGIOT";
  1688. #endif
  1689. #if defined(SIGEMT)        /* EMT instruction */
  1690.     signals[SIGEMT ].name = "SIGEMT";
  1691. #endif
  1692. #if defined(SIGKILL)        /* kill (cannot be caught or ignored) */
  1693.     signals[SIGKILL].name = "SIGKILL";
  1694. #endif
  1695. #if defined(SIGBUS)        /* bus error */
  1696.     signals[SIGBUS ].name = "SIGBUS";
  1697. #endif
  1698. #if defined(SIGSYS)        /* bad argument to system call */
  1699.     signals[SIGSYS ].name = "SIGSYS";
  1700. #endif
  1701. #if defined(SIGPIPE)        /* write on a pipe with no one to read it */
  1702.     signals[SIGPIPE].name = "SIGPIPE";
  1703. #endif
  1704. #if defined(SIGALRM)        /* alarm clock */
  1705.     signals[SIGALRM].name = "*SIGALRM";
  1706. #endif
  1707. #if defined(SIGCLD)        /* Like SIGCHLD.  */
  1708.     signals[SIGCLD ].name = "SIGCLD";
  1709. #endif
  1710. #if defined(SIGPWR)        /* imminent power failure */
  1711.     signals[SIGPWR ].name = "SIGPWR";
  1712. #endif
  1713. #if defined(SIGPOLL)        /* For keyboard input?  */
  1714.     signals[SIGPOLL].name = "SIGPOLL";
  1715. #endif
  1716. #if defined(SIGURG)        /* urgent condition on IO channel */
  1717.     signals[SIGURG ].name = "SIGURG";
  1718. #endif
  1719. #if defined(SIGSTOP)        /* sendable stop signal not from tty */
  1720.     signals[SIGSTOP].name = "SIGSTOP";
  1721. #endif
  1722. #if defined(SIGTSTP)        /* stop signal from tty */
  1723.     signals[SIGTSTP].name = "SIGTSTP";
  1724. #endif
  1725. #if defined(SIGCONT)        /* continue a stopped process */
  1726.     signals[SIGCONT].name = "SIGCONT";
  1727. #endif
  1728. #if defined(SIGCHLD)        /* to parent on child stop or exit */
  1729.     signals[SIGCHLD].name = "SIGCHLD";
  1730. #endif
  1731. #if defined(SIGTTIN)        /* to readers pgrp upon background tty read */
  1732.     signals[SIGTTIN].name = "SIGTTIN";
  1733. #endif
  1734. #if defined(SIGTTOU)        /* like TTIN for output if (tp->t_local<OSTOP) */
  1735.     signals[SIGTTOU].name = "SIGTTOU";
  1736. #endif
  1737. #if defined(SIGIO)        /* input/output signal */
  1738.     signals[SIGIO  ].name = "SIGIO";
  1739. #endif
  1740. #if defined(SIGXCPU)        /* exceeded CPU time limit */
  1741.     signals[SIGXCPU].name = "SIGXCPU";
  1742. #endif
  1743. #if defined (SIGXFSZ)        /* exceeded file size limit */
  1744.     signals[SIGXFSZ].name = "SIGXFSZ";
  1745. #endif
  1746. #if defined(SIGVTALRM)        /* virtual time alarm */
  1747.     signals[SIGVTALRM].name = "SIGVTALRM";
  1748. #endif
  1749. #if defined(SIGPROF)        /* profiling time alarm */
  1750.     signals[SIGPROF].name = "SIGPROF";
  1751. #endif
  1752. #if defined(SIGWINCH)        /* window changed */
  1753.     signals[SIGWINCH].name = "SIGWINCH";
  1754. #endif
  1755. #if defined(SIGLOST)        /* resource lost (eg, record-lock lost) */
  1756.     signals[SIGLOST].name = "SIGLOST";
  1757. #endif
  1758. #if defined(SIGUSR1)        /* user defined signal 1 */
  1759.     signals[SIGUSR1].name = "SIGUSR1";
  1760. #endif
  1761. #if defined(SIGUSR2)        /* user defined signal 2 */
  1762.     signals[SIGUSR2].name = "SIGUSR2";
  1763. #endif
  1764.  
  1765. #if 0
  1766. #ifdef HPUX
  1767.     /* initially forced to catch & discard SIGCLD to collect wait status */
  1768.     (void) Tcl_Eval(interp,"trap SIG_DFL SIGCHLD",0,(char **)0);
  1769.     /* no point in checking for errors here, since it is so early on */
  1770.     /* something else will be sure to fail before application begins */
  1771.  
  1772.     /* note that SIGCHLD is used rather than SIGCLD since HPUX defines */
  1773.     /* them both, but expect can only handle one, and it handles the */
  1774.     /* "wrong" one, first */
  1775. #endif
  1776. #endif
  1777. }
  1778.  
  1779. /* reserved to us if name begins with asterisk */
  1780. #define SIG_RESERVED(x)    (signals[x].name[0] == '*')
  1781.  
  1782. char *
  1783. signal_to_string(sig)
  1784. int sig;
  1785. {
  1786.     if (sig < 0 || sig > NSIG) {
  1787.         return("SIGNAL OUT OF RANGE");
  1788.     } else if (!signals[sig].name) {
  1789.         return("SIGNAL UNKNOWN");
  1790.     } else return(signals[sig].name + SIG_RESERVED(sig));
  1791. }
  1792.  
  1793. void
  1794. print_signal(sig)
  1795. int sig;
  1796. {
  1797.     if (signals[sig].action) Log(0,"%s (%d): %s\r\n",
  1798.         signal_to_string(sig),sig,signals[sig].action);
  1799. }
  1800.  
  1801. /* given signal index or name as string, */
  1802. /* returns signal index or -1 if bad arg */
  1803. int
  1804. string_to_signal(s)
  1805. char *s;
  1806. {
  1807.     int sig;
  1808.     char *name;
  1809.  
  1810.     /* try interpreting as an integer */
  1811.     if (1 == sscanf(s,"%d",&sig)) return(sig);
  1812.  
  1813.     /* try interpreting as a string */
  1814.     for (sig=0;sig<NSIG;sig++) {
  1815.         name = signals[sig].name;
  1816.         if (SIG_RESERVED(sig)) name++;
  1817.         if (streq(s,name) || streq(s,name+3))
  1818.             return(sig);
  1819.     }
  1820.     return(-1);
  1821. }
  1822.  
  1823. /* called upon receipt of a user-declared signal */
  1824. void
  1825. generic_sighandler(sig)
  1826. int sig;
  1827. {
  1828.     int proc_valid = TRUE;
  1829.  
  1830.     debuglog("generic_sighandler: handling signal(%d)\r\n",sig);
  1831.  
  1832.     if (sig < 0 || sig >= NSIG) {
  1833.         errorlog("caught impossible signal\r\n",sig);
  1834.     } else if (!signals[sig].action) {
  1835.         /* In this one case, we let ourselves be called when no */
  1836.         /* signaler predefined, since we are calling explicitly */
  1837.         /* from another part of the program, and it is just simpler */
  1838.         if (sig == 0) return;
  1839.         errorlog("caught unexpected signal: %s (%d)\r\n",
  1840.             signal_to_string(sig),sig);
  1841.     } else {
  1842.         int rc;
  1843.  
  1844. #ifdef REARM_SIG
  1845. #ifdef SYSV3
  1846.         /* assume no wait() occurs between SIGCLD and */
  1847.         /* this code */
  1848.         if (sig == SIGCLD) {
  1849.             int i, pid;
  1850.             int status;
  1851.             extern int fd_max;
  1852.  
  1853.             pid = wait(&status);
  1854.             for (i=0;i<=fd_max;i++) {
  1855.                 if (fs[i].pid == pid) break;
  1856.             }
  1857.             if (i>fd_max || !(fs[i].flags & FD_VALID)) {
  1858.                 debuglog("received SIGCLD from unknown pid %d\r\n",pid);
  1859.                 proc_valid = FALSE;
  1860.             } else {
  1861. /*
  1862.                 if (SetSessionFromFD(i)) DeleteSession();
  1863.                 debuglog("generic_sighandler(%d) (fs[i].flags: %x |= FD_SYSWAITED(%x)) = %x\n", i, fs[i].flags, fd, FD_SYSWAITED, fs[i].flags|FD_SYSWAITED);
  1864. */
  1865.                 fs[i].flags |= FD_SYSWAITED;
  1866.                 fs[i].wait = status;
  1867.             }
  1868.         }
  1869. #endif
  1870.         if (sig != 0) Signal(sig,generic_sighandler);
  1871. #endif
  1872.  
  1873.         debuglog("generic_sighandler: Tcl_Eval(%s)\r\n",signals[sig].action);
  1874.         if (proc_valid) {
  1875.           rc = Tcl_Eval(interp,signals[sig].action,0,(char **)0);
  1876.           if (rc != TCL_OK) {
  1877.             errorlog("caught %s (%d): error in command: %s\r\n",
  1878.             signal_to_string(sig),sig,signals[sig].action);
  1879.             if (rc != TCL_ERROR) errorlog("Tcl_Eval = %d\r\n",rc);
  1880.             if (*interp->result != 0) {
  1881.             errorlog("%s\r\n",interp->result);
  1882.             }
  1883.               }
  1884.         }
  1885.     }
  1886.  
  1887.     /* if we are doing an i_read, restart it */
  1888.     /* if (env_valid) longjmp(env,2); */
  1889.     AlarmReturn(2);    /* MDW: Mon Jun 22 14:31:19 1992 */
  1890. }
  1891.  
  1892. /* reset signal to default */
  1893. void
  1894. sig_reset(sig)
  1895. int sig;
  1896. {
  1897. #ifdef SIG_FN_RETURN
  1898.     SIG_FN_RETURN (*default_proc)();
  1899. #else
  1900.     void (*default_proc)();
  1901. #endif
  1902.  
  1903.     signals[sig].action = 0;  /* should've been free'd by now if nec. */
  1904.  
  1905.     /* SIGINT defaults to timeout/exit routine */
  1906.     /* Ultrix 1.3 compiler can't handle this */
  1907.     /* default_proc = (sig == SIGINT?sigint_handler:SIG_DFL);*/
  1908.     if (sig == SIGINT) default_proc = sigint_handler;
  1909.     else default_proc = SIG_DFL;
  1910.  
  1911.     Signal(sig,default_proc);
  1912. }
  1913.  
  1914. /*ARGSUSED*/
  1915. int
  1916. cmdMatchMax(clientData,interp,argc,argv)
  1917. ClientData clientData;
  1918. Tcl_Interp *interp;
  1919. int argc;
  1920. char **argv;
  1921. {
  1922.     int size = -1;
  1923.     int m;
  1924.     struct f *f;
  1925.     char buf[30];    /* big enough for an int, eh? */
  1926.     int Default = FALSE;
  1927.  
  1928.     argc--; argv++;
  1929.  
  1930.     if ((argc >= 1) && streq(argv[0],"-d")) Default = TRUE;
  1931.     else {
  1932.         if (!(f = update_master(&m))) return(TCL_ERROR);
  1933.     }
  1934.  
  1935.     if (argc == 0) {
  1936.         size = f->umsize;
  1937.     } else if ((argc == 1) && Default) {
  1938.         size = default_match_max;
  1939.     }
  1940.     if (size >= 0) {
  1941.         sprintf(buf,"%d",size);
  1942.         Tcl_Return(interp,buf,TCL_VOLATILE);
  1943.         return(TCL_OK);
  1944.     }
  1945.  
  1946.     /* all that's left is to set the size */
  1947.     size = atoi(argv[argc-1]);
  1948.     if (size <= 0) {
  1949.         tcl_error("%s must be positive",EXPECT_MATCH_MAX);
  1950.         return(TCL_ERROR);
  1951.     }
  1952.  
  1953.     if (Default) default_match_max = size;
  1954.     else f->umsize = size;
  1955.  
  1956.     return(TCL_OK);
  1957. }
  1958.  
  1959. /* following is only used as arg to tcl_error */
  1960. char trap_usage[] = "usage: trap [[arg] {list of signals}]";
  1961.  
  1962. /*ARGSUSED*/
  1963. int
  1964. cmdTrap(clientData, interp, argc, argv)
  1965. ClientData clientData;
  1966. Tcl_Interp *interp;
  1967. int argc;
  1968. char **argv;
  1969. {
  1970.     char *action = 0;
  1971.     int n;        /* number of signals in list */
  1972.     char **list;    /* list of signals */
  1973.     int len;    /* length of action */
  1974.     int i;
  1975.     int rc = TCL_OK;
  1976.  
  1977.     if (argc > 3) {
  1978.         tcl_error(trap_usage);
  1979.         return(TCL_ERROR);
  1980.     }
  1981.  
  1982.     if (argc == 1) {
  1983.         for (i=0;i<NSIG;i++) if (signals[i].action) print_signal(i);
  1984.         return(TCL_OK);
  1985.     }
  1986.  
  1987.     if (argc == 3) action = argv[1];
  1988.  
  1989.     /* argv[argc-1] is the list of signals */
  1990.     /* first extract it */
  1991.     if (TCL_OK != Tcl_SplitList(interp,argv[argc-1],&n,&list)) {
  1992.         errorlog("%s\r\n",interp->result);
  1993.         tcl_error(trap_usage);
  1994.         return(TCL_ERROR);
  1995.     }
  1996.  
  1997.     for (i=0;i<n;i++) {
  1998.         int sig = string_to_signal(list[i]);
  1999.         if (sig < 0 || sig >= NSIG) {
  2000.             tcl_error("trap: invalid signal %s",list[i]);
  2001.             rc = TCL_ERROR;
  2002.             break;
  2003.         }
  2004.  
  2005.         if (!action) action = "SIG_DFL";
  2006. /* {
  2007.             print_signal(sig);
  2008.             continue;
  2009.         }
  2010. */
  2011.  
  2012. #if 0
  2013. #ifdef HPUX
  2014.         if (sig == SIGCLD && streq(action,"SIG_DFL")) {
  2015.             action = "";
  2016.         }
  2017. #endif
  2018. #endif
  2019.  
  2020.         if (sig == SIGALRM) {
  2021.             /* SIGALRM reserved to us, for expect command */
  2022.             tcl_error("trap: cannot trap SIGALRM (%d)",SIGALRM);
  2023.             rc = TCL_ERROR;
  2024.             break;
  2025.         }
  2026.  
  2027.         debuglog("trap: setting up signal %d (\"%s\")\r\n",sig,list[i]);
  2028.  
  2029.         if (signals[sig].action) free(signals[sig].action);
  2030.  
  2031.         if (streq(action,"SIG_DFL")) {
  2032.             if (sig != 0) sig_reset(sig);
  2033.         } else {
  2034.             len = 1 + strlen(action);
  2035.             if (0 == (signals[sig].action = malloc(len))) {
  2036.                 tcl_error("trap: malloc failed");
  2037.                 if (sig != 0) sig_reset(sig);
  2038.                 rc = TCL_ERROR;
  2039.                 break;
  2040.             }
  2041.             memcpy(signals[sig].action,action,len);
  2042.             if (sig == 0) continue;
  2043.             if (streq(action,"SIG_IGN")) {
  2044.                 Signal(sig,SIG_IGN);
  2045.             } else Signal(sig,generic_sighandler);
  2046.         }
  2047.     }
  2048.     free((char *)list);
  2049.     return(rc);
  2050. }
  2051.