home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / expect / expect-4.7 / expect.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-12  |  55.2 KB  |  2,237 lines

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