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