OSDN Git Service

Allow for PWDCMD to override hardcoded pwd.
[pf3gnuchains/pf3gnuchains3x.git] / expect / expect.c
1 /* expect.c - expect 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 */
10
11 #include <sys/types.h>
12 #include <stdio.h>
13 #include <signal.h>
14 #include <errno.h>
15 #include <ctype.h>      /* for isspace */
16 #include <time.h>       /* for time(3) */
17 #if 0
18 #include <setjmp.h>
19 #endif
20
21 #include "expect_cf.h"
22
23 #ifdef HAVE_SYS_WAIT_H
24 #include <sys/wait.h>
25 #endif
26
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30
31 #include "tcl.h"
32
33 #include "string.h"
34
35 #include "tcl_regexp.h"
36 #include "exp_rename.h"
37 #include "exp_prog.h"
38 #include "exp_command.h"
39 #include "exp_log.h"
40 #include "exp_event.h"
41 #include "exp_tty.h"
42 #include "exp_tstamp.h" /* this should disappear when interact */
43                         /* loses ref's to it */
44 #ifdef TCL_DEBUGGER
45 #include "Dbg.h"
46 #endif
47
48 /* initial length of strings that we can guarantee patterns can match */
49 int exp_default_match_max =     2000;
50 #define INIT_EXPECT_TIMEOUT_LIT "10"    /* seconds */
51 #define INIT_EXPECT_TIMEOUT     10      /* seconds */
52 int exp_default_parity =        TRUE;
53 int exp_default_rm_nulls =      TRUE;
54
55 /* user variable names */
56 #define EXPECT_TIMEOUT          "timeout"
57 #define EXPECT_OUT              "expect_out"
58
59 /* 1 ecase struct is reserved for each case in the expect command.  Note that
60 eof/timeout don't use any of theirs, but the algorithm is simpler this way. */
61
62 struct ecase {  /* case for expect command */
63         struct exp_i    *i_list;
64         char *pat;      /* original pattern spec */
65         char *body;     /* ptr to body to be executed upon match */
66 #define PAT_EOF         1
67 #define PAT_TIMEOUT     2
68 #define PAT_DEFAULT     3
69 #define PAT_FULLBUFFER  4
70 #define PAT_GLOB        5 /* glob-style pattern list */
71 #define PAT_RE          6 /* regular expression */
72 #define PAT_EXACT       7 /* exact string */
73 #define PAT_NULL        8 /* ASCII 0 */
74 #define PAT_TYPES       9 /* used to size array of pattern type descriptions */
75         int use;        /* PAT_XXX */
76         int simple_start;/* offset from start of buffer denoting where a */
77                         /* glob or exact match begins */
78         int transfer;   /* if false, leave matched chars in input stream */
79         int indices;    /* if true, write indices */
80 /*      int iwrite;*/   /* if true write spawn_id */
81         int iread;      /* if true, reread indirects */
82         int timestamp;  /* if true, write timestamps */
83 #define CASE_UNKNOWN    0
84 #define CASE_NORM       1
85 #define CASE_LOWER      2
86         int Case;       /* convert case before doing match? */
87         Expect_regexp *re;      /* if this is 0, then pattern match via glob */
88 };
89
90 /* descriptions of the pattern types, used for debugging */
91 char *pattern_style[PAT_TYPES];
92
93 struct exp_cases_descriptor {
94         int count;
95         struct ecase **cases;
96 };
97
98 /* This describes an Expect command */
99 static
100 struct exp_cmd_descriptor {
101         int cmdtype;                    /* bg, before, after */
102         int duration;                   /* permanent or temporary */
103         int timeout_specified_by_flag;  /* if -timeout flag used */
104         int timeout;                    /* timeout period if flag used */
105         struct exp_cases_descriptor ecd;
106         struct exp_i *i_list;
107 } exp_cmds[4];
108 /* note that exp_cmds[FG] is just a fake, the real contents is stored
109    in some dynamically-allocated variable.  We use exp_cmds[FG] mostly
110    as a well-known address and also as a convenience and so we allocate
111    just a few of its fields that we need. */
112
113 static void
114 exp_cmd_init(cmd,cmdtype,duration)
115 struct exp_cmd_descriptor *cmd;
116 int duration;
117 int cmdtype;
118 {
119         cmd->duration = duration;
120         cmd->cmdtype = cmdtype;
121         cmd->ecd.cases = 0;
122         cmd->ecd.count = 0;
123         cmd->i_list = 0;
124 }
125
126 static int i_read_errno;/* place to save errno, if i_read() == -1, so it
127                            doesn't get overwritten before we get to read it */
128 #if 0
129 static jmp_buf env;     /* for interruptable read() */
130                         /* longjmp(env,1) times out the read */
131                         /* longjmp(env,2) restarts the read */
132 static int env_valid = FALSE;   /* whether we can longjmp or not */
133 #endif
134
135 #ifdef SIMPLE_EVENT
136 static int alarm_fired; /* if alarm occurs */
137 #endif
138
139 void exp_background_filehandlers_run_all();
140
141 /* exp_indirect_updateX is called by Tcl when an indirect variable is set */
142 static char *exp_indirect_update1();    /* 1-part Tcl variable names */
143 static char *exp_indirect_update2();    /* 2-part Tcl variable names */
144
145 static int      exp_i_read _ANSI_ARGS_((Tcl_Interp *,int,int,int));
146
147 #ifdef SIMPLE_EVENT
148 /*ARGSUSED*/
149 static RETSIGTYPE
150 sigalarm_handler(n)
151 int n;                  /* unused, for compatibility with STDC */
152 {
153         alarm_fired = TRUE;
154 #if 0
155         /* check env_valid first to protect us from the alarm occurring */
156         /* in the window between i_read and alarm(0) */
157         if (env_valid) longjmp(env,1);
158 #endif /*0*/
159 }
160 #endif /*SIMPLE_EVENT*/
161
162 #if 0
163 /*ARGSUSED*/
164 static RETSIGTYPE
165 sigalarm_handler(n)
166 int n;                  /* unused, for compatibility with STDC */
167 {
168 #ifdef REARM_SIG
169         signal(SIGALRM,sigalarm_handler);
170 #endif
171
172         /* check env_valid first to protect us from the alarm occurring */
173         /* in the window between i_read and alarm(0) */
174         if (env_valid) longjmp(env,1);
175 }
176 #endif /*0*/
177
178 #if 0
179
180 /* upon interrupt, act like timeout */
181 /*ARGSUSED*/
182 static RETSIGTYPE
183 sigint_handler(n)
184 int n;                  /* unused, for compatibility with STDC */
185 {
186 #ifdef REARM_SIG
187         signal(SIGINT,sigint_handler);/* not nec. for BSD, but doesn't hurt */
188 #endif
189
190 #ifdef TCL_DEBUGGER
191         if (exp_tcl_debugger_available) {
192                 /* if the debugger is active and we're reading something, */
193                 /* force the debugger to go interactive now and when done, */
194                 /* restart the read.  */
195
196                 Dbg_On(exp_interp,env_valid);
197
198                 /* restart the read */
199                 if (env_valid) longjmp(env,2);
200
201                 /* if no read is in progess, just let debugger start at */
202                 /* the next command. */
203                 return;
204         }
205 #endif
206
207 #if 0
208 /* the ability to timeout a read via ^C is hereby removed 8-Mar-1993 - DEL */
209
210         /* longjmp if we are executing a read inside of expect command */
211         if (env_valid) longjmp(env,1);
212 #endif
213
214         /* if anywhere else in code, prepare to exit */
215         exp_exit(exp_interp,0);
216 }
217 #endif /*0*/
218
219 /* remove nulls from s.  Initially, the number of chars in s is c, */
220 /* not strlen(s).  This count does not include the trailing null. */
221 /* returns number of nulls removed. */
222 static int
223 rm_nulls(s,c)
224 char *s;
225 int c;
226 {
227         char *s2 = s;   /* points to place in original string to put */
228                         /* next non-null character */
229         int count = 0;
230         int i;
231
232         for (i=0;i<c;i++,s++) {
233                 if (0 == *s) {
234                         count++;
235                         continue;
236                 }
237                 if (count) *s2 = *s;
238                 s2++;
239         }
240         return(count);
241 }
242
243 /* free up everything in ecase */
244 static void
245 free_ecase(interp,ec,free_ilist)
246 Tcl_Interp *interp;
247 struct ecase *ec;
248 int free_ilist;         /* if we should free ilist */
249 {
250         if (ec->re) ckfree((char *)ec->re);
251
252         if (ec->i_list->duration == EXP_PERMANENT) {
253                 if (ec->pat) ckfree(ec->pat);
254                 if (ec->body) ckfree(ec->body);
255         }
256
257         if (free_ilist) {
258                 ec->i_list->ecount--;
259                 if (ec->i_list->ecount == 0)
260                         exp_free_i(interp,ec->i_list,exp_indirect_update2);
261         }
262
263         ckfree((char *)ec);     /* NEW */
264 }
265
266 /* free up any argv structures in the ecases */
267 static void
268 free_ecases(interp,eg,free_ilist)
269 Tcl_Interp *interp;
270 struct exp_cmd_descriptor *eg;
271 int free_ilist;         /* if true, free ilists */
272 {
273         int i;
274
275         if (!eg->ecd.cases) return;
276
277         for (i=0;i<eg->ecd.count;i++) {
278                 free_ecase(interp,eg->ecd.cases[i],free_ilist);
279         }
280         ckfree((char *)eg->ecd.cases);
281
282         eg->ecd.cases = 0;
283         eg->ecd.count = 0;
284 }
285
286
287 #if 0
288 /* no standard defn for this, and some systems don't even have it, so avoid */
289 /* the whole quagmire by calling it something else */
290 static char *exp_strdup(s)
291 char *s;
292 {
293         char *news = ckalloc(strlen(s) + 1);
294         strcpy(news,s);
295         return(news);
296 }
297 #endif
298
299 /* In many places, there is no need to malloc a copy of a string, since it */
300 /* will be freed before we return to Tcl */
301 static void
302 save_str(lhs,rhs,nosave)
303 char **lhs;     /* left hand side */
304 char *rhs;      /* right hand side */
305 int nosave;
306 {
307         if (nosave || (rhs == 0)) {
308                 *lhs = rhs;
309         } else {
310                 *lhs = ckalloc(strlen(rhs) + 1);
311                 strcpy(*lhs,rhs);
312         }
313 }
314
315 /* return TRUE if string appears to be a set of arguments
316    The intent of this test is to support the ability of commands to have
317    all their args braced as one.  This conflicts with the possibility of
318    actually intending to have a single argument.
319    The bad case is in expect which can have a single argument with embedded
320    \n's although it's rare.  Examples that this code should handle:
321    \n           FALSE (pattern)
322    \n\n         FALSE
323    \n  \n \n    FALSE
324    foo          FALSE
325    foo\n        FALSE
326    \nfoo\n      TRUE  (set of args)
327    \nfoo\nbar   TRUE
328
329    Current test is very cheap and almost always right :-)
330 */
331 int 
332 exp_one_arg_braced(p)
333 char *p;
334 {
335         int seen_nl = FALSE;
336
337         for (;*p;p++) {
338                 if (*p == '\n') {
339                         seen_nl = TRUE;
340                         continue;
341                 }
342
343                 if (!isspace(*p)) {
344                         return(seen_nl);
345                 }
346         }
347         return FALSE;
348 }
349
350 /* called to execute a command of only one argument - a hack to commands */
351 /* to be called with all args surrounded by an outer set of braces */
352 /* returns TCL_whatever */
353 /*ARGSUSED*/
354 int
355 exp_eval_with_one_arg(clientData,interp,argv)
356 ClientData clientData;
357 Tcl_Interp *interp;
358 char **argv;
359 {
360         char *buf;
361         int rc;
362         char *a;
363
364         /* + 11 is for " -nobrace " and null at end */
365         buf = ckalloc(strlen(argv[0]) + strlen(argv[1]) + 11);
366         /* recreate statement (with -nobrace to prevent recursion) */
367         sprintf(buf,"%s -nobrace %s",argv[0],argv[1]);
368
369         /*
370          * replace top-level newlines with blanks
371          */
372
373         /* Should only be necessary to run over argv[1] and then sprintf */
374         /* that into the buffer, but the ICEM guys insist that writing */
375         /* back over the original arguments makes their Tcl compiler very */
376         /* unhappy. */
377         for (a=buf;*a;) {
378                 extern char *TclWordEnd();
379
380                 for (;isspace(*a);a++) {
381                         if (*a == '\n') *a = ' ';
382                 }
383 #if TCL_MAJOR_VERSION < 8
384                 a = TclWordEnd(a,0,(int *)0)+1;
385 #else
386                 a = TclWordEnd(a,&a[strlen(a)],0,(int *)0)+1;
387 #endif
388         }
389
390         rc = Tcl_Eval(interp,buf);
391
392         ckfree(buf);
393         return(rc);
394 }
395
396 static void
397 ecase_clear(ec)
398 struct ecase *ec;
399 {
400         ec->i_list = 0;
401         ec->pat = 0;
402         ec->body = 0;
403         ec->transfer = TRUE;
404         ec->indices = FALSE;
405 /*      ec->iwrite = FALSE;*/
406         ec->iread = FALSE;
407         ec->timestamp = FALSE;
408         ec->re = 0;
409         ec->Case = CASE_NORM;
410         ec->use = PAT_GLOB;
411 }
412
413 static struct ecase *
414 ecase_new()
415 {
416         struct ecase *ec = (struct ecase *)ckalloc(sizeof(struct ecase));
417
418         ecase_clear(ec);
419         return ec;
420 }
421
422 /*
423
424 parse_expect_args parses the arguments to expect or its variants. 
425 It normally returns TCL_OK, and returns TCL_ERROR for failure.
426 (It can't return i_list directly because there is no way to differentiate
427 between clearing, say, expect_before and signalling an error.)
428
429 eg (expect_global) is initialized to reflect the arguments parsed
430 eg->ecd.cases is an array of ecases
431 eg->ecd.count is the # of ecases
432 eg->i_list is a linked list of exp_i's which represent the -i info
433
434 Each exp_i is chained to the next so that they can be easily free'd if
435 necessary.  Each exp_i has a reference count.  If the -i is not used
436 (e.g., has no following patterns), the ref count will be 0.
437
438 Each ecase points to an exp_i.  Several ecases may point to the same exp_i.
439 Variables named by indirect exp_i's are read for the direct values.
440
441 If called from a foreground expect and no patterns or -i are given, a
442 default exp_i is forced so that the command "expect" works right.
443
444 The exp_i chain can be broken by the caller if desired.
445
446 */
447
448 static int
449 parse_expect_args(interp,eg,default_spawn_id,argc,argv)
450 Tcl_Interp *interp;
451 struct exp_cmd_descriptor *eg;
452 int default_spawn_id;   /* suggested master if called as expect_user or _tty */
453 int argc;
454 char **argv;
455 {
456         int i;
457         char *arg;
458         struct ecase ec;        /* temporary to collect args */
459
460         argv++;
461         argc--;
462
463         eg->timeout_specified_by_flag = FALSE;
464
465         ecase_clear(&ec);
466
467         /* Allocate an array to store the ecases.  Force array even if 0 */
468         /* cases.  This will often be too large (i.e., if there are flags) */
469         /* but won't affect anything. */
470
471         eg->ecd.cases = (struct ecase **)ckalloc(
472                 sizeof(struct ecase *) * (1+(argc/2)));
473
474         eg->ecd.count = 0;
475
476         for (i = 0;i<argc;i++) {
477                 arg = argv[i];
478         
479                 if (exp_flageq("timeout",arg,7)) {
480                         ec.use = PAT_TIMEOUT;
481                 } else if (exp_flageq("eof",arg,3)) {
482                         ec.use = PAT_EOF;
483                 } else if (exp_flageq("full_buffer",arg,11)) {
484                         ec.use = PAT_FULLBUFFER;
485                 } else if (exp_flageq("default",arg,7)) {
486                         ec.use = PAT_DEFAULT;
487                 } else if (exp_flageq("null",arg,4)) {
488                         ec.use = PAT_NULL;
489                 } else if (arg[0] == '-') {
490                         arg++;
491                         if (exp_flageq1('-',arg)                /* "--" is deprecated */
492                           || exp_flageq("glob",arg,2)) {
493                                 i++;
494                                 /* assignment here is not actually necessary */
495                                 /* since cases are initialized this way above */
496                                 /* ec.use = PAT_GLOB; */
497                         } else if (exp_flageq("regexp",arg,2)) {
498                                 i++;
499                                 ec.use = PAT_RE;
500                                 Expect_TclRegError((char *)0);
501                                 if (!(ec.re = Expect_TclRegComp(argv[i]))) {
502                                         exp_error(interp,"bad regular expression: %s",
503                                                                 TclGetRegError());
504                                         goto error;
505                                 }
506                         } else if (exp_flageq("exact",arg,2)) {
507                                 i++;
508                                 ec.use = PAT_EXACT;
509                         } else if (exp_flageq("notransfer",arg,1)) {
510                                 ec.transfer = 0;
511                                 continue;
512                         } else if (exp_flageq("nocase",arg,3)) {
513                                 ec.Case = CASE_LOWER;
514                                 continue;
515                         } else if (exp_flageq1('i',arg)) {
516                                 i++;
517                                 if (i>=argc) {
518                                         exp_error(interp,"-i requires following spawn_id");
519                                         goto error;
520                                 }
521
522                                 ec.i_list = exp_new_i_complex(interp,argv[i],
523                                         eg->duration,exp_indirect_update2);
524
525                                 ec.i_list->cmdtype = eg->cmdtype;
526
527                                 /* link new i_list to head of list */
528                                 ec.i_list->next = eg->i_list;
529                                 eg->i_list = ec.i_list;
530
531                                 continue;
532                         } else if (exp_flageq("indices",arg,2)) {
533                                 ec.indices = TRUE;
534                                 continue;
535                         } else if (exp_flageq("iwrite",arg,2)) {
536 /*                              ec.iwrite = TRUE;*/
537                                 continue;
538                         } else if (exp_flageq("iread",arg,2)) {
539                                 ec.iread = TRUE;
540                                 continue;
541                         } else if (exp_flageq("timestamp",arg,2)) {
542                                 ec.timestamp = TRUE;
543                                 continue;
544                         } else if (exp_flageq("timeout",arg,2)) {
545                                 i++;
546                                 if (i>=argc) {
547                                         exp_error(interp,"-timeout requires following # of seconds");
548                                         goto error;
549                                 }
550
551                                 eg->timeout = atoi(argv[i]);
552                                 eg->timeout_specified_by_flag = TRUE;
553                                 continue;
554                         } else if (exp_flageq("nobrace",arg,7)) {
555                                 /* nobrace does nothing but take up space */
556                                 /* on the command line which prevents */
557                                 /* us from re-expanding any command lines */
558                                 /* of one argument that looks like it should */
559                                 /* be expanded to multiple arguments. */
560                                 continue;
561                         } else {
562                                 exp_error(interp,"usage: unrecognized flag <%s>",arg);
563                                 goto error;
564                         }
565                 }
566
567                 /* if no -i, use previous one */
568                 if (!ec.i_list) {
569                         /* if no -i flag has occurred yet, use default */
570                         if (!eg->i_list) {
571                                 if (default_spawn_id != EXP_SPAWN_ID_BAD) {
572                                         eg->i_list = exp_new_i_simple(default_spawn_id,eg->duration);
573                                 } else {
574                                         /* it'll be checked later, if used */
575                                         (void) exp_update_master(interp,&default_spawn_id,0,0);
576                                         eg->i_list = exp_new_i_simple(default_spawn_id,eg->duration);
577                                 }
578                         }
579                         ec.i_list = eg->i_list;
580                 }
581                 ec.i_list->ecount++;
582
583                 /* save original pattern spec */
584                 /* keywords such as "-timeout" are saved as patterns here */
585                 /* useful for debugging but not otherwise used */
586                 save_str(&ec.pat,argv[i],eg->duration == EXP_TEMPORARY);
587                 save_str(&ec.body,argv[i+1],eg->duration == EXP_TEMPORARY);
588                         
589                 i++;
590
591                 *(eg->ecd.cases[eg->ecd.count] = ecase_new()) = ec;
592
593                 /* clear out for next set */
594                 ecase_clear(&ec);
595
596                 eg->ecd.count++;
597         }
598
599         /* if no patterns at all have appeared force the current */
600         /* spawn id to be added to list anyway */
601
602         if (eg->i_list == 0) {
603                 if (default_spawn_id != EXP_SPAWN_ID_BAD) {
604                         eg->i_list = exp_new_i_simple(default_spawn_id,eg->duration);
605                 } else {
606                         /* it'll be checked later, if used */
607                         (void) exp_update_master(interp,&default_spawn_id,0,0);
608                         eg->i_list = exp_new_i_simple(default_spawn_id,eg->duration);
609                 }
610         }
611
612         return(TCL_OK);
613
614  error:
615         /* very hard to free case_master_list here if it hasn't already */
616         /* been attached to a case, ugh */
617
618         /* note that i_list must be avail to free ecases! */
619         free_ecases(interp,eg,0);
620
621         /* undo temporary ecase */
622         /* free_ecase doesn't quite handle this right, so do it by hand */
623         if (ec.re) ckfree((char *)ec.re);
624         if (eg->duration == EXP_PERMANENT) {
625                 if (ec.pat) ckfree(ec.pat);
626                 if (ec.body) ckfree(ec.body);
627         }
628
629         if (eg->i_list)
630                 exp_free_i(interp,eg->i_list,exp_indirect_update2);
631         return(TCL_ERROR);
632 }
633
634 #define EXP_IS_DEFAULT(x)       ((x) == EXP_TIMEOUT || (x) == EXP_EOF)
635
636 static char yes[] = "yes\r\n";
637 static char no[] = "no\r\n";
638
639 /* this describes status of a successful match */
640 struct eval_out {
641         struct ecase *e;                /* ecase that matched */
642         struct exp_f *f;                        /* struct exp_f that matched */
643         char *buffer;                   /* buffer that matched */
644         int match;                      /* # of chars in buffer that matched */
645                                         /* or # of chars in buffer at EOF */
646 };
647
648
649 /* like eval_cases, but handles only a single cases that needs a real */
650 /* string match */
651 /* returns EXP_X where X is MATCH, NOMATCH, FULLBUFFER, TCLERRROR */
652 static int
653 eval_case_string(interp,e,m,o,last_f,last_case,suffix)
654 Tcl_Interp *interp;
655 struct ecase *e;
656 int m;
657 struct eval_out *o;             /* 'output' - i.e., final case of interest */
658 /* next two args are for debugging, when they change, reprint buffer */
659 struct exp_f **last_f;
660 int *last_case;
661 char *suffix;
662 {
663         struct exp_f *f = exp_fs + m;
664         char *buffer;
665
666         /* if -nocase, use the lowerized buffer */
667         buffer = ((e->Case == CASE_NORM)?f->buffer:f->lower);
668
669         /* if master or case changed, redisplay debug-buffer */
670         if ((f != *last_f) || e->Case != *last_case) {
671                 debuglog("\r\nexpect%s: does \"%s\" (spawn_id %d) match %s ",
672                                 suffix,
673                                 dprintify(buffer),f-exp_fs,
674                                 pattern_style[e->use]);
675                 *last_f = f;
676                 *last_case = e->Case;
677         }
678
679         if (e->use == PAT_RE) {
680                 debuglog("\"%s\"? ",dprintify(e->pat));
681                 Expect_TclRegError((char *)0);
682                 if (buffer && Expect_TclRegExec(e->re,buffer,buffer)) {
683                         o->e = e;
684                         o->match = e->re->endp[0]-buffer;
685                         o->buffer = buffer;
686                         o->f = f;
687                         debuglog(yes);
688                         return(EXP_MATCH);
689                 } else {
690                         debuglog(no);
691                         if (TclGetRegError()) {
692                             exp_error(interp,"-re failed: %s",TclGetRegError());
693                             return(EXP_TCLERROR);
694                         }
695                     }
696         } else if (e->use == PAT_GLOB) {
697                 int match; /* # of chars that matched */
698
699                 debuglog("\"%s\"? ",dprintify(e->pat));
700                 if (buffer && (-1 != (match = Exp_StringMatch(
701                                 buffer,e->pat,&e->simple_start)))) {
702                         o->e = e;
703                         o->match = match;
704                         o->buffer = buffer;
705                         o->f = f;
706                         debuglog(yes);
707                         return(EXP_MATCH);
708                 } else debuglog(no);
709         } else if (e->use == PAT_EXACT) {
710                 char *p = strstr(buffer,e->pat);
711                 debuglog("\"%s\"? ",dprintify(e->pat));
712                 if (p) {
713                         e->simple_start = p - buffer;
714                         o->e = e;
715                         o->match = strlen(e->pat);
716                         o->buffer = buffer;
717                         o->f = f;
718                         debuglog(yes);
719                         return(EXP_MATCH);
720                 } else debuglog(no);
721         } else if (e->use == PAT_NULL) {
722                 int i = 0;
723                 debuglog("null? ");
724                 for (;i<f->size;i++) {
725                         if (buffer[i] == 0) {
726                                 o->e = e;
727                                 o->match = i+1; /* in this case, match is */
728                                                 /* just the # of chars + 1 */
729                                                 /* before the null */
730                                 o->buffer = buffer;
731                                 o->f = f;
732                                 debuglog(yes);
733                                 return EXP_MATCH;
734                         }
735                 }
736                 debuglog(no);
737         } else if ((f->size == f->msize) && (f->size > 0)) {
738                 debuglog("%s? ",e->pat);
739                 o->e = e;
740                 o->match = f->umsize;
741                 o->buffer = f->buffer;
742                 o->f = f;
743                 debuglog(yes);
744                 return(EXP_FULLBUFFER);
745         }
746         return(EXP_NOMATCH);
747 }
748
749 /* sets o.e if successfully finds a matching pattern, eof, timeout or deflt */
750 /* returns original status arg or EXP_TCLERROR */
751 static int
752 eval_cases(interp,eg,m,o,last_f,last_case,status,masters,mcount,suffix)
753 Tcl_Interp *interp;
754 struct exp_cmd_descriptor *eg;
755 int m;
756 struct eval_out *o;             /* 'output' - i.e., final case of interest */
757 /* next two args are for debugging, when they change, reprint buffer */
758 struct exp_f **last_f;
759 int *last_case;
760 int status;
761 int *masters;
762 int mcount;
763 char *suffix;
764 {
765         int i;
766         int em; /* master of ecase */
767         struct ecase *e;
768
769         if (o->e || status == EXP_TCLERROR || eg->ecd.count == 0) return(status);
770
771         if (status == EXP_TIMEOUT) {
772                 for (i=0;i<eg->ecd.count;i++) {
773                         e = eg->ecd.cases[i];
774                         if (e->use == PAT_TIMEOUT || e->use == PAT_DEFAULT) {
775                                 o->e = e;
776                                 break;
777                         }
778                 }
779                 return(status);
780         } else if (status == EXP_EOF) {
781                 for (i=0;i<eg->ecd.count;i++) {
782                         e = eg->ecd.cases[i];
783                         if (e->use == PAT_EOF || e->use == PAT_DEFAULT) {
784                                 struct exp_fd_list *fdl;
785
786                                 for (fdl=e->i_list->fd_list; fdl ;fdl=fdl->next) {
787                                         em = fdl->fd;
788                                         if (em == EXP_SPAWN_ID_ANY || em == m) {
789                                                 o->e = e;
790                                                 return(status);
791                                         }
792                                 }
793                         }
794                 }
795                 return(status);
796         }
797
798         /* the top loops are split from the bottom loop only because I can't */
799         /* split'em further. */
800
801         /* The bufferful condition does not prevent a pattern match from */
802         /* occurring and vice versa, so it is scanned with patterns */
803         for (i=0;i<eg->ecd.count;i++) {
804                 struct exp_fd_list *fdl;
805                 int j;
806
807                 e = eg->ecd.cases[i];
808                 if (e->use == PAT_TIMEOUT ||
809                     e->use == PAT_DEFAULT ||
810                     e->use == PAT_EOF) continue;
811
812                 for (fdl = e->i_list->fd_list; fdl; fdl = fdl->next) {
813                         em = fdl->fd;
814                         /* if em == EXP_SPAWN_ID_ANY, then user is explicitly asking */
815                         /* every case to be checked against every master */
816                         if (em == EXP_SPAWN_ID_ANY) {
817                                 /* test against each spawn_id */
818                                 for (j=0;j<mcount;j++) {
819                                         status = eval_case_string(interp,e,masters[j],o,last_f,last_case,suffix);
820                                         if (status != EXP_NOMATCH) return(status);
821                                 }
822                         } else {
823                                 /* reject things immediately from wrong spawn_id */
824                                 if (em != m) continue;
825
826                                 status = eval_case_string(interp,e,m,o,last_f,last_case,suffix);
827                                 if (status != EXP_NOMATCH) return(status);
828                         }
829                 }
830         }
831         return(EXP_NOMATCH);
832 }
833
834 static void
835 ecases_remove_by_expi(interp,ecmd,exp_i)
836 Tcl_Interp *interp;
837 struct exp_cmd_descriptor *ecmd;
838 struct exp_i *exp_i;
839 {
840         int i;
841
842         /* delete every ecase dependent on it */
843         for (i=0;i<ecmd->ecd.count;) {
844                 struct ecase *e = ecmd->ecd.cases[i];
845                 if (e->i_list == exp_i) {
846                         free_ecase(interp,e,0);
847
848                         /* shift remaining elements down */
849                         /* but only if there are any left */
850                         if (i+1 != ecmd->ecd.count) {
851                                 memcpy(&ecmd->ecd.cases[i],
852                                        &ecmd->ecd.cases[i+1],
853                                         ((ecmd->ecd.count - i) - 1) * 
854                                         sizeof(struct exp_cmd_descriptor *));
855                         }
856                         ecmd->ecd.count--;
857                         if (0 == ecmd->ecd.count) {
858                                 ckfree((char *)ecmd->ecd.cases);
859                                 ecmd->ecd.cases = 0;
860                         }
861                 } else {
862                         i++;
863                 }
864         }
865 }
866
867 /* remove exp_i from list */
868 static void
869 exp_i_remove(interp,ei,exp_i)
870 Tcl_Interp *interp;
871 struct exp_i **ei;      /* list to remove from */
872 struct exp_i *exp_i;    /* element to remove */
873 {
874         /* since it's in middle of list, free exp_i by hand */
875         for (;*ei; ei = &(*ei)->next) {
876                 if (*ei == exp_i) {
877                         *ei = exp_i->next;
878                         exp_i->next = 0;
879                         exp_free_i(interp,exp_i,exp_indirect_update2);
880                         break;
881                 }
882         }
883 }
884
885 /* remove exp_i from list and remove any dependent ecases */
886 static void
887 exp_i_remove_with_ecases(interp,ecmd,exp_i)
888 Tcl_Interp *interp;
889 struct exp_cmd_descriptor *ecmd;
890 struct exp_i *exp_i;
891 {
892         ecases_remove_by_expi(interp,ecmd,exp_i);
893         exp_i_remove(interp,&ecmd->i_list,exp_i);
894 }
895
896 /* remove ecases tied to a single direct spawn id */
897 static void
898 ecmd_remove_fd(interp,ecmd,m,direct)
899 Tcl_Interp *interp;
900 struct exp_cmd_descriptor *ecmd;
901 int m;
902 int direct;
903 {
904         struct exp_i *exp_i, *next;
905         struct exp_fd_list **fdl;
906
907         for (exp_i=ecmd->i_list;exp_i;exp_i=next) {
908                 next = exp_i->next;
909
910                 if (!(direct & exp_i->direct)) continue;
911
912                 for (fdl = &exp_i->fd_list;*fdl;) {
913                         if (m == ((*fdl)->fd)) {
914                                 struct exp_fd_list *tmp = *fdl;
915                                 *fdl = (*fdl)->next;
916                                 exp_free_fd_single(tmp);
917
918                                 /* if last bg ecase, disarm spawn id */
919                                 if ((ecmd->cmdtype == EXP_CMD_BG) && (m != EXP_SPAWN_ID_ANY)) {
920                                         exp_fs[m].bg_ecount--;
921                                         if (exp_fs[m].bg_ecount == 0) {
922                                                 exp_disarm_background_filehandler(m);
923                                                 exp_fs[m].bg_interp = 0;
924                                         }
925                                 }
926
927                                 continue;
928                         }
929                         fdl = &(*fdl)->next;
930                 }
931
932                 /* if left with no fds (and is direct), get rid of it */
933                 /* and any dependent ecases */
934                 if (exp_i->direct == EXP_DIRECT && !exp_i->fd_list) {
935                         exp_i_remove_with_ecases(interp,ecmd,exp_i);
936                 }
937         }
938 }
939
940 /* this is called from exp_close to clean up the fd */
941 void
942 exp_ecmd_remove_fd_direct_and_indirect(interp,m)
943 Tcl_Interp *interp;
944 int m;
945 {
946         ecmd_remove_fd(interp,&exp_cmds[EXP_CMD_BEFORE],m,EXP_DIRECT|EXP_INDIRECT);
947         ecmd_remove_fd(interp,&exp_cmds[EXP_CMD_AFTER],m,EXP_DIRECT|EXP_INDIRECT);
948         ecmd_remove_fd(interp,&exp_cmds[EXP_CMD_BG],m,EXP_DIRECT|EXP_INDIRECT);
949
950         /* force it - explanation in exp_tk.c where this func is defined */
951         exp_disarm_background_filehandler_force(m);
952 }
953
954 /* arm a list of background fd's */
955 static void
956 fd_list_arm(interp,fdl)
957 Tcl_Interp *interp;
958 struct exp_fd_list *fdl;
959 {
960         /* for each spawn id in list, arm if necessary */
961         for (;fdl;fdl=fdl->next) {
962                 int m = fdl->fd;
963                 if (m == EXP_SPAWN_ID_ANY) continue;
964
965                 if (exp_fs[m].bg_ecount == 0) {
966                         exp_arm_background_filehandler(m);
967                         exp_fs[m].bg_interp = interp;
968                 }
969                 exp_fs[m].bg_ecount++;
970         }
971 }
972
973 /* return TRUE if this ecase is used by this fd */
974 static int
975 exp_i_uses_fd(exp_i,fd)
976 struct exp_i *exp_i;
977 int fd;
978 {
979         struct exp_fd_list *fdp;
980
981         for (fdp = exp_i->fd_list;fdp;fdp=fdp->next) {
982                 if (fdp->fd == fd) return 1;
983         }
984         return 0;
985 }
986
987 static void
988 ecase_append(interp,ec)
989 Tcl_Interp *interp;
990 struct ecase *ec;
991 {
992         if (!ec->transfer) Tcl_AppendElement(interp,"-notransfer");
993         if (ec->indices) Tcl_AppendElement(interp,"-indices");
994 /*      if (ec->iwrite) Tcl_AppendElement(interp,"-iwrite");*/
995         if (!ec->Case) Tcl_AppendElement(interp,"-nocase");
996
997         if (ec->re) Tcl_AppendElement(interp,"-re");
998         else if (ec->use == PAT_GLOB) Tcl_AppendElement(interp,"-gl");
999         else if (ec->use == PAT_EXACT) Tcl_AppendElement(interp,"-ex");
1000         Tcl_AppendElement(interp,ec->pat);
1001         Tcl_AppendElement(interp,ec->body?ec->body:"");
1002 }
1003
1004 /* append all ecases that match this exp_i */
1005 static void
1006 ecase_by_exp_i_append(interp,ecmd,exp_i)
1007 Tcl_Interp *interp;
1008 struct exp_cmd_descriptor *ecmd;
1009 struct exp_i *exp_i;
1010 {
1011         int i;
1012         for (i=0;i<ecmd->ecd.count;i++) {
1013                 if (ecmd->ecd.cases[i]->i_list == exp_i) {
1014                         ecase_append(interp,ecmd->ecd.cases[i]);
1015                 }
1016         }
1017 }
1018
1019 static void
1020 exp_i_append(interp,exp_i)
1021 Tcl_Interp *interp;
1022 struct exp_i *exp_i;
1023 {
1024         Tcl_AppendElement(interp,"-i");
1025         if (exp_i->direct == EXP_INDIRECT) {
1026                 Tcl_AppendElement(interp,exp_i->variable);
1027         } else {
1028                 struct exp_fd_list *fdp;
1029
1030                 /* if more than one element, add braces */
1031                 if (exp_i->fd_list->next)
1032                         Tcl_AppendResult(interp," {",(char *)0);
1033
1034                 for (fdp = exp_i->fd_list;fdp;fdp=fdp->next) {
1035                         char buf[10];   /* big enough for a small int */
1036                         sprintf(buf,"%d",fdp->fd);
1037                         Tcl_AppendElement(interp,buf);
1038                 }
1039
1040                 if (exp_i->fd_list->next)
1041                         Tcl_AppendResult(interp,"} ",(char *)0);
1042         }
1043 }
1044
1045 #if 0
1046 /* delete ecases based on named -i descriptors */
1047 int
1048 expect_delete(interp,ecmd,argc,argv)
1049 Tcl_Interp *interp;
1050 struct exp_cmd_descriptor *ecmd;
1051 int argc;
1052 char **argv;
1053 {
1054         while (*argv) {
1055                 if (streq(argv[0],"-i") && argv[1]) {
1056                         iflag = argv[1];
1057                         argc-=2; argv+=2;
1058                 } else if (streq(argv[0],"-all")) {
1059                         all = TRUE;
1060                         argc--; argv++;
1061                 } else if (streq(argv[0],"-noindirect")) {
1062                         direct &= ~EXP_INDIRECT;
1063                         argc--; argv++;
1064                 } else {
1065                         exp_error(interp,"usage: -delete [-all | -i spawn_id]\n");
1066                         return TCL_ERROR;
1067                 }
1068         }
1069
1070         if (all) {
1071                 /* same logic as at end of regular expect cmd */
1072                 free_ecases(interp,ecmd,0);
1073                 exp_free_i(interp,ecmd->i_list,exp_indirect_update2);
1074                 return TCL_OK;
1075         }
1076
1077         if (!iflag) {
1078                 if (0 == exp_update_master(interp,&m,0,0)) {
1079                         return TCL_ERROR;
1080                 }
1081         } else if (Tcl_GetInt(interp,iflag,&m) != TCL_OK) {
1082                 /* handle as in indirect */
1083
1084                 struct exp_i **old_i;
1085
1086                 for (old_i=&ecmd->i_list;*old_i;) {
1087                         struct exp_i *tmp;
1088
1089                         if ((*old_i)->direct == EXP_DIRECT) continue;
1090                         if (!streq((*old_i)->variable,iflag)) continue;
1091
1092                         ecases_remove_by_expi(interp,ecmd,*old_i);
1093
1094                         /* unlink from middle of list */
1095                         tmp = *old_i;
1096                         *old_i = tmp->next;
1097                         tmp->next = 0;
1098                         exp_free_i(interp,tmp_i,exp_indirect_update2);
1099                 } else {
1100                         old_i = &(*old_i)->next;
1101                 }
1102                 return TCL_OK;
1103         }
1104
1105         /* delete ecases of this direct_fd */
1106         /* unfinish after this ... */
1107         for (exp_i=ecmd->i_list;exp_i;exp_i=exp_i->next) {
1108                 if (!(direct & exp_i->direct)) continue;
1109                 if (!exp_i_uses_fd(exp_i,m)) continue;
1110
1111                 /* delete each ecase that uses this exp_i */
1112
1113
1114                 ecase_by_exp_i_append(interp,ecmd,exp_i);
1115         }
1116
1117         return TCL_OK;
1118 }
1119 #endif
1120
1121 /* return current setting of the permanent expect_before/after/bg */
1122 int
1123 expect_info(interp,ecmd,argc,argv)
1124 Tcl_Interp *interp;
1125 struct exp_cmd_descriptor *ecmd;
1126 int argc;
1127 char **argv;
1128 {
1129         struct exp_i *exp_i;
1130         int i;
1131         int direct = EXP_DIRECT|EXP_INDIRECT;
1132         char *iflag = 0;
1133         int all = FALSE;        /* report on all fds */
1134         int m;
1135
1136         while (*argv) {
1137                 if (streq(argv[0],"-i") && argv[1]) {
1138                         iflag = argv[1];
1139                         argc-=2; argv+=2;
1140                 } else if (streq(argv[0],"-all")) {
1141                         all = TRUE;
1142                         argc--; argv++;
1143                 } else if (streq(argv[0],"-noindirect")) {
1144                         direct &= ~EXP_INDIRECT;
1145                         argc--; argv++;
1146                 } else {
1147                         exp_error(interp,"usage: -info [-all | -i spawn_id]\n");
1148                         return TCL_ERROR;
1149                 }
1150         }
1151
1152         if (all) {
1153                 /* avoid printing out -i when redundant */
1154                 struct exp_i *previous = 0;
1155
1156                 for (i=0;i<ecmd->ecd.count;i++) {
1157                         if (previous != ecmd->ecd.cases[i]->i_list) {
1158                                 exp_i_append(interp,ecmd->ecd.cases[i]->i_list);
1159                                 previous = ecmd->ecd.cases[i]->i_list;
1160                         }
1161                         ecase_append(interp,ecmd->ecd.cases[i]);
1162                 }
1163                 return TCL_OK;
1164         }
1165
1166         if (!iflag) {
1167                 if (0 == exp_update_master(interp,&m,0,0)) {
1168                         return TCL_ERROR;
1169                 }
1170         } else if (Tcl_GetInt(interp,iflag,&m) != TCL_OK) {
1171                 /* handle as in indirect */
1172                 Tcl_ResetResult(interp);
1173                 for (i=0;i<ecmd->ecd.count;i++) {
1174                         if (ecmd->ecd.cases[i]->i_list->direct == EXP_INDIRECT &&
1175                             streq(ecmd->ecd.cases[i]->i_list->variable,iflag)) {
1176                                 ecase_append(interp,ecmd->ecd.cases[i]);
1177                         }
1178                 }
1179                 return TCL_OK;
1180         }
1181
1182         /* print ecases of this direct_fd */
1183         for (exp_i=ecmd->i_list;exp_i;exp_i=exp_i->next) {
1184                 if (!(direct & exp_i->direct)) continue;
1185                 if (!exp_i_uses_fd(exp_i,m)) continue;
1186                 ecase_by_exp_i_append(interp,ecmd,exp_i);
1187         }
1188
1189         return TCL_OK;
1190 }
1191
1192 /* Exp_ExpectGlobalCmd is invoked to process expect_before/after */
1193 /*ARGSUSED*/
1194 int
1195 Exp_ExpectGlobalCmd(clientData, interp, argc, argv)
1196 ClientData clientData;
1197 Tcl_Interp *interp;
1198 int argc;
1199 char **argv;
1200 {
1201         int result = TCL_OK;
1202         struct exp_i *exp_i, **eip;
1203         struct exp_fd_list *fdl;        /* temp for interating over fd_list */
1204         struct exp_cmd_descriptor eg;
1205         int count;
1206
1207         struct exp_cmd_descriptor *ecmd = (struct exp_cmd_descriptor *) clientData;
1208
1209         if ((argc == 2) && exp_one_arg_braced(argv[1])) {
1210                 return(exp_eval_with_one_arg(clientData,interp,argv));
1211         } else if ((argc == 3) && streq(argv[1],"-brace")) {
1212                 char *new_argv[2];
1213                 new_argv[0] = argv[0];
1214                 new_argv[1] = argv[2];
1215                 return(exp_eval_with_one_arg(clientData,interp,new_argv));
1216         }
1217
1218         if (argc > 1 && (argv[1][0] == '-')) {
1219                 if (exp_flageq("info",&argv[1][1],4)) {
1220                         return(expect_info(interp,ecmd,argc-2,argv+2));
1221                 } 
1222         }
1223
1224         exp_cmd_init(&eg,ecmd->cmdtype,EXP_PERMANENT);
1225
1226         if (TCL_ERROR == parse_expect_args(interp,&eg,EXP_SPAWN_ID_BAD,
1227                                         argc,argv)) {
1228                 return TCL_ERROR;
1229         }
1230
1231         /*
1232          * visit each NEW direct exp_i looking for spawn ids.
1233          * When found, remove them from any OLD exp_i's.
1234          */
1235
1236         /* visit each exp_i */
1237         for (exp_i=eg.i_list;exp_i;exp_i=exp_i->next) {
1238                 if (exp_i->direct == EXP_INDIRECT) continue;
1239
1240                 /* for each spawn id, remove it from ecases */
1241                 for (fdl=exp_i->fd_list;fdl;fdl=fdl->next) {
1242                         int m = fdl->fd;
1243
1244                         /* validate all input descriptors */
1245                         if (m != EXP_SPAWN_ID_ANY) {
1246                                 if (!exp_fd2f(interp,m,1,1,"expect")) {
1247                                         result = TCL_ERROR;
1248                                         goto cleanup;
1249                                 }
1250                         }
1251
1252                         /* remove spawn id from exp_i */
1253                         ecmd_remove_fd(interp,ecmd,m,EXP_DIRECT);
1254                 }
1255         }
1256         
1257         /*
1258          * For each indirect variable, release its old ecases and 
1259          * clean up the matching spawn ids.
1260          * Same logic as in "expect_X delete" command.
1261          */
1262
1263         for (exp_i=eg.i_list;exp_i;exp_i=exp_i->next) {
1264                 struct exp_i **old_i;
1265
1266                 if (exp_i->direct == EXP_DIRECT) continue;
1267
1268                 for (old_i = &ecmd->i_list;*old_i;) {
1269                         struct exp_i *tmp;
1270
1271                         if (((*old_i)->direct == EXP_DIRECT) ||
1272                             (!streq((*old_i)->variable,exp_i->variable))) {
1273                                 old_i = &(*old_i)->next;
1274                                 continue;
1275                         }
1276
1277                         ecases_remove_by_expi(interp,ecmd,*old_i);
1278
1279                         /* unlink from middle of list */
1280                         tmp = *old_i;
1281                         *old_i = tmp->next;
1282                         tmp->next = 0;
1283                         exp_free_i(interp,tmp,exp_indirect_update2);
1284                 }
1285
1286                 /* if new one has ecases, update it */
1287                 if (exp_i->ecount) {
1288                         char *msg = exp_indirect_update1(interp,ecmd,exp_i);
1289                         if (msg) {
1290                                 /* unusual way of handling error return */
1291                                 /* because of Tcl's variable tracing */
1292                                 strcpy(interp->result,msg);
1293                                 result = TCL_ERROR;
1294                                 goto indirect_update_abort;
1295                         }
1296                 }
1297         }
1298         /* empty i_lists have to be removed from global eg.i_list */
1299         /* before returning, even if during error */
1300  indirect_update_abort:
1301
1302         /*
1303          * New exp_i's that have 0 ecases indicate fd/vars to be deleted.
1304          * Now that the deletions have been done, discard the new exp_i's.
1305          */
1306
1307         for (exp_i=eg.i_list;exp_i;) {
1308                 struct exp_i *next = exp_i->next;
1309
1310                 if (exp_i->ecount == 0) {
1311                         exp_i_remove(interp,&eg.i_list,exp_i);
1312                 }
1313                 exp_i = next;
1314         }
1315         if (result == TCL_ERROR) goto cleanup;
1316
1317         /*
1318          * arm all new bg direct fds
1319          */
1320
1321         if (ecmd->cmdtype == EXP_CMD_BG) {
1322                 for (exp_i=eg.i_list;exp_i;exp_i=exp_i->next) {
1323                         if (exp_i->direct == EXP_DIRECT) {
1324                                 fd_list_arm(interp,exp_i->fd_list);
1325                         }
1326                 }
1327         }
1328
1329         /*
1330          * now that old ecases are gone, add new ecases and exp_i's (both
1331          * direct and indirect).
1332          */
1333
1334         /* append ecases */
1335
1336         count = ecmd->ecd.count + eg.ecd.count;
1337         if (eg.ecd.count) {
1338                 int start_index; /* where to add new ecases in old list */
1339
1340                 if (ecmd->ecd.count) {
1341                         /* append to end */
1342                         ecmd->ecd.cases = (struct ecase **)ckrealloc((char *)ecmd->ecd.cases, count * sizeof(struct ecase *));
1343                         start_index = ecmd->ecd.count;
1344                 } else {
1345                         /* append to beginning */
1346                         ecmd->ecd.cases = (struct ecase **)ckalloc(eg.ecd.count * sizeof(struct ecase *));
1347                         start_index = 0;
1348                 }
1349                 memcpy(&ecmd->ecd.cases[start_index],eg.ecd.cases,
1350                                         eg.ecd.count*sizeof(struct ecase *));
1351                 ecmd->ecd.count = count;
1352         }
1353
1354         /* append exp_i's */
1355         for (eip = &ecmd->i_list;*eip;eip = &(*eip)->next) {
1356                 /* empty loop to get to end of list */
1357         }
1358         /* *exp_i now points to end of list */
1359
1360         *eip = eg.i_list;       /* connect new list to end of current list */
1361
1362  cleanup:
1363         if (result == TCL_ERROR) {
1364                 /* in event of error, free any unreferenced ecases */
1365                 /* but first, split up i_list so that exp_i's aren't */
1366                 /* freed twice */
1367
1368                 for (exp_i=eg.i_list;exp_i;) {
1369                         struct exp_i *next = exp_i->next;
1370                         exp_i->next = 0;
1371                         exp_i = next;
1372                 }
1373                 free_ecases(interp,&eg,1);
1374         } else {
1375                 if (eg.ecd.cases) ckfree((char *)eg.ecd.cases);
1376         }
1377
1378         if (ecmd->cmdtype == EXP_CMD_BG) {
1379                 exp_background_filehandlers_run_all();
1380         }
1381
1382         return(result);
1383 }
1384
1385 /* adjusts file according to user's size request */
1386 void
1387 exp_adjust(f)
1388 struct exp_f *f;
1389 {
1390         int new_msize;
1391
1392         /* get the latest buffer size.  Double the user input for */
1393         /* two reasons.  1) Need twice the space in case the match */
1394         /* straddles two bufferfuls, 2) easier to hack the division */
1395         /* by two when shifting the buffers later on.  The extra  */
1396         /* byte in the malloc's is just space for a null we can slam on the */
1397         /* end.  It makes the logic easier later.  The -1 here is so that */
1398         /* requests actually come out to even/word boundaries (if user */
1399         /* gives "reasonable" requests) */
1400         new_msize = f->umsize*2 - 1;
1401         if (new_msize != f->msize) {
1402                 if (!f->buffer) {
1403                         /* allocate buffer space for 1st time */
1404                         f->buffer = ckalloc((unsigned)new_msize+1);
1405                         f->lower = ckalloc((unsigned)new_msize+1);
1406                         f->size = 0;
1407                 } else {
1408                         /* buffer already exists - resize */
1409
1410                         /* if truncated, forget about some data */
1411                         if (f->size > new_msize) {
1412                                 /* copy end of buffer down */
1413                                 memmove(f->buffer,f->buffer+(f->size - new_msize),new_msize);
1414                                 memmove(f->lower, f->lower +(f->size - new_msize),new_msize);
1415                                 f->size = new_msize;
1416
1417                                 f->key = expect_key++;
1418                         }
1419
1420                         f->buffer = ckrealloc(f->buffer,new_msize+1);
1421                         f->lower = ckrealloc(f->lower,new_msize+1);
1422                 }
1423                 f->msize = new_msize;
1424                 f->buffer[f->size] = '\0';
1425                 f->lower[f->size] = '\0';
1426         }
1427 }
1428
1429
1430 /*
1431
1432  expect_read() does the logical equivalent of a read() for the
1433 expect command.  This includes figuring out which descriptor should
1434 be read from.
1435
1436 The result of the read() is left in a spawn_id's buffer rather than
1437 explicitly passing it back.  Note that if someone else has modified a
1438 buffer either before or while this expect is running (i.e., if we or
1439 some event has called Tcl_Eval which did another expect/interact),
1440 expect_read will also call this a successful read (for the purposes if
1441 needing to pattern match against it).
1442
1443 */
1444 /* if it returns a negative number, it corresponds to a EXP_XXX result */
1445 /* if it returns a non-negative number, it means there is data */
1446 /* (0 means nothing new was actually read, but it should be looked at again) */
1447 int
1448 expect_read(interp,masters,masters_max,m,timeout,key)
1449 Tcl_Interp *interp;
1450 int *masters;                   /* If 0, then m is already known and set. */
1451 int masters_max;                /* If *masters is not-zero, then masters_max */
1452                                 /* is the number of masters. */
1453                                 /* If *masters is zero, then masters_max */
1454                                 /* is used as the mask (ready vs except). */
1455                                 /* Crude but simplifies the interface. */
1456 int *m;                         /* Out variable to leave new master. */
1457 int timeout;
1458 int key;
1459 {
1460         struct exp_f *f;
1461         int cc;
1462         int write_count;
1463         int tcl_set_flags;      /* if we have to discard chars, this tells */
1464                                 /* whether to show user locally or globally */
1465
1466         if (masters == 0) {
1467                 /* we already know the master, just find out what happened */
1468                 cc = exp_get_next_event_info(interp,*m,masters_max);
1469                 tcl_set_flags = TCL_GLOBAL_ONLY;
1470         } else {
1471                 cc = exp_get_next_event(interp,masters,masters_max,m,timeout,key);
1472                 tcl_set_flags = 0;
1473         }
1474
1475         if (cc == EXP_DATA_NEW) {
1476                 /* try to read it */
1477
1478                 cc = exp_i_read(interp,*m,timeout,tcl_set_flags);
1479
1480                 /* the meaning of 0 from i_read means eof.  Muck with it a */
1481                 /* little, so that from now on it means "no new data arrived */
1482                 /* but it should be looked at again anyway". */
1483                 if (cc == 0) {
1484                         cc = EXP_EOF;
1485                 } else if (cc > 0) {
1486                         f = exp_fs + *m;
1487                         f->buffer[f->size += cc] = '\0';
1488
1489                         /* strip parity if requested */
1490                         if (f->parity == 0) {
1491                                 /* do it from end backwards */
1492                                 char *p = f->buffer + f->size - 1;
1493                                 int count = cc;
1494                                 while (count--) {
1495                                         *p-- &= 0x7f;
1496                                 }
1497                         }
1498                 } /* else {
1499                         assert(cc < 0) in which case some sort of error was
1500                         encountered such as an interrupt with that forced an
1501                         error return
1502                 } */
1503         } else if (cc == EXP_DATA_OLD) {
1504                 f = exp_fs + *m;
1505                 cc = 0;
1506         } else if (cc == EXP_RECONFIGURE) {
1507                 return EXP_RECONFIGURE;
1508         }
1509
1510         if (cc == EXP_ABEOF) {  /* abnormal EOF */
1511                 /* On many systems, ptys produce EIO upon EOF - sigh */
1512                 if (i_read_errno == EIO) {
1513                         /* Sun, Cray, BSD, and others */
1514                         cc = EXP_EOF;
1515                 } else if (i_read_errno == EINVAL) {
1516                         /* Solaris 2.4 occasionally returns this */
1517                         cc = EXP_EOF;
1518                 } else {
1519                         if (i_read_errno == EBADF) {
1520                                 exp_error(interp,"bad spawn_id (process died earlier?)");
1521                         } else {
1522                                 exp_error(interp,"i_read(spawn_id=%d): %s",*m,
1523                                         Tcl_PosixError(interp));
1524                                 exp_close(interp,*m);
1525                         }
1526                         return(EXP_TCLERROR);
1527                         /* was goto error; */
1528                 }
1529         }
1530
1531         /* EOF, TIMEOUT, and ERROR return here */
1532         /* In such cases, there is no need to update screen since, if there */
1533         /* was prior data read, it would have been sent to the screen when */
1534         /* it was read. */
1535         if (cc < 0) return (cc);
1536
1537         /* update display */
1538
1539         if (f->size) write_count = f->size - f->printed;
1540         else write_count = 0;
1541
1542         if (write_count) {
1543                 if (logfile_all || (loguser && logfile)) {
1544                         fwrite(f->buffer + f->printed,1,write_count,logfile);
1545                 }
1546                 /* don't write to user if they're seeing it already, */
1547                 /* that is, typing it! */
1548                 if (loguser && !exp_is_stdinfd(*m) && !exp_is_devttyfd(*m))
1549                         fwrite(f->buffer + f->printed,
1550                                         1,write_count,stdout);
1551                 if (debugfile) fwrite(f->buffer + f->printed,
1552                                         1,write_count,debugfile);
1553
1554                 /* remove nulls from input, since there is no way */
1555                 /* for Tcl to deal with such strings.  Doing it here */
1556                 /* lets them be sent to the screen, just in case */
1557                 /* they are involved in formatting operations */
1558                 if (f->rm_nulls) {
1559                         f->size -= rm_nulls(f->buffer + f->printed,write_count);
1560                 }
1561                 f->buffer[f->size] = '\0';
1562
1563                 /* copy to lowercase buffer */
1564                 exp_lowmemcpy(f->lower+f->printed,
1565                               f->buffer+f->printed,
1566                                         1 + f->size - f->printed);
1567
1568                 f->printed = f->size; /* count'm even if not logging */
1569         }
1570         return(cc);
1571 }
1572
1573 /* when buffer fills, copy second half over first and */
1574 /* continue, so we can do matches over multiple buffers */
1575 void
1576 exp_buffer_shuffle(interp,f,save_flags,array_name,caller_name)
1577 Tcl_Interp *interp;
1578 struct exp_f *f;
1579 int save_flags;
1580 char *array_name;
1581 char *caller_name;
1582 {
1583         char spawn_id[10];      /* enough for a %d */
1584         char match_char;        /* place to hold char temporarily */
1585                                 /* uprooted by a NULL */
1586
1587         int first_half = f->size/2;
1588         int second_half = f->size - first_half;
1589
1590         /*
1591          * allow user to see data we are discarding
1592          */
1593
1594         sprintf(spawn_id,"%d",f-exp_fs);
1595         debuglog("%s: set %s(spawn_id) \"%s\"\r\n",
1596                  caller_name,array_name,dprintify(spawn_id));
1597         Tcl_SetVar2(interp,array_name,"spawn_id",spawn_id,save_flags);
1598
1599         /* temporarily null-terminate buffer in middle */
1600         match_char = f->buffer[first_half];
1601         f->buffer[first_half] = 0;
1602
1603         debuglog("%s: set %s(buffer) \"%s\"\r\n",
1604                  caller_name,array_name,dprintify(f->buffer));
1605         Tcl_SetVar2(interp,array_name,"buffer",f->buffer,save_flags);
1606
1607         /* remove middle-null-terminator */
1608         f->buffer[first_half] = match_char;
1609
1610         memcpy(f->buffer,f->buffer+first_half,second_half);
1611         memcpy(f->lower, f->lower +first_half,second_half);
1612         f->size = second_half;
1613         f->printed -= first_half;
1614         if (f->printed < 0) f->printed = 0;
1615 }
1616
1617 /* map EXP_ style return value to TCL_ style return value */
1618 /* not defined to work on TCL_OK */
1619 int
1620 exp_tcl2_returnvalue(x)
1621 int x;
1622 {
1623         switch (x) {
1624         case TCL_ERROR:                 return EXP_TCLERROR;
1625         case TCL_RETURN:                return EXP_TCLRET;
1626         case TCL_BREAK:                 return EXP_TCLBRK;
1627         case TCL_CONTINUE:              return EXP_TCLCNT;
1628         case EXP_CONTINUE:              return EXP_TCLCNTEXP;
1629         case EXP_CONTINUE_TIMER:        return EXP_TCLCNTTIMER;
1630         case EXP_TCL_RETURN:            return EXP_TCLRETTCL;
1631         }
1632 }
1633
1634 /* map from EXP_ style return value to TCL_ style return values */
1635 int
1636 exp_2tcl_returnvalue(x)
1637 int x;
1638 {
1639         switch (x) {
1640         case EXP_TCLERROR:              return TCL_ERROR;
1641         case EXP_TCLRET:                return TCL_RETURN;
1642         case EXP_TCLBRK:                return TCL_BREAK;
1643         case EXP_TCLCNT:                return TCL_CONTINUE;
1644         case EXP_TCLCNTEXP:             return EXP_CONTINUE;
1645         case EXP_TCLCNTTIMER:           return EXP_CONTINUE_TIMER;
1646         case EXP_TCLRETTCL:             return EXP_TCL_RETURN;
1647         }
1648 }
1649
1650 /* returns # of chars read or (non-positive) error of form EXP_XXX */
1651 /* returns 0 for end of file */
1652 /* If timeout is non-zero, set an alarm before doing the read, else assume */
1653 /* the read will complete immediately. */
1654 /*ARGSUSED*/
1655 static int
1656 exp_i_read(interp,m,timeout,save_flags)
1657 Tcl_Interp *interp;
1658 int m;
1659 int timeout;
1660 int save_flags;
1661 {
1662         struct exp_f *f;
1663         int cc = EXP_TIMEOUT;
1664
1665         f = exp_fs + m;
1666         if (f->size == f->msize) 
1667                 exp_buffer_shuffle(interp,f,save_flags,EXPECT_OUT,"expect");
1668
1669 #ifdef SIMPLE_EVENT
1670  restart:
1671
1672         alarm_fired = FALSE;
1673
1674         if (timeout > -1) {
1675                 signal(SIGALRM,sigalarm_handler);
1676                 alarm((timeout > 0)?timeout:1);
1677         }
1678 #endif
1679
1680         cc = read(m,f->buffer+f->size, f->msize-f->size);
1681         i_read_errno = errno;
1682
1683 #ifdef SIMPLE_EVENT
1684         alarm(0);
1685
1686         if (cc == -1) {
1687                 /* check if alarm went off */
1688                 if (i_read_errno == EINTR) {
1689                         if (alarm_fired) {
1690                                 return EXP_TIMEOUT;
1691                         } else {
1692                                 if (Tcl_AsyncReady()) {
1693                                         int rc = Tcl_AsyncInvoke(interp,TCL_OK);
1694                                         if (rc != TCL_OK) return(exp_tcl2_returnvalue(rc));
1695                                 }
1696                                 if (!f->valid) {
1697                                         exp_error(interp,"spawn_id %d no longer valid",f-exp_fs);
1698                                         return EXP_TCLERROR;
1699                                 }
1700                                 goto restart;
1701                         }
1702                 }
1703         }
1704 #endif
1705         return(cc);
1706 }
1707
1708 /* variables predefined by expect are retrieved using this routine
1709 which looks in the global space if they are not in the local space.
1710 This allows the user to localize them if desired, and also to
1711 avoid having to put "global" in procedure definitions.
1712 */
1713 char *
1714 exp_get_var(interp,var)
1715 Tcl_Interp *interp;
1716 char *var;
1717 {
1718         char *val;
1719
1720         if (NULL != (val = Tcl_GetVar(interp,var,0 /* local */)))
1721                 return(val);
1722         return(Tcl_GetVar(interp,var,TCL_GLOBAL_ONLY));
1723 }
1724
1725 static int
1726 get_timeout(interp)
1727 Tcl_Interp *interp;
1728 {
1729         static int timeout = INIT_EXPECT_TIMEOUT;
1730         char *t;
1731
1732         if (NULL != (t = exp_get_var(interp,EXPECT_TIMEOUT))) {
1733                 timeout = atoi(t);
1734         }
1735         return(timeout);
1736 }
1737
1738 /* make a copy of a linked list (1st arg) and attach to end of another (2nd
1739 arg) */
1740 static int
1741 update_expect_fds(i_list,fd_union)
1742 struct exp_i *i_list;
1743 struct exp_fd_list **fd_union;
1744 {
1745         struct exp_i *p;
1746
1747         /* for each i_list in an expect statement ... */
1748         for (p=i_list;p;p=p->next) {
1749                 struct exp_fd_list *fdl;
1750
1751                 /* for each fd in the i_list */
1752                 for (fdl=p->fd_list;fdl;fdl=fdl->next) {
1753                         struct exp_fd_list *tmpfdl;
1754                         struct exp_fd_list *u;
1755
1756                         if (fdl->fd == EXP_SPAWN_ID_ANY) continue;
1757
1758                         /* check this one against all so far */
1759                         for (u = *fd_union;u;u=u->next) {
1760                                 if (fdl->fd == u->fd) goto found;
1761                         }
1762                         /* if not found, link in as head of list */
1763                         tmpfdl = exp_new_fd(fdl->fd);
1764                         tmpfdl->next = *fd_union;
1765                         *fd_union = tmpfdl;
1766                 found:;
1767                 }
1768         }
1769         return TCL_OK;
1770 }
1771
1772 char *
1773 exp_cmdtype_printable(cmdtype)
1774 int cmdtype;
1775 {
1776         switch (cmdtype) {
1777         case EXP_CMD_FG: return("expect");
1778         case EXP_CMD_BG: return("expect_background");
1779         case EXP_CMD_BEFORE: return("expect_before");
1780         case EXP_CMD_AFTER: return("expect_after");
1781         }
1782 #ifdef LINT
1783         return("unknown expect command");
1784 #endif
1785 }
1786
1787 /* exp_indirect_update2 is called back via Tcl's trace handler whenever */
1788 /* an indirect spawn id list is changed */
1789 /*ARGSUSED*/
1790 static char *
1791 exp_indirect_update2(clientData, interp, name1, name2, flags)
1792 ClientData clientData;
1793 Tcl_Interp *interp;     /* Interpreter containing variable. */
1794 char *name1;            /* Name of variable. */
1795 char *name2;            /* Second part of variable name. */
1796 int flags;              /* Information about what happened. */
1797 {
1798         char *msg;
1799
1800         struct exp_i *exp_i = (struct exp_i *)clientData;
1801         exp_configure_count++;
1802         msg = exp_indirect_update1(interp,&exp_cmds[exp_i->cmdtype],exp_i);
1803
1804         exp_background_filehandlers_run_all();
1805
1806         return msg;
1807 }
1808
1809 static char *
1810 exp_indirect_update1(interp,ecmd,exp_i)
1811 Tcl_Interp *interp;
1812 struct exp_cmd_descriptor *ecmd;
1813 struct exp_i *exp_i;
1814 {
1815         struct exp_fd_list *fdl;        /* temp for interating over fd_list */
1816
1817         /*
1818          * disarm any fd's that lose all their ecases
1819          */
1820
1821         if (ecmd->cmdtype == EXP_CMD_BG) {
1822                 /* clean up each spawn id used by this exp_i */
1823                 for (fdl=exp_i->fd_list;fdl;fdl=fdl->next) {
1824                         int m = fdl->fd;
1825
1826                         if (m == EXP_SPAWN_ID_ANY) continue;
1827
1828                         /* silently skip closed or preposterous fds */
1829                         /* since we're just disabling them anyway */
1830                         /* preposterous fds will have been reported */
1831                         /* by code in next section already */
1832                         if (!exp_fd2f(interp,fdl->fd,1,0,"")) continue;
1833
1834                         exp_fs[m].bg_ecount--;
1835                         if (exp_fs[m].bg_ecount == 0) {
1836                                 exp_disarm_background_filehandler(m);
1837                                 exp_fs[m].bg_interp = 0;
1838                         }
1839                 }
1840         }
1841
1842         /*
1843          * reread indirect variable
1844          */
1845
1846         exp_i_update(interp,exp_i);
1847
1848         /*
1849          * check validity of all fd's in variable
1850          */
1851
1852         for (fdl=exp_i->fd_list;fdl;fdl=fdl->next) {
1853                 /* validate all input descriptors */
1854                 if (fdl->fd == EXP_SPAWN_ID_ANY) continue;
1855
1856                 if (!exp_fd2f(interp,fdl->fd,1,1,
1857                                 exp_cmdtype_printable(ecmd->cmdtype))) {
1858                         static char msg[200];
1859                         sprintf(msg,"%s from indirect variable (%s)",
1860                                 interp->result,exp_i->variable);
1861                         return msg;
1862                 }
1863         }
1864
1865         /* for each spawn id in list, arm if necessary */
1866         if (ecmd->cmdtype == EXP_CMD_BG) {
1867                 fd_list_arm(interp,exp_i->fd_list);
1868         }
1869
1870         return (char *)0;
1871 }
1872
1873 void
1874 exp_background_filehandlers_run_all()
1875 {
1876         int m;
1877         struct exp_f *f;
1878
1879         /* kick off any that already have input waiting */
1880         for (m=0;m<=exp_fd_max;m++) {
1881                 f = exp_fs + m;
1882                 if (!f->valid) continue;
1883
1884                 /* is bg_interp the best way to check if armed? */
1885                 if (f->bg_interp && (f->size > 0)) {
1886                         exp_background_filehandler((ClientData)f->fd_ptr,0/*ignored*/);
1887                 }
1888         }
1889 }
1890
1891 /* this function is called from the background when input arrives */
1892 /*ARGSUSED*/
1893 void
1894 exp_background_filehandler(clientData,mask)
1895 ClientData clientData;
1896 int mask;
1897 {
1898         int m;
1899
1900         Tcl_Interp *interp;
1901         int cc;                 /* number of chars returned in a single read */
1902                                 /* or negative EXP_whatever */
1903         struct exp_f *f;                /* file associated with master */
1904
1905         int i;                  /* trusty temporary */
1906
1907         struct eval_out eo;     /* final case of interest */
1908         struct exp_f *last_f;   /* for differentiating when multiple f's */
1909                                 /* to print out better debugging messages */
1910         int last_case;          /* as above but for case */
1911
1912         /* restore our environment */
1913         m = *(int *)clientData;
1914         f = exp_fs + m;
1915         interp = f->bg_interp;
1916
1917         /* temporarily prevent this handler from being invoked again */
1918         exp_block_background_filehandler(m);
1919
1920         /* if mask == 0, then we've been called because the patterns changed */
1921         /* not because the waiting data has changed, so don't actually do */
1922         /* any I/O */
1923
1924         if (mask == 0) {
1925                 cc = 0;
1926         } else {
1927                 cc = expect_read(interp,(int *)0,mask,&m,EXP_TIME_INFINITY,0);
1928         }
1929
1930 do_more_data:
1931         eo.e = 0;               /* no final case yet */
1932         eo.f = 0;               /* no final file selected yet */
1933         eo.match = 0;           /* nothing matched yet */
1934
1935         /* force redisplay of buffer when debugging */
1936         last_f = 0;
1937
1938         if (cc == EXP_EOF) {
1939                 /* do nothing */
1940         } else if (cc < 0) { /* EXP_TCLERROR or any other weird value*/
1941                 goto finish;
1942                 /* if we were going to do this right, we should */
1943                 /* differentiate between things like HP ioctl-open-traps */
1944                 /* that fall out here and should rightfully be ignored */
1945                 /* and real errors that should be reported.  Come to */
1946                 /* think of it, the only errors will come from HP */
1947                 /* ioctl handshake botches anyway. */
1948         } else {
1949                 /* normal case, got data */
1950                 /* new data if cc > 0, same old data if cc == 0 */
1951
1952                 /* below here, cc as general status */
1953                 cc = EXP_NOMATCH;
1954         }
1955
1956         cc = eval_cases(interp,&exp_cmds[EXP_CMD_BEFORE],
1957                         m,&eo,&last_f,&last_case,cc,&m,1,"_background");
1958         cc = eval_cases(interp,&exp_cmds[EXP_CMD_BG],
1959                         m,&eo,&last_f,&last_case,cc,&m,1,"_background");
1960         cc = eval_cases(interp,&exp_cmds[EXP_CMD_AFTER],
1961                         m,&eo,&last_f,&last_case,cc,&m,1,"_background");
1962         if (cc == EXP_TCLERROR) {
1963                 /* only likely problem here is some internal regexp botch */
1964                 Tcl_BackgroundError(interp);
1965                 goto finish;
1966         }
1967         /* special eof code that cannot be done in eval_cases */
1968         /* or above, because it would then be executed several times */
1969         if (cc == EXP_EOF) {
1970                 eo.f = exp_fs + m;
1971                 eo.match = eo.f->size;
1972                 eo.buffer = eo.f->buffer;
1973                 debuglog("expect_background: read eof\r\n");
1974                 goto matched;
1975         }
1976         if (!eo.e) {
1977                 /* if we get here, there must not have been a match */
1978                 goto finish;
1979         }
1980
1981  matched:
1982 #define out(i,val)  debuglog("expect_background: set %s(%s) \"%s\"\r\n",EXPECT_OUT,i, \
1983                                                 dprintify(val)); \
1984                     Tcl_SetVar2(interp,EXPECT_OUT,i,val,TCL_GLOBAL_ONLY);
1985         {
1986 /*              int iwrite = FALSE;*/   /* write spawn_id? */
1987                 char *body = 0;
1988                 char *buffer;   /* pointer to normal or lowercased data */
1989                 struct ecase *e = 0;    /* points to current ecase */
1990                 int match = -1;         /* characters matched */
1991                 char match_char;        /* place to hold char temporarily */
1992                                         /* uprooted by a NULL */
1993                 char *eof_body = 0;
1994
1995                 if (eo.e) {
1996                         e = eo.e;
1997                         body = e->body;
1998 /*                      iwrite = e->iwrite;*/
1999                         if (cc != EXP_TIMEOUT) {
2000                                 f = eo.f;
2001                                 match = eo.match;
2002                                 buffer = eo.buffer;
2003                         }
2004 #if 0
2005                         if (e->timestamp) {
2006                                 char value[20];
2007
2008                                 time(&current_time);
2009                                 elapsed_time = current_time - start_time;
2010                                 elapsed_time_total = current_time - start_time_total;
2011                                 sprintf(value,"%d",elapsed_time);
2012                                 out("seconds",value);
2013                                 sprintf(value,"%d",elapsed_time_total);
2014                                 out("seconds_total",value);
2015                                 /* deprecated */
2016                                 exp_timestamp(interp,&current_time,EXPECT_OUT);
2017                         }
2018 #endif
2019                 } else if (cc == EXP_EOF) {
2020                         /* read an eof but no user-supplied case */
2021                         f = eo.f;
2022                         match = eo.match;
2023                         buffer = eo.buffer;
2024                 }                       
2025
2026                 if (match >= 0) {
2027                         char name[20], value[20];
2028
2029                         if (e && e->use == PAT_RE) {
2030                                 Expect_regexp *re = e->re;
2031
2032                                 for (i=0;i<NSUBEXP;i++) {
2033                                         int offset;
2034
2035                                         if (re->startp[i] == 0) continue;
2036
2037                                         if (e->indices) {
2038                                           /* start index */
2039                                           sprintf(name,"%d,start",i);
2040                                           offset = re->startp[i]-buffer;
2041                                           sprintf(value,"%d",offset);
2042                                           out(name,value);
2043
2044                                           /* end index */
2045                                           sprintf(name,"%d,end",i);
2046                                           sprintf(value,"%d",
2047                                                 re->endp[i]-buffer-1);
2048                                           out(name,value);
2049                                         }
2050
2051                                         /* string itself */
2052                                         sprintf(name,"%d,string",i);
2053
2054                                         /* temporarily null-terminate in */
2055                                         /* middle */
2056                                         match_char = *re->endp[i];
2057                                         *re->endp[i] = 0;
2058                                         out(name,re->startp[i]);
2059                                         *re->endp[i] = match_char;
2060                                 }
2061                                 /* redefine length of string that */
2062                                 /* matched for later extraction */
2063                                 match = re->endp[0]-buffer;
2064                         } else if (e && (e->use == PAT_GLOB || e->use == PAT_EXACT)) {
2065                                 char *str;
2066
2067                                 if (e->indices) {
2068                                   /* start index */
2069                                   sprintf(value,"%d",e->simple_start);
2070                                   out("0,start",value);
2071
2072                                   /* end index */
2073                                   sprintf(value,"%d",e->simple_start + match - 1);
2074                                   out("0,end",value);
2075                                 }
2076
2077                                 /* string itself */
2078                                 str = f->buffer + e->simple_start;
2079                                 /* temporarily null-terminate in middle */
2080                                 match_char = str[match];
2081                                 str[match] = 0;
2082                                 out("0,string",str);
2083                                 str[match] = match_char;
2084
2085                                 /* redefine length of string that */
2086                                 /* matched for later extraction */
2087                                 match += e->simple_start;
2088                         } else if (e && e->use == PAT_NULL && e->indices) {
2089                                 /* start index */
2090                                 sprintf(value,"%d",match-1);
2091                                 out("0,start",value);
2092                                 /* end index */
2093                                 sprintf(value,"%d",match-1);
2094                                 out("0,end",value);
2095                         } else if (e && e->use == PAT_FULLBUFFER) {
2096                                 debuglog("expect_background: full buffer\r\n");
2097                         }
2098                 }
2099
2100                 /* this is broken out of (match > 0) (above) since it can */
2101                 /* that an EOF occurred with match == 0 */
2102                 if (eo.f) {
2103                         char spawn_id[10];      /* enough for a %d */
2104
2105 /*                      if (iwrite) {*/
2106                                 sprintf(spawn_id,"%d",f-exp_fs);
2107                                 out("spawn_id",spawn_id);
2108 /*                      }*/
2109
2110                         /* save buf[0..match] */
2111                         /* temporarily null-terminate string in middle */
2112                         match_char = f->buffer[match];
2113                         f->buffer[match] = 0;
2114                         out("buffer",f->buffer);
2115                         /* remove middle-null-terminator */
2116                         f->buffer[match] = match_char;
2117
2118                         /* "!e" means no case matched - transfer by default */
2119                         if (!e || e->transfer) {
2120                                 /* delete matched chars from input buffer */
2121                                 f->size -= match;
2122                                 f->printed -= match;
2123                                 if (f->size != 0) {
2124                                    memmove(f->buffer,f->buffer+match,f->size);
2125                                    memmove(f->lower,f->lower+match,f->size);
2126                                 }
2127                                 f->buffer[f->size] = '\0';
2128                                 f->lower[f->size] = '\0';
2129                         }
2130
2131                         if (cc == EXP_EOF) {
2132                                 /* exp_close() deletes all background bodies */
2133                                 /* so save eof body temporarily */
2134                                 if (body) {
2135                                         eof_body = ckalloc(strlen(body)+1);
2136                                         strcpy(eof_body,body);
2137                                         body = eof_body;
2138                                 }
2139
2140                                 exp_close(interp,f - exp_fs);
2141                         }
2142
2143                 }
2144
2145                 if (body) {
2146                         int result = Tcl_GlobalEval(interp,body);
2147                         if (result != TCL_OK) Tcl_BackgroundError(interp);
2148
2149                         if (eof_body) ckfree(eof_body);
2150                 }
2151
2152
2153                 /*
2154                  * Event handler will not call us back if there is more input
2155                  * pending but it has already arrived.  bg_status will be
2156                  * "blocked" only if armed.
2157                  */
2158                 if (exp_fs[m].valid && (exp_fs[m].bg_status == blocked)
2159                  && (f->size > 0)) {
2160                         cc = f->size;
2161                         goto do_more_data;
2162                 }
2163         }
2164  finish:
2165         /* fd could have gone away, so check before using */
2166         if (exp_fs[m].valid)
2167                 exp_unblock_background_filehandler(m);
2168 }
2169 #undef out
2170
2171 /*ARGSUSED*/
2172 int
2173 Exp_ExpectCmd(clientData, interp, argc, argv)
2174 ClientData clientData;
2175 Tcl_Interp *interp;
2176 int argc;
2177 char **argv;
2178 {
2179         int cc;                 /* number of chars returned in a single read */
2180                                 /* or negative EXP_whatever */
2181         int m;                  /* before doing an actual read, attempt */
2182                                 /* to match upon any spawn_id */
2183         struct exp_f *f;                /* file associated with master */
2184
2185         int i;                  /* trusty temporary */
2186         struct exp_cmd_descriptor eg;
2187         struct exp_fd_list *fd_list;    /* list of masters to watch */
2188         struct exp_fd_list *fdl;        /* temp for interating over fd_list */
2189         int *masters;           /* array of masters to watch */
2190         int mcount;             /* number of masters to watch */
2191
2192         struct eval_out eo;     /* final case of interest */
2193
2194         int result;             /* Tcl result */
2195
2196         time_t start_time_total;/* time at beginning of this procedure */
2197         time_t start_time = 0;  /* time when restart label hit */
2198         time_t current_time = 0;/* current time (when we last looked)*/
2199         time_t end_time;        /* future time at which to give up */
2200         time_t elapsed_time_total;/* time from now to match/fail/timeout */
2201         time_t elapsed_time;    /* time from restart to (ditto) */
2202
2203         struct exp_f *last_f;   /* for differentiating when multiple f's */
2204                                 /* to print out better debugging messages */
2205         int last_case;          /* as above but for case */
2206         int first_time = 1;     /* if not "restarted" */
2207
2208         int key;                /* identify this expect command instance */
2209         int configure_count;    /* monitor exp_configure_count */
2210
2211         int timeout;            /* seconds */
2212         int remtime;            /* remaining time in timeout */
2213         int reset_timer;        /* should timer be reset after continue? */
2214
2215         if ((argc == 2) && exp_one_arg_braced(argv[1])) {
2216                 return(exp_eval_with_one_arg(clientData,interp,argv));
2217         } else if ((argc == 3) && streq(argv[1],"-brace")) {
2218                 char *new_argv[2];
2219                 new_argv[0] = argv[0];
2220                 new_argv[1] = argv[2];
2221                 return(exp_eval_with_one_arg(clientData,interp,new_argv));
2222         }
2223
2224         time(&start_time_total);
2225         start_time = start_time_total;
2226         reset_timer = TRUE;
2227
2228         /* make arg list for processing cases */
2229         /* do it dynamically, since expect can be called recursively */
2230
2231         exp_cmd_init(&eg,EXP_CMD_FG,EXP_TEMPORARY);
2232         fd_list = 0;
2233         masters = 0;
2234         if (TCL_ERROR == parse_expect_args(interp,&eg,
2235                                         *(int *)clientData,argc,argv))
2236                 return TCL_ERROR;
2237
2238  restart_with_update:
2239         /* validate all descriptors */
2240         /* and flatten fds into array */
2241
2242         if ((TCL_ERROR == update_expect_fds(exp_cmds[EXP_CMD_BEFORE].i_list,&fd_list))
2243          || (TCL_ERROR == update_expect_fds(exp_cmds[EXP_CMD_AFTER].i_list, &fd_list))
2244          || (TCL_ERROR == update_expect_fds(eg.i_list,&fd_list))) {
2245                 result = TCL_ERROR;
2246                 goto cleanup;
2247         }
2248
2249         /* declare ourselves "in sync" with external view of close/indirect */
2250         configure_count = exp_configure_count;
2251
2252         /* count and validate fd_list */
2253         mcount = 0;
2254         for (fdl=fd_list;fdl;fdl=fdl->next) {
2255                 mcount++;
2256                 /* validate all input descriptors */
2257                 if (!exp_fd2f(interp,fdl->fd,1,1,"expect")) {
2258                         result = TCL_ERROR;
2259                         goto cleanup;
2260                 }
2261         }
2262
2263         /* make into an array */
2264         masters = (int *)ckalloc(mcount * sizeof(int));
2265         for (fdl=fd_list,i=0;fdl;fdl=fdl->next,i++) {
2266                 masters[i] = fdl->fd;
2267         }
2268
2269      restart:
2270         if (first_time) first_time = 0;
2271         else time(&start_time);
2272
2273         if (eg.timeout_specified_by_flag) {
2274                 timeout = eg.timeout;
2275         } else {
2276                 /* get the latest timeout */
2277                 timeout = get_timeout(interp);
2278         }
2279
2280         key = expect_key++;
2281
2282         result = TCL_OK;
2283         last_f = 0;
2284
2285         /* end of restart code */
2286
2287         eo.e = 0;               /* no final case yet */
2288         eo.f = 0;               /* no final file selected yet */
2289         eo.match = 0;           /* nothing matched yet */
2290
2291         /* timeout code is a little tricky, be very careful changing it */
2292         if (timeout != EXP_TIME_INFINITY) {
2293                 /* if exp_continue -continue_timer, do not update end_time */
2294                 if (reset_timer) {
2295                         time(&current_time);
2296                         end_time = current_time + timeout;
2297                 } else {
2298                         reset_timer = TRUE;
2299                 }
2300         }
2301
2302         /* remtime and current_time updated at bottom of loop */
2303         remtime = timeout;
2304
2305         for (;;) {
2306                 if ((timeout != EXP_TIME_INFINITY) && (remtime < 0)) {
2307                         cc = EXP_TIMEOUT;
2308                 } else {
2309                         cc = expect_read(interp,masters,mcount,&m,remtime,key);
2310                 }
2311
2312                 /*SUPPRESS 530*/
2313                 if (cc == EXP_EOF) {
2314                         /* do nothing */
2315                 } else if (cc == EXP_TIMEOUT) {
2316                         debuglog("expect: timed out\r\n");
2317                 } else if (cc == EXP_RECONFIGURE) {
2318                         reset_timer = FALSE;
2319                         goto restart_with_update;
2320                 } else if (cc < 0) { /* EXP_TCLERROR or any other weird value*/
2321                         goto error;
2322                 } else {
2323                         /* new data if cc > 0, same old data if cc == 0 */
2324
2325                         f = exp_fs + m;
2326
2327                         /* below here, cc as general status */
2328                         cc = EXP_NOMATCH;
2329
2330                         /* force redisplay of buffer when debugging */
2331                         last_f = 0;
2332                 }
2333
2334                 cc = eval_cases(interp,&exp_cmds[EXP_CMD_BEFORE],
2335                         m,&eo,&last_f,&last_case,cc,masters,mcount,"");
2336                 cc = eval_cases(interp,&eg,
2337                         m,&eo,&last_f,&last_case,cc,masters,mcount,"");
2338                 cc = eval_cases(interp,&exp_cmds[EXP_CMD_AFTER],
2339                         m,&eo,&last_f,&last_case,cc,masters,mcount,"");
2340                 if (cc == EXP_TCLERROR) goto error;
2341                 /* special eof code that cannot be done in eval_cases */
2342                 /* or above, because it would then be executed several times */
2343                 if (cc == EXP_EOF) {
2344                         eo.f = exp_fs + m;
2345                         eo.match = eo.f->size;
2346                         eo.buffer = eo.f->buffer;
2347                         debuglog("expect: read eof\r\n");
2348                         break;
2349                 } else if (cc == EXP_TIMEOUT) break;
2350                 /* break if timeout or eof and failed to find a case for it */
2351
2352                 if (eo.e) break;
2353
2354                 /* no match was made with current data, force a read */
2355                 f->force_read = TRUE;
2356
2357                 if (timeout != EXP_TIME_INFINITY) {
2358                         time(&current_time);
2359                         remtime = end_time - current_time;
2360                 }
2361         }
2362
2363         goto done;
2364
2365 error:
2366         result = exp_2tcl_returnvalue(cc);
2367  done:
2368 #define out(i,val)  debuglog("expect: set %s(%s) \"%s\"\r\n",EXPECT_OUT,i, \
2369                                                 dprintify(val)); \
2370                     Tcl_SetVar2(interp,EXPECT_OUT,i,val,0);
2371
2372         if (result != TCL_ERROR) {
2373 /*              int iwrite = FALSE;*/   /* write spawn_id? */
2374                 char *body = 0;
2375                 char *buffer;   /* pointer to normal or lowercased data */
2376                 struct ecase *e = 0;    /* points to current ecase */
2377                 int match = -1;         /* characters matched */
2378                 char match_char;        /* place to hold char temporarily */
2379                                         /* uprooted by a NULL */
2380                 char *eof_body = 0;
2381
2382                 if (eo.e) {
2383                         e = eo.e;
2384                         body = e->body;
2385 /*                      iwrite = e->iwrite;*/
2386                         if (cc != EXP_TIMEOUT) {
2387                                 f = eo.f;
2388                                 match = eo.match;
2389                                 buffer = eo.buffer;
2390                         }
2391                         if (e->timestamp) {
2392                                 char value[20];
2393
2394                                 time(&current_time);
2395                                 elapsed_time = current_time - start_time;
2396                                 elapsed_time_total = current_time - start_time_total;
2397                                 sprintf(value,"%d",elapsed_time);
2398                                 out("seconds",value);
2399                                 sprintf(value,"%d",elapsed_time_total);
2400                                 out("seconds_total",value);
2401
2402                                 /* deprecated */
2403                                 exp_timestamp(interp,&current_time,EXPECT_OUT);
2404                         }
2405                 } else if (cc == EXP_EOF) {
2406                         /* read an eof but no user-supplied case */
2407                         f = eo.f;
2408                         match = eo.match;
2409                         buffer = eo.buffer;
2410                 }                       
2411
2412                 if (match >= 0) {
2413                         char name[20], value[20];
2414
2415                         if (e && e->use == PAT_RE) {
2416                                 Expect_regexp *re = e->re;
2417
2418                                 for (i=0;i<NSUBEXP;i++) {
2419                                         int offset;
2420
2421                                         if (re->startp[i] == 0) continue;
2422
2423                                         if (e->indices) {
2424                                           /* start index */
2425                                           sprintf(name,"%d,start",i);
2426                                           offset = re->startp[i]-buffer;
2427                                           sprintf(value,"%d",offset);
2428                                           out(name,value);
2429
2430                                           /* end index */
2431                                           sprintf(name,"%d,end",i);
2432                                           sprintf(value,"%d",
2433                                                 re->endp[i]-buffer-1);
2434                                           out(name,value);
2435                                         }
2436
2437                                         /* string itself */
2438                                         sprintf(name,"%d,string",i);
2439
2440                                         /* temporarily null-terminate in */
2441                                         /* middle */
2442                                         match_char = *re->endp[i];
2443                                         *re->endp[i] = 0;
2444                                         out(name,re->startp[i]);
2445                                         *re->endp[i] = match_char;
2446                                 }
2447                                 /* redefine length of string that */
2448                                 /* matched for later extraction */
2449                                 match = re->endp[0]-buffer;
2450                         } else if (e && (e->use == PAT_GLOB || e->use == PAT_EXACT)) {
2451                                 char *str;
2452
2453                                 if (e->indices) {
2454                                   /* start index */
2455                                   sprintf(value,"%d",e->simple_start);
2456                                   out("0,start",value);
2457
2458                                   /* end index */
2459                                   sprintf(value,"%d",e->simple_start + match - 1);
2460                                   out("0,end",value);
2461                                 }
2462
2463                                 /* string itself */
2464                                 str = f->buffer + e->simple_start;
2465                                 /* temporarily null-terminate in middle */
2466                                 match_char = str[match];
2467                                 str[match] = 0;
2468                                 out("0,string",str);
2469                                 str[match] = match_char;
2470
2471                                 /* redefine length of string that */
2472                                 /* matched for later extraction */
2473                                 match += e->simple_start;
2474                         } else if (e && e->use == PAT_NULL && e->indices) {
2475                                 /* start index */
2476                                 sprintf(value,"%d",match-1);
2477                                 out("0,start",value);
2478                                 /* end index */
2479                                 sprintf(value,"%d",match-1);
2480                                 out("0,end",value);
2481                         } else if (e && e->use == PAT_FULLBUFFER) {
2482                                 debuglog("expect: full buffer\r\n");
2483                         }
2484                 }
2485
2486                 /* this is broken out of (match > 0) (above) since it can */
2487                 /* that an EOF occurred with match == 0 */
2488                 if (eo.f) {
2489                         char spawn_id[10];      /* enough for a %d */
2490
2491 /*                      if (iwrite) {*/
2492                                 sprintf(spawn_id,"%d",f-exp_fs);
2493                                 out("spawn_id",spawn_id);
2494 /*                      }*/
2495
2496                         /* save buf[0..match] */
2497                         /* temporarily null-terminate string in middle */
2498                         match_char = f->buffer[match];
2499                         f->buffer[match] = 0;
2500                         out("buffer",f->buffer);
2501                         /* remove middle-null-terminator */
2502                         f->buffer[match] = match_char;
2503
2504                         /* "!e" means no case matched - transfer by default */
2505                         if (!e || e->transfer) {
2506                                 /* delete matched chars from input buffer */
2507                                 f->size -= match;
2508                                 f->printed -= match;
2509                                 if (f->size != 0) {
2510                                    memmove(f->buffer,f->buffer+match,f->size);
2511                                    memmove(f->lower,f->lower+match,f->size);
2512                                 }
2513                                 f->buffer[f->size] = '\0';
2514                                 f->lower[f->size] = '\0';
2515                         }
2516
2517                         if (cc == EXP_EOF) {
2518                                 /* exp_close() deletes all background bodies */
2519                                 /* so save eof body temporarily */
2520                                 if (body) {
2521                                         eof_body = ckalloc(strlen(body)+1);
2522                                         strcpy(eof_body,body);
2523                                         body = eof_body;
2524                                 }
2525
2526                                 exp_close(interp,f - exp_fs);
2527                         }
2528
2529                 }
2530
2531                 if (body) {
2532                         result = Tcl_Eval(interp,body);
2533
2534                         if (eof_body) ckfree(eof_body);
2535                 }
2536         }
2537
2538  cleanup:
2539         if (result == EXP_CONTINUE_TIMER) {
2540                 reset_timer = FALSE;
2541                 result = EXP_CONTINUE;
2542         }
2543
2544         if ((result == EXP_CONTINUE)
2545              && (configure_count == exp_configure_count)) {
2546                 debuglog("expect: continuing expect\r\n");
2547                 goto restart;
2548         }
2549
2550         if (fd_list) {
2551                 exp_free_fd(fd_list);
2552                 fd_list = 0;
2553         }
2554         if (masters) {
2555                 ckfree((char *)masters);
2556                 masters = 0;
2557         }
2558
2559         if (result == EXP_CONTINUE) {
2560                 debuglog("expect: continuing expect after update\r\n");
2561                 goto restart_with_update;
2562         }
2563
2564         free_ecases(interp,&eg,0);      /* requires i_lists to be avail */
2565         exp_free_i(interp,eg.i_list,exp_indirect_update2);
2566
2567         return(result);
2568 }
2569 #undef out
2570
2571 /* beginning of deprecated code */
2572
2573 #define out(elt)                Tcl_SetVar2(interp,array,elt,ascii,0);
2574 void
2575 exp_timestamp(interp,timeval,array)
2576 Tcl_Interp *interp;
2577 time_t *timeval;
2578 char *array;
2579 {
2580         struct tm *tm;
2581         char *ascii;
2582
2583         tm = localtime(timeval);        /* split */
2584         ascii = asctime(tm);            /* print */
2585         ascii[24] = '\0';               /* zap trailing \n */
2586
2587         out("timestamp");
2588
2589         sprintf(ascii,"%ld",*timeval);
2590         out("epoch");
2591
2592         sprintf(ascii,"%d",tm->tm_sec);
2593         out("sec");
2594         sprintf(ascii,"%d",tm->tm_min);
2595         out("min");
2596         sprintf(ascii,"%d",tm->tm_hour);
2597         out("hour");
2598         sprintf(ascii,"%d",tm->tm_mday);
2599         out("mday");
2600         sprintf(ascii,"%d",tm->tm_mon);
2601         out("mon");
2602         sprintf(ascii,"%d",tm->tm_year);
2603         out("year");
2604         sprintf(ascii,"%d",tm->tm_wday);
2605         out("wday");
2606         sprintf(ascii,"%d",tm->tm_yday);
2607         out("yday");
2608         sprintf(ascii,"%d",tm->tm_isdst);
2609         out("isdst");
2610 }
2611 /* end of deprecated code */
2612
2613 /*ARGSUSED*/
2614 static int
2615 Exp_TimestampCmd(clientData, interp, argc, argv)
2616 ClientData clientData;
2617 Tcl_Interp *interp;
2618 int argc;
2619 char **argv;
2620 {
2621         char *format = 0;
2622         time_t seconds = -1;
2623         int gmt = FALSE;        /* local time by default */
2624         struct tm *tm;
2625         Tcl_DString dstring;
2626
2627         argc--; argv++;
2628
2629         while (*argv) {
2630                 if (streq(*argv,"-format")) {
2631                         argc--; argv++;
2632                         if (!*argv) goto usage_error;
2633                         format = *argv;
2634                         argc--; argv++;
2635                 } else if (streq(*argv,"-seconds")) {
2636                         argc--; argv++;
2637                         if (!*argv) goto usage_error;
2638                         seconds = atoi(*argv);
2639                         argc--; argv++;
2640                 } else if (streq(*argv,"-gmt")) {
2641                         gmt = TRUE;
2642                         argc--; argv++;
2643                 } else break;
2644         }
2645
2646         if (argc) goto usage_error;
2647
2648         if (seconds == -1) {
2649                 time(&seconds);
2650         }
2651
2652         Tcl_DStringInit(&dstring);
2653
2654         if (format) {
2655                 if (gmt) {
2656                         tm = gmtime(&seconds);
2657                 } else {
2658                         tm = localtime(&seconds);
2659                 }
2660 /*              exp_strftime(interp->result,TCL_RESULT_SIZE,format,tm);*/
2661                 exp_strftime(format,tm,&dstring);
2662                 Tcl_DStringResult(interp,&dstring);
2663         } else {
2664                 sprintf(interp->result,"%ld",seconds);
2665         }
2666         
2667         return TCL_OK;
2668  usage_error:
2669         exp_error(interp,"args: [-seconds #] [-format format]");
2670         return TCL_ERROR;
2671
2672 }
2673
2674 /* lowmemcpy - like memcpy but it lowercases result */
2675 void
2676 exp_lowmemcpy(dest,src,n)
2677 char *dest;
2678 char *src;
2679 int n;
2680 {
2681         for (;n>0;n--) {
2682                 *dest = ((isascii(*src) && isupper(*src))?tolower(*src):*src);
2683                 src++;  dest++;
2684         }
2685 }
2686
2687 /*ARGSUSED*/
2688 int
2689 Exp_MatchMaxCmd(clientData,interp,argc,argv)
2690 ClientData clientData;
2691 Tcl_Interp *interp;
2692 int argc;
2693 char **argv;
2694 {
2695         int size = -1;
2696         int m = -1;
2697         struct exp_f *f;
2698         int Default = FALSE;
2699
2700         argc--; argv++;
2701
2702         for (;argc>0;argc--,argv++) {
2703                 if (streq(*argv,"-d")) {
2704                         Default = TRUE;
2705                 } else if (streq(*argv,"-i")) {
2706                         argc--;argv++;
2707                         if (argc < 1) {
2708                                 exp_error(interp,"-i needs argument");
2709                                 return(TCL_ERROR);
2710                         }
2711                         m = atoi(*argv);
2712                 } else break;
2713         }
2714
2715         if (!Default) {
2716                 if (m == -1) {
2717                         if (!(f = exp_update_master(interp,&m,0,0)))
2718                                 return(TCL_ERROR);
2719                 } else {
2720                         if (!(f = exp_fd2f(interp,m,0,0,"match_max")))
2721                                 return(TCL_ERROR);
2722                 }
2723         } else if (m != -1) {
2724                 exp_error(interp,"cannot do -d and -i at the same time");
2725                 return(TCL_ERROR);
2726         }
2727
2728         if (argc == 0) {
2729                 if (Default) {
2730                         size = exp_default_match_max;
2731                 } else {
2732                         size = f->umsize;
2733                 }
2734                 sprintf(interp->result,"%d",size);
2735                 return(TCL_OK);
2736         }
2737
2738         if (argc > 1) {
2739                 exp_error(interp,"too many arguments");
2740                 return(TCL_OK);
2741         }
2742
2743         /* all that's left is to set the size */
2744         size = atoi(argv[0]);
2745         if (size <= 0) {
2746                 exp_error(interp,"must be positive");
2747                 return(TCL_ERROR);
2748         }
2749
2750         if (Default) exp_default_match_max = size;
2751         else f->umsize = size;
2752
2753         return(TCL_OK);
2754 }
2755
2756 /*ARGSUSED*/
2757 int
2758 Exp_RemoveNullsCmd(clientData,interp,argc,argv)
2759 ClientData clientData;
2760 Tcl_Interp *interp;
2761 int argc;
2762 char **argv;
2763 {
2764         int value = -1;
2765         int m = -1;
2766         struct exp_f *f;
2767         int Default = FALSE;
2768
2769         argc--; argv++;
2770
2771         for (;argc>0;argc--,argv++) {
2772                 if (streq(*argv,"-d")) {
2773                         Default = TRUE;
2774                 } else if (streq(*argv,"-i")) {
2775                         argc--;argv++;
2776                         if (argc < 1) {
2777                                 exp_error(interp,"-i needs argument");
2778                                 return(TCL_ERROR);
2779                         }
2780                         m = atoi(*argv);
2781                 } else break;
2782         }
2783
2784         if (!Default) {
2785                 if (m == -1) {
2786                         if (!(f = exp_update_master(interp,&m,0,0)))
2787                                 return(TCL_ERROR);
2788                 } else {
2789                         if (!(f = exp_fd2f(interp,m,0,0,"remove_nulls")))
2790                                 return(TCL_ERROR);
2791                 }
2792         } else if (m != -1) {
2793                 exp_error(interp,"cannot do -d and -i at the same time");
2794                 return(TCL_ERROR);
2795         }
2796
2797         if (argc == 0) {
2798                 if (Default) {
2799                         value = exp_default_match_max;
2800                 } else {
2801                         value = f->rm_nulls;
2802                 }
2803                 sprintf(interp->result,"%d",value);
2804                 return(TCL_OK);
2805         }
2806
2807         if (argc > 1) {
2808                 exp_error(interp,"too many arguments");
2809                 return(TCL_OK);
2810         }
2811
2812         /* all that's left is to set the value */
2813         value = atoi(argv[0]);
2814         if (value != 0 && value != 1) {
2815                 exp_error(interp,"must be 0 or 1");
2816                 return(TCL_ERROR);
2817         }
2818
2819         if (Default) exp_default_rm_nulls = value;
2820         else f->rm_nulls = value;
2821
2822         return(TCL_OK);
2823 }
2824
2825 /*ARGSUSED*/
2826 int
2827 Exp_ParityCmd(clientData,interp,argc,argv)
2828 ClientData clientData;
2829 Tcl_Interp *interp;
2830 int argc;
2831 char **argv;
2832 {
2833         int parity;
2834         int m = -1;
2835         struct exp_f *f;
2836         int Default = FALSE;
2837
2838         argc--; argv++;
2839
2840         for (;argc>0;argc--,argv++) {
2841                 if (streq(*argv,"-d")) {
2842                         Default = TRUE;
2843                 } else if (streq(*argv,"-i")) {
2844                         argc--;argv++;
2845                         if (argc < 1) {
2846                                 exp_error(interp,"-i needs argument");
2847                                 return(TCL_ERROR);
2848                         }
2849                         m = atoi(*argv);
2850                 } else break;
2851         }
2852
2853         if (!Default) {
2854                 if (m == -1) {
2855                         if (!(f = exp_update_master(interp,&m,0,0)))
2856                                 return(TCL_ERROR);
2857                 } else {
2858                         if (!(f = exp_fd2f(interp,m,0,0,"parity")))
2859                                 return(TCL_ERROR);
2860                 }
2861         } else if (m != -1) {
2862                 exp_error(interp,"cannot do -d and -i at the same time");
2863                 return(TCL_ERROR);
2864         }
2865
2866         if (argc == 0) {
2867                 if (Default) {
2868                         parity = exp_default_parity;
2869                 } else {
2870                         parity = f->parity;
2871                 }
2872                 sprintf(interp->result,"%d",parity);
2873                 return(TCL_OK);
2874         }
2875
2876         if (argc > 1) {
2877                 exp_error(interp,"too many arguments");
2878                 return(TCL_OK);
2879         }
2880
2881         /* all that's left is to set the parity */
2882         parity = atoi(argv[0]);
2883
2884         if (Default) exp_default_parity = parity;
2885         else f->parity = parity;
2886
2887         return(TCL_OK);
2888 }
2889
2890 #if DEBUG_PERM_ECASES
2891 /* This big chunk of code is just for debugging the permanent */
2892 /* expect cases */
2893 void
2894 exp_fd_print(fdl)
2895 struct exp_fd_list *fdl;
2896 {
2897         if (!fdl) return;
2898         printf("%d ",fdl->fd);
2899         exp_fd_print(fdl->next);
2900 }
2901
2902 void
2903 exp_i_print(exp_i)
2904 struct exp_i *exp_i;
2905 {
2906         if (!exp_i) return;
2907         printf("exp_i %x",exp_i);
2908         printf((exp_i->direct == EXP_DIRECT)?" direct":" indirect");
2909         printf((exp_i->duration == EXP_PERMANENT)?" perm":" tmp");
2910         printf("  ecount = %d\n",exp_i->ecount);
2911         printf("variable %s, value %s\n",
2912                 ((exp_i->variable)?exp_i->variable:"--"),
2913                 ((exp_i->value)?exp_i->value:"--"));
2914         printf("fds: ");
2915         exp_fd_print(exp_i->fd_list); printf("\n");
2916         exp_i_print(exp_i->next);
2917 }
2918
2919 void
2920 exp_ecase_print(ecase)
2921 struct ecase *ecase;
2922 {
2923         printf("pat <%s>\n",ecase->pat);
2924         printf("exp_i = %x\n",ecase->i_list);
2925 }
2926
2927 void
2928 exp_ecases_print(ecd)
2929 struct exp_cases_descriptor *ecd;
2930 {
2931         int i;
2932
2933         printf("%d cases\n",ecd->count);
2934         for (i=0;i<ecd->count;i++) exp_ecase_print(ecd->cases[i]);
2935 }
2936
2937 void
2938 exp_cmd_print(ecmd)
2939 struct exp_cmd_descriptor *ecmd;
2940 {
2941         printf("expect cmd type: %17s",exp_cmdtype_printable(ecmd->cmdtype));
2942         printf((ecmd->duration==EXP_PERMANENT)?" perm ": "tmp ");
2943         /* printdict */
2944         exp_ecases_print(&ecmd->ecd);
2945         exp_i_print(ecmd->i_list);
2946 }
2947
2948 void
2949 exp_cmds_print()
2950 {
2951         exp_cmd_print(&exp_cmds[EXP_CMD_BEFORE]);
2952         exp_cmd_print(&exp_cmds[EXP_CMD_AFTER]);
2953         exp_cmd_print(&exp_cmds[EXP_CMD_BG]);
2954 }
2955
2956 /*ARGSUSED*/
2957 int
2958 cmdX(clientData, interp, argc, argv)
2959 ClientData clientData;
2960 Tcl_Interp *interp;
2961 int argc;
2962 char **argv;
2963 {
2964         exp_cmds_print();
2965         return TCL_OK;
2966 }
2967 #endif /*DEBUG_PERM_ECASES*/
2968
2969 /* need address for passing into cmdExpect */
2970 static int spawn_id_bad = EXP_SPAWN_ID_BAD;
2971 static int spawn_id_user = EXP_SPAWN_ID_USER;
2972
2973 static struct exp_cmd_data
2974 cmd_data[]  = {
2975 {"expect",      exp_proc(Exp_ExpectCmd),        (ClientData)&spawn_id_bad,      0},
2976 {"expect_after",exp_proc(Exp_ExpectGlobalCmd),(ClientData)&exp_cmds[EXP_CMD_AFTER],0},
2977 {"expect_before",exp_proc(Exp_ExpectGlobalCmd),(ClientData)&exp_cmds[EXP_CMD_BEFORE],0},
2978 {"expect_user", exp_proc(Exp_ExpectCmd),        (ClientData)&spawn_id_user,     0},
2979 {"expect_tty",  exp_proc(Exp_ExpectCmd),        (ClientData)&exp_dev_tty,       0},
2980 {"expect_background",exp_proc(Exp_ExpectGlobalCmd),(ClientData)&exp_cmds[EXP_CMD_BG],0},
2981 {"match_max",   exp_proc(Exp_MatchMaxCmd),      0,      0},
2982 {"remove_nulls",exp_proc(Exp_RemoveNullsCmd),   0,      0},
2983 {"parity",      exp_proc(Exp_ParityCmd),                0,      0},
2984 {"timestamp",   exp_proc(Exp_TimestampCmd),     0,      0},
2985 {0}};
2986
2987 void
2988 exp_init_expect_cmds(interp)
2989 Tcl_Interp *interp;
2990 {
2991         exp_create_commands(interp,cmd_data);
2992
2993         Tcl_SetVar(interp,EXPECT_TIMEOUT,INIT_EXPECT_TIMEOUT_LIT,0);
2994         Tcl_SetVar(interp,EXP_SPAWN_ID_ANY_VARNAME,EXP_SPAWN_ID_ANY_LIT,0);
2995
2996         exp_cmd_init(&exp_cmds[EXP_CMD_BEFORE],EXP_CMD_BEFORE,EXP_PERMANENT);
2997         exp_cmd_init(&exp_cmds[EXP_CMD_AFTER ],EXP_CMD_AFTER, EXP_PERMANENT);
2998         exp_cmd_init(&exp_cmds[EXP_CMD_BG    ],EXP_CMD_BG,    EXP_PERMANENT);
2999         exp_cmd_init(&exp_cmds[EXP_CMD_FG    ],EXP_CMD_FG,    EXP_TEMPORARY);
3000
3001         /* preallocate to one element, so future realloc's work */
3002         exp_cmds[EXP_CMD_BEFORE].ecd.cases = 0;
3003         exp_cmds[EXP_CMD_AFTER ].ecd.cases = 0;
3004         exp_cmds[EXP_CMD_BG    ].ecd.cases = 0;
3005
3006         pattern_style[PAT_EOF] = "eof";
3007         pattern_style[PAT_TIMEOUT] = "timeout";
3008         pattern_style[PAT_DEFAULT] = "default";
3009         pattern_style[PAT_FULLBUFFER] = "full buffer";
3010         pattern_style[PAT_GLOB] = "glob pattern";
3011         pattern_style[PAT_RE] = "regular expression";
3012         pattern_style[PAT_EXACT] = "exact string";
3013         pattern_style[PAT_NULL] = "null";
3014
3015 #if 0
3016         Tcl_CreateCommand(interp,"x",
3017                 cmdX,(ClientData)0,exp_deleteProc);
3018 #endif
3019 }
3020
3021 void
3022 exp_init_sig() {
3023 #if 0
3024         signal(SIGALRM,sigalarm_handler);
3025         signal(SIGINT,sigint_handler);
3026 #endif
3027 }