OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [expect/] [expect.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.