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