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

Subversion Repositories or1k

[/] [or1k/] [tags/] [start/] [insight/] [expect/] [exp_command.c] - Blame information for rev 579

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/* exp_command.c - the bulk of the 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 "expect_cf.h"
12
 
13
#include <stdio.h>
14
#include <sys/types.h>
15
/*#include <sys/time.h> seems to not be present on SVR3 systems */
16
/* and it's not used anyway as far as I can tell */
17
 
18
/* AIX insists that stropts.h be included before ioctl.h, because both */
19
/* define _IO but only ioctl.h checks first.  Oddly, they seem to be */
20
/* defined differently! */
21
#ifdef HAVE_STROPTS_H
22
#  include <sys/stropts.h>
23
#endif
24
#include <sys/ioctl.h>
25
 
26
#ifdef HAVE_SYS_FCNTL_H
27
#  include <sys/fcntl.h>
28
#else
29
#  include <fcntl.h>
30
#endif
31
#include <sys/file.h>
32
#include "exp_tty.h"
33
 
34
#ifdef HAVE_SYS_WAIT_H
35
  /* ISC doesn't def WNOHANG unless _POSIX_SOURCE is def'ed */
36
# ifdef WNOHANG_REQUIRES_POSIX_SOURCE
37
#  define _POSIX_SOURCE
38
# endif
39
# include <sys/wait.h>
40
# ifdef WNOHANG_REQUIRES_POSIX_SOURCE
41
#  undef _POSIX_SOURCE
42
# endif
43
#endif
44
 
45
#include <errno.h>
46
#include <signal.h>
47
 
48
#if defined(SIGCLD) && !defined(SIGCHLD)
49
#define SIGCHLD SIGCLD
50
#endif
51
 
52
/* Use _NSIG if NSIG not present */
53
#ifndef NSIG
54
#ifdef _NSIG
55
#define NSIG _NSIG
56
#endif
57
#endif
58
 
59
#ifdef HAVE_PTYTRAP
60
#include <sys/ptyio.h>
61
#endif
62
 
63
#ifdef CRAY
64
# ifndef TCSETCTTY
65
#  if defined(HAVE_TERMIOS)
66
#   include <termios.h>
67
#  else
68
#   include <termio.h>
69
#  endif
70
# endif
71
#endif
72
 
73
#ifdef HAVE_UNISTD_H
74
# include <unistd.h>
75
#endif
76
 
77
#include <math.h>               /* for log/pow computation in send -h */
78
#include <ctype.h>              /* all this for ispunct! */
79
 
80
#include "tclInt.h"             /* need OpenFile */
81
/*#include <varargs.h>          tclInt.h drags in varargs.h.  Since Pyramid */
82
/*                              objects to including varargs.h twice, just */
83
/*                              omit this one. */
84
 
85
#include "tcl.h"
86
#include "string.h"
87
#include "expect_tcl.h"
88
#include "exp_rename.h"
89
#include "exp_prog.h"
90
#include "exp_command.h"
91
#include "exp_log.h"
92
#include "exp_event.h"
93
#include "exp_pty.h"
94
#ifdef TCL_DEBUGGER
95
#include "Dbg.h"
96
#endif
97
 
98
#define SPAWN_ID_VARNAME "spawn_id"
99
 
100
int getptymaster();
101
int getptyslave();
102
 
103
int exp_forked = FALSE;         /* whether we are child process */
104
 
105
/* the following are just reserved addresses, to be used as ClientData */
106
/* args to be used to tell commands how they were called. */
107
/* The actual values won't be used, only the addresses, but I give them */
108
/* values out of my irrational fear the compiler might collapse them all. */
109
static int sendCD_error = 2;    /* called as send_error */
110
static int sendCD_user = 3;     /* called as send_user */
111
static int sendCD_proc = 4;     /* called as send or send_spawn */
112
static int sendCD_tty = 6;      /* called as send_tty */
113
 
114
struct exp_f *exp_fs = 0;                /* process array (indexed by spawn_id's) */
115
int exp_fd_max = -1;            /* highest fd */
116
 
117
/*
118
 * expect_key is just a source for generating a unique stamp.  As each
119
 * expect/interact command begins, it generates a new key and marks all
120
 * the spawn ids of interest with it.  Then, if someone comes along and
121
 * marks them with yet a newer key, the old command will recognize this
122
 * reexamine the state of the spawned process.
123
 */
124
int expect_key = 0;
125
 
126
/*
127
 * exp_configure_count is incremented whenever a spawned process is closed
128
 * or an indirect list is modified.  This forces any (stack of) expect or
129
 * interact commands to reexamine the state of the world and adjust
130
 * accordingly.
131
 */
132
int exp_configure_count = 0;
133
 
134
/* this message is required because fopen sometimes fails to set errno */
135
/* Apparently, it "does the user a favor" and doesn't even call open */
136
/* if the file name is bizarre enough.  This means we can't handle fopen */
137
/* with the obvious trivial logic. */
138
static char *open_failed = "could not open - odd file name?";
139
 
140
#ifdef HAVE_PTYTRAP
141
/* slaveNames provides a mapping from the pty slave names to our */
142
/* spawn id entry.  This is needed only on HPs for stty, sigh. */
143
static Tcl_HashTable slaveNames;
144
#endif /* HAVE_PTYTRAP */
145
 
146
#ifdef FULLTRAPS
147
static void
148
init_traps(traps)
149
RETSIGTYPE (*traps[])();
150
{
151
        int i;
152
 
153
        for (i=1;i<NSIG;i++) {
154
                traps[i] = SIG_ERR;
155
        }
156
}
157
#endif
158
 
159
/* Do not terminate format strings with \n!!! */
160
/*VARARGS*/
161
void
162
exp_error TCL_VARARGS_DEF(Tcl_Interp *,arg1)
163
/*exp_error(va_alist)*/
164
/*va_dcl*/
165
{
166
        Tcl_Interp *interp;
167
        char *fmt;
168
        va_list args;
169
 
170
        interp = TCL_VARARGS_START(Tcl_Interp *,arg1,args);
171
        /*va_start(args);*/
172
        /*interp = va_arg(args,Tcl_Interp *);*/
173
        fmt = va_arg(args,char *);
174
        vsprintf(interp->result,fmt,args);
175
        va_end(args);
176
}
177
 
178
/* returns handle if fd is usable, 0 if not */
179
struct exp_f *
180
exp_fd2f(interp,fd,opened,adjust,msg)
181
Tcl_Interp *interp;
182
int fd;
183
int opened;             /* check not closed */
184
int adjust;             /* adjust buffer sizes */
185
char *msg;
186
{
187
        if (fd >= 0 && fd <= exp_fd_max && (exp_fs[fd].valid)) {
188
                struct exp_f *f = exp_fs + fd;
189
 
190
                /* following is a little tricky, do not be tempted do the */
191
                /* 'usual' boolean simplification */
192
                if ((!opened) || !f->user_closed) {
193
                        if (adjust) exp_adjust(f);
194
                        return f;
195
                }
196
        }
197
 
198
        exp_error(interp,"%s: invalid spawn id (%d)",msg,fd);
199
        return(0);
200
}
201
 
202
#if 0
203
/* following routine is not current used, but might be later */
204
/* returns fd or -1 if no such entry */
205
static int
206
pid_to_fd(pid)
207
int pid;
208
{
209
        int fd;
210
 
211
        for (fd=0;fd<=exp_fd_max;fd++) {
212
                if (exp_fs[fd].pid == pid) return(fd);
213
        }
214
        return 0;
215
}
216
#endif
217
 
218
/* Tcl needs commands in writable space */
219
static char close_cmd[] = "close";
220
 
221
/* zero out the wait status field */
222
static void
223
exp_wait_zero(status)
224
WAIT_STATUS_TYPE *status;
225
{
226
        int i;
227
 
228
        for (i=0;i<sizeof(WAIT_STATUS_TYPE);i++) {
229
                ((char *)status)[i] = 0;
230
        }
231
}
232
 
233
/* prevent an fd from being allocated */
234
void
235
exp_busy(fd)
236
int fd;
237
{
238
        int x = open("/dev/null",0);
239
        if (x != fd) {
240
                fcntl(x,F_DUPFD,fd);
241
                close(x);
242
        }
243
        exp_close_on_exec(fd);
244
}
245
 
246
/* called just before an exp_f entry is about to be invalidated */
247
void
248
exp_f_prep_for_invalidation(interp,f)
249
Tcl_Interp *interp;
250
struct exp_f *f;
251
{
252
        int fd = f - exp_fs;
253
 
254
        exp_ecmd_remove_fd_direct_and_indirect(interp,fd);
255
 
256
        exp_configure_count++;
257
 
258
        if (f->buffer) {
259
                ckfree(f->buffer);
260
                f->buffer = 0;
261
                f->msize = 0;
262
                f->size = 0;
263
                f->printed = 0;
264
                f->echoed = 0;
265
                if (f->fg_armed) {
266
                        exp_event_disarm(f-exp_fs);
267
                        f->fg_armed = FALSE;
268
                }
269
                ckfree(f->lower);
270
        }
271
        f->fg_armed = FALSE;
272
}
273
 
274
/*ARGSUSED*/
275
void
276
exp_trap_on(master)
277
int master;
278
{
279
#ifdef HAVE_PTYTRAP
280
        if (master == -1) return;
281
        exp_slave_control(master,1);
282
#endif /* HAVE_PTYTRAP */
283
}
284
 
285
int
286
exp_trap_off(name)
287
char *name;
288
{
289
#ifdef HAVE_PTYTRAP
290
        int master;
291
        struct exp_f *f;
292
        int enable = 0;
293
 
294
        Tcl_HashEntry *entry = Tcl_FindHashEntry(&slaveNames,name);
295
        if (!entry) {
296
                debuglog("exp_trap_off: no entry found for %s\n",name);
297
                return -1;
298
        }
299
 
300
        f = (struct exp_f *)Tcl_GetHashValue(entry);
301
        master = f - exp_fs;
302
 
303
        exp_slave_control(master,0);
304
 
305
        return master;
306
#else
307
        return name[0];  /* pacify lint, use arg and return something */
308
#endif
309
}
310
 
311
/*ARGSUSED*/
312
void
313
sys_close(fd,f)
314
int fd;
315
struct exp_f *f;
316
{
317
        /* Ignore close errors.  Some systems are really odd and */
318
        /* return errors for no evident reason.  Anyway, receiving */
319
        /* an error upon pty-close doesn't mean anything anyway as */
320
        /* far as I know. */
321
        close(fd);
322
        f->sys_closed = TRUE;
323
 
324
#ifdef HAVE_PTYTRAP
325
        if (f->slave_name) {
326
                Tcl_HashEntry *entry;
327
 
328
                entry = Tcl_FindHashEntry(&slaveNames,f->slave_name);
329
                Tcl_DeleteHashEntry(entry);
330
 
331
                ckfree(f->slave_name);
332
                f->slave_name = 0;
333
        }
334
#endif
335
}
336
 
337
/* given a Tcl file identifier, close it */
338
static void
339
close_tcl_file(interp,file_id)
340
Tcl_Interp *interp;
341
char *file_id;
342
{
343
    Tcl_VarEval(interp,"close ",file_id,(char *)0);
344
 
345
#if 0  /* old Tcl 7.6 code */
346
        char *argv[3];
347
        Tcl_CmdInfo info;
348
 
349
        argv[0] = close_cmd;
350
        argv[1] = file_id;
351
        argv[2] = 0;
352
 
353
        Tcl_ResetResult(interp);
354
        Tcl_GetCommandInfo(interp,"close",&info);
355
        if (0 == Tcl_GetCommandInfo(interp,"close",&info)) {
356
                info.clientData = 0;
357
        }
358
        (void) Tcl_CloseCmd(info.clientData,interp,2,argv);
359
#endif
360
}
361
 
362
 
363
/* close all connections
364
The kernel would actually do this by default, however Tcl is going to
365
come along later and try to reap its exec'd processes.  If we have
366
inherited any via spawn -open, Tcl can hang if we don't close the
367
connections first.
368
*/
369
 
370
void
371
exp_close_all(interp)
372
Tcl_Interp *interp;
373
{
374
        int fd;
375
 
376
        for (fd=0;fd<=exp_fd_max;fd++) {
377
                if (exp_fs[fd].valid) {
378
                        exp_close(interp,fd);
379
                }
380
        }
381
}
382
 
383
int
384
exp_close(interp,fd)
385
Tcl_Interp *interp;
386
int fd;
387
{
388
        struct exp_f *f = exp_fd2f(interp,fd,1,0,"close");
389
        if (!f) return(TCL_ERROR);
390
 
391
        f->user_closed = TRUE;
392
 
393
        if (f->slave_fd != EXP_NOFD) close(f->slave_fd);
394
#if 0
395
        if (f->tcl_handle) {
396
                ckfree(f->tcl_handle);
397
                if ((f - exp_fs) != f->tcl_output) close(f->tcl_output);
398
        }
399
#endif
400
        sys_close(fd,f);
401
 
402
        if (f->tcl_handle) {
403
                if ((f - exp_fs) != f->tcl_output) close(f->tcl_output);
404
 
405
                if (!f->leaveopen) {
406
                        /*
407
                         * Ignore errors from close; they report things like
408
                         * broken pipeline, etc, which don't affect our
409
                         * subsequent handling.
410
                         */
411
 
412
                        close_tcl_file(interp,f->tcl_handle);
413
 
414
                        ckfree(f->tcl_handle);
415
                        f->tcl_handle = 0;
416
                }
417
        }
418
 
419
        exp_f_prep_for_invalidation(interp,f);
420
 
421
        if (f->user_waited) {
422
                f->valid = FALSE;
423
        } else {
424
                exp_busy(fd);
425
                f->sys_closed = FALSE;
426
        }
427
 
428
        return(TCL_OK);
429
}
430
 
431
static struct exp_f *
432
fd_new(fd,pid)
433
int fd;
434
int pid;
435
{
436
        int i, low;
437
        struct exp_f *newfs;    /* temporary, so we don't lose old exp_fs */
438
 
439
        /* resize table if nec */
440
        if (fd > exp_fd_max) {
441
                if (!exp_fs) {  /* no fd's yet allocated */
442
                        newfs = (struct exp_f *)ckalloc(sizeof(struct exp_f)*(fd+1));
443
                        low = 0;
444
                } else {                /* enlarge fd table */
445
                        newfs = (struct exp_f *)ckrealloc((char *)exp_fs,sizeof(struct exp_f)*(fd+1));
446
                        low = exp_fd_max+1;
447
                }
448
                exp_fs = newfs;
449
                exp_fd_max = fd;
450
                for (i = low; i <= exp_fd_max; i++) { /* init new fd entries */
451
                        exp_fs[i].valid = FALSE;
452
                        exp_fs[i].fd_ptr = (int *)ckalloc(sizeof(int));
453
                        *exp_fs[i].fd_ptr = i;
454
 
455
/*                      exp_fs[i].ptr = (struct exp_f **)ckalloc(sizeof(struct exp_fs *));*/
456
 
457
                }
458
 
459
#if 0
460
                for (i = 0; i <= exp_fd_max; i++) { /* update all indirect ptrs */
461
                        *exp_fs[i].ptr = exp_fs + i;
462
                }
463
#endif
464
        }
465
 
466
        /* this could happen if user does "spawn -open stdin" I suppose */
467
        if (exp_fs[fd].valid) return exp_fs+fd;
468
 
469
        /* close down old table entry if nec */
470
        exp_fs[fd].pid = pid;
471
        exp_fs[fd].size = 0;
472
        exp_fs[fd].msize = 0;
473
        exp_fs[fd].buffer = 0;
474
        exp_fs[fd].printed = 0;
475
        exp_fs[fd].echoed = 0;
476
        exp_fs[fd].rm_nulls = exp_default_rm_nulls;
477
        exp_fs[fd].parity = exp_default_parity;
478
        exp_fs[fd].key = expect_key++;
479
        exp_fs[fd].force_read = FALSE;
480
        exp_fs[fd].fg_armed = FALSE;
481
#if TCL_MAJOR_VERSION < 8
482
        /* Master must be inited each time because Tcl could have alloc'd */
483
        /* this fd and shut it down (deallocating the FileHandle) behind */
484
        /* our backs */
485
        exp_fs[fd].Master = Tcl_GetFile((ClientData)fd,TCL_UNIX_FD);
486
        exp_fs[fd].MasterOutput = 0;
487
        exp_fs[fd].Slave = 0;
488
#endif /* TCL_MAJOR_VERSION < 8 */
489
#ifdef __CYGWIN32__
490
       exp_fs[fd].channel = NULL;
491
       exp_fs[fd].fileproc = NULL;
492
#endif
493
        exp_fs[fd].tcl_handle = 0;
494
        exp_fs[fd].slave_fd = EXP_NOFD;
495
#ifdef HAVE_PTYTRAP
496
        exp_fs[fd].slave_name = 0;
497
#endif /* HAVE_PTYTRAP */
498
        exp_fs[fd].umsize = exp_default_match_max;
499
        exp_fs[fd].valid = TRUE;
500
        exp_fs[fd].user_closed = FALSE;
501
        exp_fs[fd].sys_closed = FALSE;
502
        exp_fs[fd].user_waited = FALSE;
503
        exp_fs[fd].sys_waited = FALSE;
504
        exp_fs[fd].bg_interp = 0;
505
        exp_fs[fd].bg_status = unarmed;
506
        exp_fs[fd].bg_ecount = 0;
507
 
508
        return exp_fs+fd;
509
}
510
 
511
#if 0
512
void
513
exp_global_init(eg,duration,location)
514
struct expect_global *eg;
515
int duration;
516
int location;
517
{
518
        eg->ecases = 0;
519
        eg->ecount = 0;
520
        eg->i_list = 0;
521
        eg->duration = duration;
522
        eg->location = location;
523
}
524
#endif
525
 
526
void
527
exp_init_spawn_id_vars(interp)
528
Tcl_Interp *interp;
529
{
530
        Tcl_SetVar(interp,"user_spawn_id",EXP_SPAWN_ID_USER_LIT,0);
531
        Tcl_SetVar(interp,"error_spawn_id",EXP_SPAWN_ID_ERROR_LIT,0);
532
 
533
        /* note that the user_spawn_id is NOT /dev/tty which could */
534
        /* (at least in theory anyway) be later re-opened on a different */
535
        /* fd, while stdin might have been redirected away from /dev/tty */
536
 
537
        if (exp_dev_tty != -1) {
538
                char dev_tty_str[10];
539
                sprintf(dev_tty_str,"%d",exp_dev_tty);
540
                Tcl_SetVar(interp,"tty_spawn_id",dev_tty_str,0);
541
        }
542
}
543
 
544
void
545
exp_init_spawn_ids()
546
{
547
        /* note whether 0,1,2 are connected to a terminal so that if we */
548
        /* disconnect, we can shut these down.  We would really like to */
549
        /* test if 0,1,2 are our controlling tty, but I don't know any */
550
        /* way to do that portably.  Anyway, the likelihood of anyone */
551
        /* disconnecting after redirecting to a non-controlling tty is */
552
        /* virtually zero. */
553
 
554
        fd_new(0,isatty(0)?exp_getpid:EXP_NOPID);
555
        fd_new(1,isatty(1)?exp_getpid:EXP_NOPID);
556
        fd_new(2,isatty(2)?exp_getpid:EXP_NOPID);
557
 
558
        if (exp_dev_tty != -1) {
559
                fd_new(exp_dev_tty,exp_getpid);
560
        }
561
 
562
        /* really should be in interpreter() but silly to do on every call */
563
        exp_adjust(&exp_fs[0]);
564
}
565
 
566
void
567
exp_close_on_exec(fd)
568
int fd;
569
{
570
        (void) fcntl(fd,F_SETFD,1);
571
}
572
 
573
#define STTY_INIT       "stty_init"
574
 
575
#if 0
576
static void
577
show_pgrp(fd,string)
578
int fd;
579
char *string;
580
{
581
        int pgrp;
582
 
583
        fprintf(stderr,"getting pgrp for %s\n",string);
584
        if (-1 == ioctl(fd,TIOCGETPGRP,&pgrp)) perror("TIOCGETPGRP");
585
        else fprintf(stderr,"%s pgrp = %d\n",string,pgrp);
586
        if (-1 == ioctl(fd,TIOCGPGRP,&pgrp)) perror("TIOCGPGRP");
587
        else fprintf(stderr,"%s pgrp = %d\n",string,pgrp);
588
        if (-1 == tcgetpgrp(fd,pgrp)) perror("tcgetpgrp");
589
        else fprintf(stderr,"%s pgrp = %d\n",string,pgrp);
590
}
591
 
592
static void
593
set_pgrp(fd)
594
int fd;
595
{
596
        int pgrp = getpgrp(0);
597
        if (-1 == ioctl(fd,TIOCSETPGRP,&pgrp)) perror("TIOCSETPGRP");
598
        if (-1 == ioctl(fd,TIOCSPGRP,&pgrp)) perror("TIOCSPGRP");
599
        if (-1 == tcsetpgrp(fd,pgrp)) perror("tcsetpgrp");
600
}
601
#endif
602
 
603
/*ARGSUSED*/
604
static void
605
set_slave_name(f,name)
606
struct exp_f *f;
607
char *name;
608
{
609
#ifdef HAVE_PTYTRAP
610
        int newptr;
611
        Tcl_HashEntry *entry;
612
 
613
        /* save slave name */
614
        f->slave_name = ckalloc(strlen(exp_pty_slave_name)+1);
615
        strcpy(f->slave_name,exp_pty_slave_name);
616
 
617
        entry = Tcl_CreateHashEntry(&slaveNames,exp_pty_slave_name,&newptr);
618
        Tcl_SetHashValue(entry,(ClientData)f);
619
#endif /* HAVE_PTYTRAP */
620
}
621
 
622
#ifdef __CYGWIN32__
623
/* Sometimes, the win32 version of expect passes a windows handle to
624
   dup(), which normally only takes file descriptors.  We check for
625
   that with this wrapper.  DJ */
626
#include <windows.h>
627
static int
628
cygwin_pipe_dup (int oldfd)
629
{
630
  int rv = dup(oldfd);
631
  if (rv != -1) /* cool */
632
    return rv;
633
  /* Oops, check for a handle */
634
  if (GetFileType((HANDLE)oldfd) == FILE_TYPE_PIPE)
635
    {
636
      if (DuplicateHandle(GetCurrentProcess(),
637
                          (HANDLE)oldfd,
638
                          GetCurrentProcess(),
639
                          (HANDLE *)&rv,
640
                          0, 0,
641
                          DUPLICATE_SAME_ACCESS))
642
        {
643
          int fd = cygwin32_attach_handle_to_fd ("/dev/piped",
644
                                                 -1, rv,
645
                                                 1, O_RDWR);
646
          if (fd >= 0)
647
            return fd;
648
        }
649
    }
650
  return -1;
651
}
652
#endif
653
 
654
/* arguments are passed verbatim to execvp() */
655
/*ARGSUSED*/
656
static int
657
Exp_SpawnCmd(clientData,interp,argc,argv)
658
ClientData clientData;
659
Tcl_Interp *interp;
660
int argc;
661
char **argv;
662
{
663
        int slave;
664
        int pid;
665
        char **a;
666
        /* tell Saber to ignore non-use of ttyfd */
667
        /*SUPPRESS 591*/
668
        int errorfd;    /* place to stash fileno(stderr) in child */
669
                        /* while we're setting up new stderr */
670
        int ttyfd;
671
        int master;
672
        int write_master;       /* write fd of Tcl-opened files */
673
        int ttyinit = TRUE;
674
        int ttycopy = TRUE;
675
        int echo = TRUE;
676
        int console = FALSE;
677
        int pty_only = FALSE;
678
 
679
#ifdef FULLTRAPS
680
                                /* Allow user to reset signals in child */
681
                                /* The following array contains indicates */
682
                                /* whether sig should be DFL or IGN */
683
                                /* ERR is used to indicate no initialization */
684
        RETSIGTYPE (*traps[NSIG])();
685
#endif
686
        int ignore[NSIG];       /* if true, signal in child is ignored */
687
                                /* if false, signal gets default behavior */
688
        int i;                  /* trusty overused temporary */
689
 
690
        char *argv0 = argv[0];
691
        char *openarg = 0;
692
        int leaveopen = FALSE;
693
        FILE *readfilePtr;
694
        FILE *writefilePtr;
695
        int rc, wc;
696
        char *stty_init;
697
        int slave_write_ioctls = 1;
698
                /* by default, slave will be write-ioctled this many times */
699
        int slave_opens = 3;
700
                /* by default, slave will be opened this many times */
701
                /* first comes from initial allocation */
702
                /* second comes from stty */
703
                /* third is our own signal that stty is done */
704
 
705
        int sync_fds[2];
706
        int sync2_fds[2];
707
        int status_pipe[2];
708
        int child_errno;
709
        char sync_byte;
710
 
711
        char buf[4];            /* enough space for a string literal */
712
                                /* representing a file descriptor */
713
        Tcl_DString dstring;
714
        Tcl_DStringInit(&dstring);
715
 
716
#ifdef FULLTRAPS
717
        init_traps(&traps);
718
#endif
719
        /* don't ignore any signals in child by default */
720
        for (i=1;i<NSIG;i++) {
721
                ignore[i] = FALSE;
722
        }
723
 
724
        argc--; argv++;
725
 
726
        for (;argc>0;argc--,argv++) {
727
                if (streq(*argv,"-nottyinit")) {
728
                        ttyinit = FALSE;
729
                        slave_write_ioctls--;
730
                        slave_opens--;
731
                } else if (streq(*argv,"-nottycopy")) {
732
                        ttycopy = FALSE;
733
                } else if (streq(*argv,"-noecho")) {
734
                        echo = FALSE;
735
                } else if (streq(*argv,"-console")) {
736
                        console = TRUE;
737
                } else if (streq(*argv,"-pty")) {
738
                        pty_only = TRUE;
739
                } else if (streq(*argv,"-open")) {
740
                        if (argc < 2) {
741
                                exp_error(interp,"usage: -open file-identifier");
742
                                return TCL_ERROR;
743
                        }
744
                        openarg = argv[1];
745
                        argc--; argv++;
746
                } else if (streq(*argv,"-leaveopen")) {
747
                        if (argc < 2) {
748
                                exp_error(interp,"usage: -open file-identifier");
749
                                return TCL_ERROR;
750
                        }
751
                        openarg = argv[1];
752
                        leaveopen = TRUE;
753
                        argc--; argv++;
754
                } else if (streq(*argv,"-ignore")) {
755
                        int sig;
756
 
757
                        if (argc < 2) {
758
                                exp_error(interp,"usage: -ignore signal");
759
                                return TCL_ERROR;
760
                        }
761
                        sig = exp_string_to_signal(interp,argv[1]);
762
                        if (sig == -1) {
763
                                exp_error(interp,"usage: -ignore %s: unknown signal name",argv[1]);
764
                                return TCL_ERROR;
765
                        }
766
                        ignore[sig] = TRUE;
767
                        argc--; argv++;
768
#ifdef FULLTRAPS
769
                } else if (streq(*argv,"-trap")) {
770
                        /* argv[1] is action */
771
                        /* argv[2] is list of signals */
772
 
773
                        RETSIGTYPE (*sig_handler)();
774
                        int n;          /* number of signals in list */
775
                        char **list;    /* list of signals */
776
 
777
                        if (argc < 3) {
778
                                exp_error(interp,"usage: -trap siglist SIG_DFL or SIG_IGN");
779
                                return TCL_ERROR;
780
                        }
781
 
782
                        if (0 == strcmp(argv[2],"SIG_DFL")) {
783
                                sig_handler = SIG_DFL;
784
                        } else if (0 == strcmp(argv[2],"SIG_IGN")) {
785
                                sig_handler = SIG_IGN;
786
                        } else {
787
                                exp_error(interp,"usage: -trap siglist SIG_DFL or SIG_IGN");
788
                                return TCL_ERROR;
789
                        }
790
 
791
                        if (TCL_OK != Tcl_SplitList(interp,argv[1],&n,&list)) {
792
                                errorlog("%s\r\n",interp->result);
793
                                exp_error(interp,"usage: -trap {siglist} ...");
794
                                return TCL_ERROR;
795
                        }
796
                        for (i=0;i<n;i++) {
797
                                int sig = exp_string_to_signal(interp,list[i]);
798
                                if (sig == -1) {
799
                                        ckfree((char *)&list);
800
                                        return TCL_ERROR;
801
                                }
802
                                traps[sig] = sig_handler;
803
                        }
804
                        ckfree((char *)&list);
805
 
806
                        argc--; argv++;
807
                        argc--; argv++;
808
#endif /*FULLTRAPS*/
809
                } else break;
810
        }
811
 
812
        if (openarg && (argc != 0)) {
813
                exp_error(interp,"usage: -[leave]open [fileXX]");
814
                return TCL_ERROR;
815
        }
816
 
817
        if (!pty_only && !openarg && (argc == 0)) {
818
                exp_error(interp,"usage: spawn [spawn-args] program [program-args]");
819
                return(TCL_ERROR);
820
        }
821
 
822
        stty_init = exp_get_var(interp,STTY_INIT);
823
        if (stty_init) {
824
                slave_write_ioctls++;
825
                slave_opens++;
826
        }
827
 
828
/* any extraneous ioctl's that occur in slave must be accounted for
829
when trapping, see below in child half of fork */
830
#if defined(TIOCSCTTY) && !defined(CIBAUD) && !defined(sun) && !defined(hp9000s300)
831
        slave_write_ioctls++;
832
        slave_opens++;
833
#endif
834
 
835
        exp_pty_slave_name = 0;
836
 
837
        Tcl_ReapDetachedProcs();
838
 
839
        if (!openarg) {
840
                if (echo) {
841
                        exp_log(0,"%s ",argv0);
842
                        for (a = argv;*a;a++) {
843
                                exp_log(0,"%s ",*a);
844
                        }
845
                        exp_nflog("\r\n",0);
846
                }
847
 
848
                if (0 > (master = getptymaster())) {
849
                        /*
850
                         * failed to allocate pty, try and figure out why
851
                         * so we can suggest to user what to do about it.
852
                         */
853
 
854
                        int count;
855
                        int testfd;
856
 
857
                        if (exp_pty_error) {
858
                                exp_error(interp,"%s",exp_pty_error);
859
                                return TCL_ERROR;
860
                        }
861
 
862
                        count = 0;
863
                        for (i=3;i<=exp_fd_max;i++) {
864
                                count += exp_fs[i].valid;
865
                        }
866
                        if (count > 10) {
867
                                exp_error(interp,"The system only has a finite number of ptys and you have many of them in use.  The usual reason for this is that you forgot (or didn't know) to call \"wait\" after closing each of them.");
868
                                return TCL_ERROR;
869
                        }
870
 
871
                        testfd = open("/",0);
872
                        close(testfd);
873
 
874
                        if (testfd != -1) {
875
                                exp_error(interp,"The system has no more ptys.  Ask your system administrator to create more.");
876
                        } else {
877
                                exp_error(interp,"- You have too many files are open.  Close some files or increase your per-process descriptor limit.");
878
                        }
879
                        return(TCL_ERROR);
880
                }
881
#ifdef PTYTRAP_DIES
882
                if (!pty_only) exp_slave_control(master,1);
883
#endif /* PTYTRAP_DIES */
884
 
885
#define SPAWN_OUT "spawn_out"
886
                Tcl_SetVar2(interp,SPAWN_OUT,"slave,name",exp_pty_slave_name,0);
887
        } else {
888
                Tcl_Channel chan;
889
                int mode;
890
#if TCL_MAJOR_VERSION < 8
891
                Tcl_File tclReadFile, tclWriteFile;
892
#endif /* TCL_MAJOR_VERSION < 8 */
893
                /* CYGNUS LOCAL 64bit/law */
894
                /* These must be both wide enough and aligned enough for
895
                   the TCL code to store a pointer into them!  */
896
                void *rfd, *wfd;
897
                /* END CYGNUS LOCAL */
898
 
899
                if (echo) exp_log(0,"%s [open ...]\r\n",argv0);
900
 
901
#if TCL7_4
902
                rc = Tcl_GetOpenFile(interp,openarg,0,1,&readfilePtr);
903
                wc = Tcl_GetOpenFile(interp,openarg,1,1,&writefilePtr);
904
 
905
                /* fail only if both descriptors are bad */
906
                if (rc == TCL_ERROR && wc == TCL_ERROR) {
907
                        return TCL_ERROR;
908
                }
909
 
910
                master = fileno((rc == TCL_OK)?readfilePtr:writefilePtr);
911
 
912
                /* make a new copy of file descriptor */
913
                if (-1 == (write_master = master = dup(master))) {
914
                        exp_error(interp,"fdopen: %s",Tcl_PosixError(interp));
915
                        return TCL_ERROR;
916
                }
917
 
918
                /* if writefilePtr is different, dup that too */
919
                if ((rc == TCL_OK) && (wc == TCL_OK) && (fileno(writefilePtr) != fileno(readfilePtr))) {
920
                        if (-1 == (write_master = dup(fileno(writefilePtr)))) {
921
                                exp_error(interp,"fdopen: %s",Tcl_PosixError(interp));
922
                                return TCL_ERROR;
923
                        }
924
                        exp_close_on_exec(write_master);
925
                }
926
 
927
#endif
928
                if (!(chan = Tcl_GetChannel(interp,openarg,&mode))) {
929
                        return TCL_ERROR;
930
                }
931
                if (!mode) {
932
                        exp_error(interp,"channel is neither readable nor writable");
933
                        return TCL_ERROR;
934
                }
935
                if (mode & TCL_READABLE) {
936
#if TCL_MAJOR_VERSION < 8
937
                        tclReadFile = Tcl_GetChannelFile(chan, TCL_READABLE);
938
                        rfd = (int)Tcl_GetFileInfo(tclReadFile, (int *)0);
939
#else
940
                        if (TCL_ERROR == Tcl_GetChannelHandle(chan, TCL_READABLE, (ClientData) &rfd)) {
941
                                return TCL_ERROR;
942
                        }
943
#endif /* TCL_MAJOR_VERSION < 8 */
944
                }
945
                if (mode & TCL_WRITABLE) {
946
#if TCL_MAJOR_VERSION < 8
947
                        tclWriteFile = Tcl_GetChannelFile(chan, TCL_WRITABLE);
948
                        wfd = (int)Tcl_GetFileInfo(tclWriteFile, (int *)0);
949
#else
950
                        if (TCL_ERROR == Tcl_GetChannelHandle(chan, TCL_WRITABLE, (ClientData) &wfd)) {
951
                                return TCL_ERROR;
952
                        }
953
#endif /* TCL_MAJOR_VERSION < 8 */
954
                }
955
 
956
                master = ((mode & TCL_READABLE)?rfd:wfd);
957
 
958
                /* make a new copy of file descriptor */
959
#ifdef __CYGWIN32__
960
                if (-1 == (write_master = master = cygwin_pipe_dup(master))) {
961
#else
962
                if (-1 == (write_master = master = dup(master))) {
963
#endif
964
                        exp_error(interp,"fdopen: %s",Tcl_PosixError(interp));
965
                        return TCL_ERROR;
966
                }
967
 
968
                /* if writefilePtr is different, dup that too */
969
                if ((mode & TCL_READABLE) && (mode & TCL_WRITABLE) && (wfd != rfd)) {
970
                        if (-1 == (write_master = dup(wfd))) {
971
                                exp_error(interp,"fdopen: %s",Tcl_PosixError(interp));
972
                                return TCL_ERROR;
973
                        }
974
                        exp_close_on_exec(write_master);
975
                }
976
 
977
                /*
978
                 * It would be convenient now to tell Tcl to close its
979
                 * file descriptor.  Alas, if involved in a pipeline, Tcl
980
                 * will be unable to complete a wait on the process.
981
                 * So simply remember that we meant to close it.  We will
982
                 * do so later in our own close routine.
983
                 */
984
        }
985
 
986
        /* much easier to set this, than remember all masters */
987
        exp_close_on_exec(master);
988
 
989
        if (openarg || pty_only) {
990
                struct exp_f *f;
991
 
992
                f = fd_new(master,EXP_NOPID);
993
 
994
                if (openarg) {
995
                        /* save file# handle */
996
                        f->tcl_handle = ckalloc(strlen(openarg)+1);
997
                        strcpy(f->tcl_handle,openarg);
998
 
999
                        f->tcl_output = write_master;
1000
#if 0
1001
                        /* save fd handle for output */
1002
                        if (wc == TCL_OK) {
1003
/*                              f->tcl_output = fileno(writefilePtr);*/
1004
                                f->tcl_output = write_master;
1005
                        } else {
1006
                                /* if we actually try to write to it at some */
1007
                                /* time in the future, then this will cause */
1008
                                /* an error */
1009
                                f->tcl_output = master;
1010
                        }
1011
#endif
1012
 
1013
                        f->leaveopen = leaveopen;
1014
                }
1015
 
1016
                if (exp_pty_slave_name) set_slave_name(f,exp_pty_slave_name);
1017
 
1018
                /* make it appear as if process has been waited for */
1019
                f->sys_waited = TRUE;
1020
                exp_wait_zero(&f->wait);
1021
 
1022
                /* tell user id of new process */
1023
                sprintf(buf,"%d",master);
1024
                Tcl_SetVar(interp,SPAWN_ID_VARNAME,buf,0);
1025
 
1026
                if (!openarg) {
1027
                        char value[20];
1028
                        int dummyfd1, dummyfd2;
1029
 
1030
                        /*
1031
                         * open the slave side in the same process to support
1032
                         * the -pty flag.
1033
                         */
1034
 
1035
                        /* Start by working around a bug in Tcl's exec.
1036
                           It closes all the file descriptors from 3 to it's
1037
                           own fd_max which inappropriately closes our slave
1038
                           fd.  To avoid this, open several dummy fds.  Then
1039
                           exec's fds will fall below ours.
1040
                           Note that if you do something like pre-allocating
1041
                           a bunch before using them or generating a pipeline,
1042
                           then this code won't help.
1043
                           Instead you'll need to add the right number of
1044
                           explicit Tcl open's of /dev/null.
1045
                           The right solution is fix Tcl's exec so it is not
1046
                           so cavalier.
1047
                         */
1048
 
1049
                        dummyfd1 = open("/dev/null",0);
1050
                        dummyfd2 = open("/dev/null",0);
1051
 
1052
                        if (0 > (f->slave_fd = getptyslave(ttycopy,ttyinit,
1053
                                        stty_init))) {
1054
                                exp_error(interp,"open(slave pty): %s\r\n",Tcl_PosixError(interp));
1055
                                return TCL_ERROR;
1056
                        }
1057
 
1058
                        close(dummyfd1);
1059
                        close(dummyfd2);
1060
 
1061
                        exp_slave_control(master,1);
1062
 
1063
                        sprintf(value,"%d",f->slave_fd);
1064
                        Tcl_SetVar2(interp,SPAWN_OUT,"slave,fd",value,0);
1065
                }
1066
                sprintf(interp->result,"%d",EXP_NOPID);
1067
                debuglog("spawn: returns {%s}\r\n",interp->result);
1068
 
1069
                return TCL_OK;
1070
        }
1071
 
1072
        if (NULL == (argv[0] = Tcl_TildeSubst(interp,argv[0],&dstring))) {
1073
                goto parent_error;
1074
        }
1075
 
1076
        if (-1 == pipe(sync_fds)) {
1077
                exp_error(interp,"too many programs spawned?  could not create pipe: %s",Tcl_PosixError(interp));
1078
                goto parent_error;
1079
        }
1080
 
1081
        if (-1 == pipe(sync2_fds)) {
1082
                close(sync_fds[0]);
1083
                close(sync_fds[1]);
1084
                exp_error(interp,"too many programs spawned?  could not create pipe: %s",Tcl_PosixError(interp));
1085
                goto parent_error;
1086
        }
1087
 
1088
        if (-1 == pipe(status_pipe)) {
1089
                close(sync_fds[0]);
1090
                close(sync_fds[1]);
1091
                close(sync2_fds[0]);
1092
                close(sync2_fds[1]);
1093
        }
1094
 
1095
        if ((pid = fork()) == -1) {
1096
                exp_error(interp,"fork: %s",Tcl_PosixError(interp));
1097
                goto parent_error;
1098
        }
1099
 
1100
        if (pid) { /* parent */
1101
                struct exp_f *f;
1102
 
1103
                close(sync_fds[1]);
1104
                close(sync2_fds[0]);
1105
                close(status_pipe[1]);
1106
 
1107
                f = fd_new(master,pid);
1108
 
1109
                if (exp_pty_slave_name) set_slave_name(f,exp_pty_slave_name);
1110
 
1111
#ifdef CRAY
1112
                setptypid(pid);
1113
#endif
1114
 
1115
 
1116
#if PTYTRAP_DIES
1117
#ifdef HAVE_PTYTRAP
1118
 
1119
                while (slave_opens) {
1120
                        int cc;
1121
                        cc = exp_wait_for_slave_open(master);
1122
#if defined(TIOCSCTTY) && !defined(CIBAUD) && !defined(sun) && !defined(hp9000s300)
1123
                        if (cc == TIOCSCTTY) slave_opens = 0;
1124
#endif
1125
                        if (cc == TIOCOPEN) slave_opens--;
1126
                        if (cc == -1) {
1127
                                exp_error(interp,"failed to trap slave pty");
1128
                                goto parent_error;
1129
                        }
1130
                }
1131
 
1132
#if 0
1133
                /* trap initial ioctls in a feeble attempt to not block */
1134
                /* the initially.  If the process itself ioctls */
1135
                /* /dev/tty, such blocks will be trapped later */
1136
                /* during normal event processing */
1137
 
1138
                /* initial slave ioctl */
1139
                while (slave_write_ioctls) {
1140
                        int cc;
1141
 
1142
                        cc = exp_wait_for_slave_open(master);
1143
#if defined(TIOCSCTTY) && !defined(CIBAUD) && !defined(sun) && !defined(hp9000s300)
1144
                        if (cc == TIOCSCTTY) slave_write_ioctls = 0;
1145
#endif
1146
                        if (cc & IOC_IN) slave_write_ioctls--;
1147
                        else if (cc == -1) {
1148
                                exp_error(interp,"failed to trap slave pty");
1149
                                goto parent_error;
1150
                        }
1151
                }
1152
#endif /*0*/
1153
 
1154
#endif /* HAVE_PTYTRAP */
1155
#endif /* PTYTRAP_DIES */
1156
 
1157
                /*
1158
                 * wait for slave to initialize pty before allowing
1159
                 * user to send to it
1160
                 */
1161
 
1162
                debuglog("parent: waiting for sync byte\r\n");
1163
                while (((rc = read(sync_fds[0],&sync_byte,1)) < 0) && (errno == EINTR)) {
1164
                        /* empty */;
1165
                }
1166
                if (rc == -1) {
1167
                        errorlog("parent: sync byte read: %s\r\n",Tcl_ErrnoMsg(errno));
1168
                        exit(-1);
1169
                }
1170
 
1171
                /* turn on detection of eof */
1172
                exp_slave_control(master,1);
1173
 
1174
                /*
1175
                 * tell slave to go on now now that we have initialized pty
1176
                 */
1177
 
1178
                debuglog("parent: telling child to go ahead\r\n");
1179
                wc = write(sync2_fds[1]," ",1);
1180
                if (wc == -1) {
1181
                        errorlog("parent: sync byte write: %s\r\n",Tcl_ErrnoMsg(errno));
1182
                        exit(-1);
1183
                }
1184
 
1185
                debuglog("parent: now unsynchronized from child\r\n");
1186
                close(sync_fds[0]);
1187
                close(sync2_fds[1]);
1188
 
1189
                /* see if child's exec worked */
1190
        retry:
1191
                switch (read(status_pipe[0],&child_errno,sizeof child_errno)) {
1192
                case -1:
1193
                        if (errno == EINTR) goto retry;
1194
                        /* well it's not really the child's errno */
1195
                        /* but it can be treated that way */
1196
                        child_errno = errno;
1197
                        break;
1198
                case 0:
1199
                        /* child's exec succeeded */
1200
                        child_errno = 0;
1201
                        break;
1202
                default:
1203
                        /* child's exec failed; err contains exec's errno  */
1204
                        waitpid(pid, NULL, 0);
1205
                        /* in order to get Tcl to set errorcode, we must */
1206
                        /* hand set errno */
1207
                        errno = child_errno;
1208
                        exp_error(interp, "couldn't execute \"%s\": %s",
1209
                                argv[0],Tcl_PosixError(interp));
1210
                        goto parent_error;
1211
                }
1212
                close(status_pipe[0]);
1213
 
1214
 
1215
                /* tell user id of new process */
1216
                sprintf(buf,"%d",master);
1217
                Tcl_SetVar(interp,SPAWN_ID_VARNAME,buf,0);
1218
 
1219
                sprintf(interp->result,"%d",pid);
1220
                debuglog("spawn: returns {%s}\r\n",interp->result);
1221
 
1222
                Tcl_DStringFree(&dstring);
1223
                return(TCL_OK);
1224
parent_error:
1225
                Tcl_DStringFree(&dstring);
1226
                return TCL_ERROR;
1227
        }
1228
 
1229
        /* child process - do not return from here!  all errors must exit() */
1230
 
1231
        close(sync_fds[0]);
1232
        close(sync2_fds[1]);
1233
        close(status_pipe[0]);
1234
        exp_close_on_exec(status_pipe[1]);
1235
 
1236
        if (exp_dev_tty != -1) {
1237
                close(exp_dev_tty);
1238
                exp_dev_tty = -1;
1239
        }
1240
 
1241
#ifdef CRAY
1242
        (void) close(master);
1243
#endif
1244
 
1245
/* ultrix (at least 4.1-2) fails to obtain controlling tty if setsid */
1246
/* is called.  setpgrp works though.  */
1247
#if defined(POSIX) && !defined(ultrix)
1248
#define DO_SETSID
1249
#endif
1250
#ifdef __convex__
1251
#define DO_SETSID
1252
#endif
1253
 
1254
#ifdef DO_SETSID
1255
        setsid();
1256
#else
1257
#ifdef SYSV3
1258
#ifndef CRAY
1259
        setpgrp();
1260
#endif /* CRAY */
1261
#else /* !SYSV3 */
1262
#ifdef MIPS_BSD
1263
        /* required on BSD side of MIPS OS <jmsellen@watdragon.waterloo.edu> */
1264
#       include <sysv/sys.s>
1265
        syscall(SYS_setpgrp);
1266
#endif
1267
        setpgrp(0,0);
1268
/*      setpgrp(0,getpid());*/  /* make a new pgrp leader */
1269
 
1270
/* Pyramid lacks this defn */
1271
#ifdef TIOCNOTTY
1272
        ttyfd = open("/dev/tty", O_RDWR);
1273
        if (ttyfd >= 0) {
1274
                (void) ioctl(ttyfd, TIOCNOTTY, (char *)0);
1275
                (void) close(ttyfd);
1276
        }
1277
#endif /* TIOCNOTTY */
1278
 
1279
#endif /* SYSV3 */
1280
#endif /* DO_SETSID */
1281
 
1282
        /* save stderr elsewhere to avoid BSD4.4 bogosity that warns */
1283
        /* if stty finds dev(stderr) != dev(stdout) */
1284
 
1285
        /* save error fd while we're setting up new one */
1286
        errorfd = fcntl(2,F_DUPFD,3);
1287
        /* and here is the macro to restore it */
1288
#define restore_error_fd {close(2);fcntl(errorfd,F_DUPFD,2);}
1289
 
1290
        close(0);
1291
        close(1);
1292
        close(2);
1293
 
1294
        /* since we closed fd 0, open of pty slave must return fd 0 */
1295
 
1296
        /* since getptyslave may have to run stty, (some of which work on fd */
1297
        /* 0 and some of which work on 1) do the dup's inside getptyslave. */
1298
 
1299
        if (0 > (slave = getptyslave(ttycopy,ttyinit,stty_init))) {
1300
                restore_error_fd
1301
                errorlog("open(slave pty): %s\r\n",Tcl_ErrnoMsg(errno));
1302
                exit(-1);
1303
        }
1304
        /* sanity check */
1305
        if (slave != 0) {
1306
                restore_error_fd
1307
                errorlog("getptyslave: slave = %d but expected 0\n",slave);
1308
                exit(-1);
1309
        }
1310
 
1311
/* The test for hpux may have to be more specific.  In particular, the */
1312
/* code should be skipped on the hp9000s300 and hp9000s720 (but there */
1313
/* is no documented define for the 720!) */
1314
 
1315
/*#if defined(TIOCSCTTY) && !defined(CIBAUD) && !defined(sun) && !defined(hpux)*/
1316
#if defined(TIOCSCTTY) && !defined(sun) && !defined(hpux)
1317
        /* 4.3+BSD way to acquire controlling terminal */
1318
        /* according to Stevens - Adv. Prog..., p 642 */
1319
        /* Oops, it appears that the CIBAUD is on Linux also */
1320
        /* so let's try without... */
1321
#ifdef __QNX__
1322
        if (tcsetct(0, getpid()) == -1) {
1323
#else
1324
        if (ioctl(0,TIOCSCTTY,(char *)0) < 0) {
1325
#endif
1326
                restore_error_fd
1327
                errorlog("failed to get controlling terminal using TIOCSCTTY");
1328
                exit(-1);
1329
        }
1330
#endif
1331
 
1332
#ifdef CRAY
1333
        (void) setsid();
1334
        (void) ioctl(0,TCSETCTTY,0);
1335
        (void) close(0);
1336
        if (open("/dev/tty", O_RDWR) < 0) {
1337
                restore_error_fd
1338
                errorlog("open(/dev/tty): %s\r\n",Tcl_ErrnoMsg(errno));
1339
                exit(-1);
1340
        }
1341
        (void) close(1);
1342
        (void) close(2);
1343
        (void) dup(0);
1344
        (void) dup(0);
1345
        setptyutmp();   /* create a utmp entry */
1346
 
1347
        /* _CRAY2 code from Hal Peterson <hrp@cray.com>, Cray Research, Inc. */
1348
#ifdef _CRAY2
1349
        /*
1350
         * Interpose a process between expect and the spawned child to
1351
         * keep the slave side of the pty open to allow time for expect
1352
         * to read the last output.  This is a workaround for an apparent
1353
         * bug in the Unicos pty driver on Cray-2's under Unicos 6.0 (at
1354
         * least).
1355
         */
1356
        if ((pid = fork()) == -1) {
1357
                restore_error_fd
1358
                errorlog("second fork: %s\r\n",Tcl_ErrnoMsg(errno));
1359
                exit(-1);
1360
        }
1361
 
1362
        if (pid) {
1363
                /* Intermediate process. */
1364
                int status;
1365
                int timeout;
1366
                char *t;
1367
 
1368
                /* How long should we wait? */
1369
                if (t = exp_get_var(interp,"pty_timeout"))
1370
                        timeout = atoi(t);
1371
                else if (t = exp_get_var(interp,"timeout"))
1372
                        timeout = atoi(t)/2;
1373
                else
1374
                        timeout = 5;
1375
 
1376
                /* Let the spawned process run to completion. */
1377
                while (wait(&status) < 0 && errno == EINTR)
1378
                        /* empty body */;
1379
 
1380
                /* Wait for the pty to clear. */
1381
                sleep(timeout);
1382
 
1383
                /* Duplicate the spawned process's status. */
1384
                if (WIFSIGNALED(status))
1385
                        kill(getpid(), WTERMSIG(status));
1386
 
1387
                /* The kill may not have worked, but this will. */
1388
                exit(WEXITSTATUS(status));
1389
        }
1390
#endif /* _CRAY2 */
1391
#endif /* CRAY */
1392
 
1393
        if (console) exp_console_set();
1394
 
1395
#ifdef FULLTRAPS
1396
        for (i=1;i<NSIG;i++) {
1397
                if (traps[i] != SIG_ERR) {
1398
                        signal(i,traps[i]);
1399
                }
1400
        }
1401
#endif /* FULLTRAPS */
1402
 
1403
        for (i=1;i<NSIG;i++) {
1404
                signal(i,ignore[i]?SIG_IGN:SIG_DFL);
1405
        }
1406
 
1407
#if 0
1408
        /* avoid fflush of cmdfile since this screws up the parents seek ptr */
1409
        /* There is no portable way to fclose a shared read-stream!!!! */
1410
        if (exp_cmdfile && (exp_cmdfile != stdin))
1411
                (void) close(fileno(exp_cmdfile));
1412
        if (logfile) (void) fclose(logfile);
1413
        if (debugfile) (void) fclose(debugfile);
1414
#endif
1415
        /* (possibly multiple) masters are closed automatically due to */
1416
        /* earlier fcntl(,,CLOSE_ON_EXEC); */
1417
 
1418
        /* tell parent that we are done setting up pty */
1419
        /* The actual char sent back is irrelevant. */
1420
 
1421
        /* debuglog("child: telling parent that pty is initialized\r\n");*/
1422
        wc = write(sync_fds[1]," ",1);
1423
        if (wc == -1) {
1424
                restore_error_fd
1425
                errorlog("child: sync byte write: %s\r\n",Tcl_ErrnoMsg(errno));
1426
                exit(-1);
1427
        }
1428
        close(sync_fds[1]);
1429
 
1430
        /* wait for master to let us go on */
1431
        /* debuglog("child: waiting for go ahead from parent\r\n"); */
1432
 
1433
/*      close(master);  /* force master-side close so we can read */
1434
 
1435
        while (((rc = read(sync2_fds[0],&sync_byte,1)) < 0) && (errno == EINTR)) {
1436
                /* empty */;
1437
        }
1438
 
1439
        if (rc == -1) {
1440
                restore_error_fd
1441
                errorlog("child: sync byte read: %s\r\n",Tcl_ErrnoMsg(errno));
1442
                exit(-1);
1443
        }
1444
        close(sync2_fds[0]);
1445
 
1446
        /* debuglog("child: now unsynchronized from parent\r\n"); */
1447
 
1448
        /* So much for close-on-exec.  Tcl doesn't mark its files that way */
1449
        /* everything has to be closed explicitly. */
1450
        if (exp_close_in_child) (*exp_close_in_child)();
1451
 
1452
        (void) execvp(argv[0],argv);
1453
#if 0
1454
        /* Unfortunately, by now we've closed fd's to stderr, logfile and
1455
                debugfile.
1456
           The only reasonable thing to do is to send back the error as
1457
           part of the program output.  This will be picked up in an
1458
           expect or interact command.
1459
        */
1460
        errorlog("%s: %s\r\n",argv[0],Tcl_ErrnoMsg(errno));
1461
#endif
1462
        /* if exec failed, communicate the reason back to the parent */
1463
        write(status_pipe[1], &errno, sizeof errno);
1464
        exit(-1);
1465
        /*NOTREACHED*/
1466
}
1467
 
1468
/*ARGSUSED*/
1469
static int
1470
Exp_ExpPidCmd(clientData,interp,argc,argv)
1471
ClientData clientData;
1472
Tcl_Interp *interp;
1473
int argc;
1474
char **argv;
1475
{
1476
        struct exp_f *f;
1477
        int m = -1;
1478
 
1479
        argc--; argv++;
1480
 
1481
        for (;argc>0;argc--,argv++) {
1482
                if (streq(*argv,"-i")) {
1483
                        argc--; argv++;
1484
                        if (!*argv) goto usage;
1485
                        m = atoi(*argv);
1486
                } else goto usage;
1487
        }
1488
 
1489
        if (m == -1) {
1490
                if (exp_update_master(interp,&m,0,0) == 0) return TCL_ERROR;
1491
        }
1492
 
1493
        if (0 == (f = exp_fd2f(interp,m,1,0,"exp_pid"))) return TCL_ERROR;
1494
 
1495
        sprintf(interp->result,"%d",f->pid);
1496
        return TCL_OK;
1497
 usage:
1498
        exp_error(interp,"usage: -i spawn_id");
1499
        return TCL_ERROR;
1500
}
1501
 
1502
/*ARGSUSED*/
1503
static int
1504
Exp_GetpidDeprecatedCmd(clientData, interp, argc, argv)
1505
ClientData clientData;
1506
Tcl_Interp *interp;
1507
int argc;
1508
char **argv;
1509
{
1510
        debuglog("getpid is deprecated, use pid\r\n");
1511
        sprintf(interp->result,"%d",getpid());
1512
        return(TCL_OK);
1513
}
1514
 
1515
/* returns current master (via out-parameter) */
1516
/* returns f or 0, but note that since exp_fd2f calls tcl_error, this */
1517
/* may be immediately followed by a "return(TCL_ERROR)"!!! */
1518
struct exp_f *
1519
exp_update_master(interp,m,opened,adjust)
1520
Tcl_Interp *interp;
1521
int *m;
1522
int opened;
1523
int adjust;
1524
{
1525
        char *s = exp_get_var(interp,SPAWN_ID_VARNAME);
1526
        *m = (s?atoi(s):EXP_SPAWN_ID_USER);
1527
        return(exp_fd2f(interp,*m,opened,adjust,(s?s:EXP_SPAWN_ID_USER_LIT)));
1528
}
1529
 
1530
/*ARGSUSED*/
1531
static int
1532
Exp_SleepCmd(clientData,interp,argc,argv)
1533
ClientData clientData;
1534
Tcl_Interp *interp;
1535
int argc;
1536
char **argv;
1537
{
1538
        argc--; argv++;
1539
 
1540
        if (argc != 1) {
1541
                exp_error(interp,"must have one arg: seconds");
1542
                return TCL_ERROR;
1543
        }
1544
 
1545
        return(exp_dsleep(interp,(double)atof(*argv)));
1546
}
1547
 
1548
/* write exactly this many bytes, i.e. retry partial writes */
1549
/* returns 0 for success, -1 for failure */
1550
static int
1551
exact_write(fd,buffer,rembytes)
1552
int fd;
1553
char *buffer;
1554
int rembytes;
1555
{
1556
        int cc;
1557
 
1558
        while (rembytes) {
1559
                if (-1 == (cc = write(fd,buffer,rembytes))) return(-1);
1560
                if (0 == cc) {
1561
                        /* This shouldn't happen but I'm told that it does */
1562
                        /* nonetheless (at least on SunOS 4.1.3).  Since */
1563
                        /* this is not a documented return value, the most */
1564
                        /* reasonable thing is to complain here and retry */
1565
                        /* in the hopes that is some transient condition. */
1566
                        sleep(1);
1567
                        exp_debuglog("write() failed to write anything but returned - sleeping and retrying...\n");
1568
                }
1569
 
1570
                buffer += cc;
1571
                rembytes -= cc;
1572
        }
1573
        return(0);
1574
}
1575
 
1576
struct slow_arg {
1577
        int size;
1578
        double time;
1579
};
1580
 
1581
/* returns 0 for success, -1 for failure */
1582
static int
1583
get_slow_args(interp,x)
1584
Tcl_Interp *interp;
1585
struct slow_arg *x;
1586
{
1587
        int sc;         /* return from scanf */
1588
        char *s = exp_get_var(interp,"send_slow");
1589
        if (!s) {
1590
                exp_error(interp,"send -s: send_slow has no value");
1591
                return(-1);
1592
        }
1593
        if (2 != (sc = sscanf(s,"%d %lf",&x->size,&x->time))) {
1594
                exp_error(interp,"send -s: found %d value(s) in send_slow but need 2",sc);
1595
                return(-1);
1596
        }
1597
        if (x->size <= 0) {
1598
                exp_error(interp,"send -s: size (%d) in send_slow must be positive", x->size);
1599
                return(-1);
1600
        }
1601
        if (x->time <= 0) {
1602
                exp_error(interp,"send -s: time (%f) in send_slow must be larger",x->time);
1603
                return(-1);
1604
        }
1605
        return(0);
1606
}
1607
 
1608
/* returns 0 for success, -1 for failure, pos. for Tcl return value */
1609
static int
1610
slow_write(interp,fd,buffer,rembytes,arg)
1611
Tcl_Interp *interp;
1612
int fd;
1613
char *buffer;
1614
int rembytes;
1615
struct slow_arg *arg;
1616
{
1617
        int rc;
1618
 
1619
        while (rembytes > 0) {
1620
                int len;
1621
 
1622
                len = (arg->size<rembytes?arg->size:rembytes);
1623
                if (0 > exact_write(fd,buffer,len)) return(-1);
1624
                rembytes -= arg->size;
1625
                buffer += arg->size;
1626
 
1627
                /* skip sleep after last write */
1628
                if (rembytes > 0) {
1629
                        rc = exp_dsleep(interp,arg->time);
1630
                        if (rc>0) return rc;
1631
                }
1632
        }
1633
        return(0);
1634
}
1635
 
1636
struct human_arg {
1637
        float alpha;            /* average interarrival time in seconds */
1638
        float alpha_eow;        /* as above but for eow transitions */
1639
        float c;                /* shape */
1640
        float min, max;
1641
};
1642
 
1643
/* returns -1 if error, 0 if success */
1644
static int
1645
get_human_args(interp,x)
1646
Tcl_Interp *interp;
1647
struct human_arg *x;
1648
{
1649
        int sc;         /* return from scanf */
1650
        char *s = exp_get_var(interp,"send_human");
1651
 
1652
        if (!s) {
1653
                exp_error(interp,"send -h: send_human has no value");
1654
                return(-1);
1655
        }
1656
        if (5 != (sc = sscanf(s,"%f %f %f %f %f",
1657
                        &x->alpha,&x->alpha_eow,&x->c,&x->min,&x->max))) {
1658
                if (sc == EOF) sc = 0;   /* make up for overloaded return */
1659
                exp_error(interp,"send -h: found %d value(s) in send_human but need 5",sc);
1660
                return(-1);
1661
        }
1662
        if (x->alpha < 0 || x->alpha_eow < 0) {
1663
                exp_error(interp,"send -h: average interarrival times (%f %f) must be non-negative in send_human", x->alpha,x->alpha_eow);
1664
                return(-1);
1665
        }
1666
        if (x->c <= 0) {
1667
                exp_error(interp,"send -h: variability (%f) in send_human must be positive",x->c);
1668
                return(-1);
1669
        }
1670
        x->c = 1/x->c;
1671
 
1672
        if (x->min < 0) {
1673
                exp_error(interp,"send -h: minimum (%f) in send_human must be non-negative",x->min);
1674
                return(-1);
1675
        }
1676
        if (x->max < 0) {
1677
                exp_error(interp,"send -h: maximum (%f) in send_human must be non-negative",x->max);
1678
                return(-1);
1679
        }
1680
        if (x->max < x->min) {
1681
                exp_error(interp,"send -h: maximum (%f) must be >= minimum (%f) in send_human",x->max,x->min);
1682
                return(-1);
1683
        }
1684
        return(0);
1685
}
1686
 
1687
/* Compute random numbers from 0 to 1, for expect's send -h */
1688
/* This implementation sacrifices beauty for portability */
1689
static float
1690
unit_random()
1691
{
1692
        /* current implementation is pathetic but works */
1693
        /* 99991 is largest prime in my CRC - can't hurt, eh? */
1694
        return((float)(1+(rand()%99991))/99991.0);
1695
}
1696
 
1697
void
1698
exp_init_unit_random()
1699
{
1700
        srand(getpid());
1701
}
1702
 
1703
/* This function is my implementation of the Weibull distribution. */
1704
/* I've added a max time and an "alpha_eow" that captures the slight */
1705
/* but noticable change in human typists when hitting end-of-word */
1706
/* transitions. */
1707
/* returns 0 for success, -1 for failure, pos. for Tcl return value */
1708
static int
1709
human_write(interp,fd,buffer,arg)
1710
Tcl_Interp *interp;
1711
int fd;
1712
char *buffer;
1713
struct human_arg *arg;
1714
{
1715
        char *sp;
1716
        float t;
1717
        float alpha;
1718
        int wc;
1719
        int in_word = TRUE;
1720
 
1721
        debuglog("human_write: avg_arr=%f/%f  1/shape=%f  min=%f  max=%f\r\n",
1722
                arg->alpha,arg->alpha_eow,arg->c,arg->min,arg->max);
1723
 
1724
        for (sp = buffer;*sp;sp++) {
1725
                /* use the end-of-word alpha at eow transitions */
1726
                if (in_word && (ispunct(*sp) || isspace(*sp)))
1727
                        alpha = arg->alpha_eow;
1728
                else alpha = arg->alpha;
1729
                in_word = !(ispunct(*sp) || isspace(*sp));
1730
 
1731
                t = alpha * pow(-log((double)unit_random()),arg->c);
1732
 
1733
                /* enforce min and max times */
1734
                if (t<arg->min) t = arg->min;
1735
                else if (t>arg->max) t = arg->max;
1736
 
1737
/*fprintf(stderr,"\nwriting <%c> but first sleep %f seconds\n",*sp,t);*/
1738
                /* skip sleep before writing first character */
1739
                if (sp != buffer) {
1740
                        wc = exp_dsleep(interp,(double)t);
1741
                        if (wc > 0) return wc;
1742
                }
1743
 
1744
                wc = write(fd,sp,1);
1745
                if (0 > wc) return(wc);
1746
        }
1747
        return(0);
1748
}
1749
 
1750
struct exp_i *exp_i_pool = 0;
1751
struct exp_fd_list *exp_fd_list_pool = 0;
1752
 
1753
#define EXP_I_INIT_COUNT        10
1754
#define EXP_FD_INIT_COUNT       10
1755
 
1756
struct exp_i *
1757
exp_new_i()
1758
{
1759
        int n;
1760
        struct exp_i *i;
1761
 
1762
        if (!exp_i_pool) {
1763
                /* none avail, generate some new ones */
1764
                exp_i_pool = i = (struct exp_i *)ckalloc(
1765
                        EXP_I_INIT_COUNT * sizeof(struct exp_i));
1766
                for (n=0;n<EXP_I_INIT_COUNT-1;n++,i++) {
1767
                        i->next = i+1;
1768
                }
1769
                i->next = 0;
1770
        }
1771
 
1772
        /* now that we've made some, unlink one and give to user */
1773
 
1774
        i = exp_i_pool;
1775
        exp_i_pool = exp_i_pool->next;
1776
        i->value = 0;
1777
        i->variable = 0;
1778
        i->fd_list = 0;
1779
        i->ecount = 0;
1780
        i->next = 0;
1781
        return i;
1782
}
1783
 
1784
struct exp_fd_list *
1785
exp_new_fd(val)
1786
int val;
1787
{
1788
        int n;
1789
        struct exp_fd_list *fd;
1790
 
1791
        if (!exp_fd_list_pool) {
1792
                /* none avail, generate some new ones */
1793
                exp_fd_list_pool = fd = (struct exp_fd_list *)ckalloc(
1794
                        EXP_FD_INIT_COUNT * sizeof(struct exp_fd_list));
1795
                for (n=0;n<EXP_FD_INIT_COUNT-1;n++,fd++) {
1796
                        fd->next = fd+1;
1797
                }
1798
                fd->next = 0;
1799
        }
1800
 
1801
        /* now that we've made some, unlink one and give to user */
1802
 
1803
        fd = exp_fd_list_pool;
1804
        exp_fd_list_pool = exp_fd_list_pool->next;
1805
        fd->fd = val;
1806
        /* fd->next is assumed to be changed by caller */
1807
        return fd;
1808
}
1809
 
1810
void
1811
exp_free_fd(fd_first)
1812
struct exp_fd_list *fd_first;
1813
{
1814
        struct exp_fd_list *fd, *penultimate;
1815
 
1816
        if (!fd_first) return;
1817
 
1818
        /* link entire chain back in at once by first finding last pointer */
1819
        /* making that point back to pool, and then resetting pool to this */
1820
 
1821
        /* run to end */
1822
        for (fd = fd_first;fd;fd=fd->next) {
1823
                penultimate = fd;
1824
        }
1825
        penultimate->next = exp_fd_list_pool;
1826
        exp_fd_list_pool = fd_first;
1827
}
1828
 
1829
/* free a single fd */
1830
void
1831
exp_free_fd_single(fd)
1832
struct exp_fd_list *fd;
1833
{
1834
        fd->next = exp_fd_list_pool;
1835
        exp_fd_list_pool = fd;
1836
}
1837
 
1838
void
1839
exp_free_i(interp,i,updateproc)
1840
Tcl_Interp *interp;
1841
struct exp_i *i;
1842
Tcl_VarTraceProc *updateproc;   /* proc to invoke if indirect is written */
1843
{
1844
        if (i->next) exp_free_i(interp,i->next,updateproc);
1845
 
1846
        exp_free_fd(i->fd_list);
1847
 
1848
        if (i->direct == EXP_INDIRECT) {
1849
                Tcl_UntraceVar(interp,i->variable,
1850
                        TCL_GLOBAL_ONLY|TCL_TRACE_WRITES,
1851
                        updateproc,(ClientData)i);
1852
        }
1853
 
1854
        /* here's the long form
1855
           if duration & direct free(var)  free(val)
1856
                PERM      DIR                   1
1857
                PERM      INDIR     1           1
1858
                TMP       DIR
1859
                TMP       INDIR                 1
1860
           Also if i->variable was a bogus variable name, i->value might not be
1861
           set, so test i->value to protect this
1862
           TMP in this case does NOT mean from the "expect" command.  Rather
1863
           it means "an implicit spawn id from any expect or expect_XXX
1864
           command".  In other words, there was no variable name provided.
1865
        */
1866
        if (i->value
1867
           && (((i->direct == EXP_DIRECT) && (i->duration == EXP_PERMANENT))
1868
                || ((i->direct == EXP_INDIRECT) && (i->duration == EXP_TEMPORARY)))) {
1869
                ckfree(i->value);
1870
        } else if (i->duration == EXP_PERMANENT) {
1871
                if (i->value) ckfree(i->value);
1872
                if (i->variable) ckfree(i->variable);
1873
        }
1874
 
1875
        i->next = exp_i_pool;
1876
        exp_i_pool = i;
1877
}
1878
 
1879
/* generate a descriptor for a "-i" flag */
1880
/* cannot fail */
1881
struct exp_i *
1882
exp_new_i_complex(interp,arg,duration,updateproc)
1883
Tcl_Interp *interp;
1884
char *arg;              /* spawn id list or a variable containing a list */
1885
int duration;           /* if we have to copy the args */
1886
                        /* should only need do this in expect_before/after */
1887
Tcl_VarTraceProc *updateproc;   /* proc to invoke if indirect is written */
1888
{
1889
        struct exp_i *i;
1890
        char **stringp;
1891
 
1892
        i = exp_new_i();
1893
 
1894
        i->direct = (isdigit(arg[0]) || (arg[0] == '-'))?EXP_DIRECT:EXP_INDIRECT;
1895
        if (i->direct == EXP_DIRECT) {
1896
                stringp = &i->value;
1897
        } else {
1898
                stringp = &i->variable;
1899
        }
1900
 
1901
        i->duration = duration;
1902
        if (duration == EXP_PERMANENT) {
1903
                *stringp = ckalloc(strlen(arg)+1);
1904
                strcpy(*stringp,arg);
1905
        } else {
1906
                *stringp = arg;
1907
        }
1908
 
1909
        i->fd_list = 0;
1910
        exp_i_update(interp,i);
1911
 
1912
        /* if indirect, ask Tcl to tell us when variable is modified */
1913
 
1914
        if (i->direct == EXP_INDIRECT) {
1915
                Tcl_TraceVar(interp, i->variable,
1916
                        TCL_GLOBAL_ONLY|TCL_TRACE_WRITES,
1917
                        updateproc, (ClientData) i);
1918
        }
1919
 
1920
        return i;
1921
}
1922
 
1923
void
1924
exp_i_add_fd(i,fd)
1925
struct exp_i *i;
1926
int fd;
1927
{
1928
        struct exp_fd_list *new_fd;
1929
 
1930
        new_fd = exp_new_fd(fd);
1931
        new_fd->next = i->fd_list;
1932
        i->fd_list = new_fd;
1933
}
1934
 
1935
/* this routine assumes i->fd is meaningful */
1936
void
1937
exp_i_parse_fds(i)
1938
struct exp_i *i;
1939
{
1940
        char *p = i->value;
1941
 
1942
        /* reparse it */
1943
        while (1) {
1944
                int m;
1945
                int negative = 0;
1946
                int valid_spawn_id = 0;
1947
 
1948
                m = 0;
1949
                while (isspace(*p)) p++;
1950
                for (;;p++) {
1951
                        if (*p == '-') negative = 1;
1952
                        else if (isdigit(*p)) {
1953
                                m = m*10 + (*p-'0');
1954
                                valid_spawn_id = 1;
1955
                        } else if (*p == '\0' || isspace(*p)) break;
1956
                }
1957
 
1958
                /* we either have a spawn_id or whitespace at end of string */
1959
 
1960
                /* skip whitespace end-of-string */
1961
                if (!valid_spawn_id) break;
1962
 
1963
                if (negative) m = -m;
1964
 
1965
                exp_i_add_fd(i,m);
1966
        }
1967
}
1968
 
1969
/* updates a single exp_i struct */
1970
void
1971
exp_i_update(interp,i)
1972
Tcl_Interp *interp;
1973
struct exp_i *i;
1974
{
1975
        char *p;        /* string representation of list of spawn ids */
1976
 
1977
        if (i->direct == EXP_INDIRECT) {
1978
                p = Tcl_GetVar(interp,i->variable,TCL_GLOBAL_ONLY);
1979
                if (!p) {
1980
                        p = "";
1981
                        exp_debuglog("warning: indirect variable %s undefined",i->variable);
1982
                }
1983
 
1984
                if (i->value) {
1985
                        if (streq(p,i->value)) return;
1986
 
1987
                        /* replace new value with old */
1988
                        ckfree(i->value);
1989
                }
1990
                i->value = ckalloc(strlen(p)+1);
1991
                strcpy(i->value,p);
1992
 
1993
                exp_free_fd(i->fd_list);
1994
                i->fd_list = 0;
1995
        } else {
1996
                /* no free, because this should only be called on */
1997
                /* "direct" i's once */
1998
                i->fd_list = 0;
1999
        }
2000
        exp_i_parse_fds(i);
2001
}
2002
 
2003
struct exp_i *
2004
exp_new_i_simple(fd,duration)
2005
int fd;
2006
int duration;           /* if we have to copy the args */
2007
                        /* should only need do this in expect_before/after */
2008
{
2009
        struct exp_i *i;
2010
 
2011
        i = exp_new_i();
2012
 
2013
        i->direct = EXP_DIRECT;
2014
        i->duration = duration;
2015
 
2016
        exp_i_add_fd(i,fd);
2017
 
2018
        return i;
2019
}
2020
 
2021
/*ARGSUSED*/
2022
static int
2023
Exp_SendLogCmd(clientData, interp, argc, argv)
2024
ClientData clientData;
2025
Tcl_Interp *interp;
2026
int argc;
2027
char **argv;
2028
{
2029
        char *string;
2030
        int len;
2031
 
2032
        argv++;
2033
        argc--;
2034
 
2035
        if (argc) {
2036
                if (streq(*argv,"--")) {
2037
                        argc--; argv++;
2038
                }
2039
        }
2040
 
2041
        if (argc != 1) {
2042
                exp_error(interp,"usage: send [args] string");
2043
                return TCL_ERROR;
2044
        }
2045
 
2046
        string = *argv;
2047
 
2048
        len = strlen(string);
2049
 
2050
        if (debugfile) fwrite(string,1,len,debugfile);
2051
        if (logfile) fwrite(string,1,len,logfile);
2052
 
2053
        return(TCL_OK);
2054
}
2055
 
2056
 
2057
/* I've rewritten this to be unbuffered.  I did this so you could shove */
2058
/* large files through "send".  If you are concerned about efficiency */
2059
/* you should quote all your send args to make them one single argument. */
2060
/*ARGSUSED*/
2061
static int
2062
Exp_SendCmd(clientData, interp, argc, argv)
2063
ClientData clientData;
2064
Tcl_Interp *interp;
2065
int argc;
2066
char **argv;
2067
{
2068
        int m = -1;     /* spawn id (master) */
2069
        int rc;         /* final result of this procedure */
2070
        struct human_arg human_args;
2071
        struct slow_arg slow_args;
2072
#define SEND_STYLE_STRING_MASK  0x07    /* mask to detect a real string arg */
2073
#define SEND_STYLE_PLAIN        0x01
2074
#define SEND_STYLE_HUMAN        0x02
2075
#define SEND_STYLE_SLOW         0x04
2076
#define SEND_STYLE_ZERO         0x10
2077
#define SEND_STYLE_BREAK        0x20
2078
        int send_style = SEND_STYLE_PLAIN;
2079
        int want_cooked = TRUE;
2080
        char *string;           /* string to send */
2081
        int len;                /* length of string to send */
2082
        int zeros;              /* count of how many ascii zeros to send */
2083
 
2084
        char *i_masters = 0;
2085
        struct exp_fd_list *fd;
2086
        struct exp_i *i;
2087
        char *arg;
2088
 
2089
        argv++;
2090
        argc--;
2091
        while (argc) {
2092
                arg = *argv;
2093
                if (arg[0] != '-') break;
2094
                arg++;
2095
                if (exp_flageq1('-',arg)) {                     /* "--" */
2096
                        argc--; argv++;
2097
                        break;
2098
                } else if (exp_flageq1('i',arg)) {              /* "-i" */
2099
                        argc--; argv++;
2100
                        if (argc==0) {
2101
                                exp_error(interp,"usage: -i spawn_id");
2102
                                return(TCL_ERROR);
2103
                        }
2104
                        i_masters = *argv;
2105
                        argc--; argv++;
2106
                        continue;
2107
                } else if (exp_flageq1('h',arg)) {              /* "-h" */
2108
                        argc--; argv++;
2109
                        if (-1 == get_human_args(interp,&human_args))
2110
                                return(TCL_ERROR);
2111
                        send_style = SEND_STYLE_HUMAN;
2112
                        continue;
2113
                } else if (exp_flageq1('s',arg)) {              /* "-s" */
2114
                        argc--; argv++;
2115
                        if (-1 == get_slow_args(interp,&slow_args))
2116
                                return(TCL_ERROR);
2117
                        send_style = SEND_STYLE_SLOW;
2118
                        continue;
2119
                } else if (exp_flageq("null",arg,1) || exp_flageq1('0',arg)) {
2120
                        argc--; argv++;                         /* "-null" */
2121
                        if (!*argv) zeros = 1;
2122
                        else {
2123
                                zeros = atoi(*argv);
2124
                                argc--; argv++;
2125
                                if (zeros < 1) return TCL_OK;
2126
                        }
2127
                        send_style = SEND_STYLE_ZERO;
2128
                        string = "<zero(s)>";
2129
                        continue;
2130
                } else if (exp_flageq("raw",arg,1)) {           /* "-raw" */
2131
                        argc--; argv++;
2132
                        want_cooked = FALSE;
2133
                        continue;
2134
                } else if (exp_flageq("break",arg,1)) {         /* "-break" */
2135
                        argc--; argv++;
2136
                        send_style = SEND_STYLE_BREAK;
2137
                        string = "<break>";
2138
                        continue;
2139
                } else {
2140
                        exp_error(interp,"usage: unrecognized flag <-%.80s>",arg);
2141
                        return TCL_ERROR;
2142
                }
2143
        }
2144
 
2145
        if (send_style & SEND_STYLE_STRING_MASK) {
2146
                if (argc != 1) {
2147
                        exp_error(interp,"usage: send [args] string");
2148
                        return TCL_ERROR;
2149
                }
2150
                string = *argv;
2151
        }
2152
        len = strlen(string);
2153
 
2154
        if (clientData == &sendCD_user) m = 1;
2155
        else if (clientData == &sendCD_error) m = 2;
2156
        else if (clientData == &sendCD_tty) m = exp_dev_tty;
2157
        else if (!i_masters) {
2158
                /* we really do want to check if it is open */
2159
                /* but since stdin could be closed, we have to first */
2160
                /* get the fd and then convert it from 0 to 1 if necessary */
2161
                if (0 == exp_update_master(interp,&m,0,0))
2162
                        return(TCL_ERROR);
2163
        }
2164
 
2165
        /* if master != -1, then it holds desired master */
2166
        /* else i_masters does */
2167
 
2168
        if (m != -1) {
2169
                i = exp_new_i_simple(m,EXP_TEMPORARY);
2170
        } else {
2171
                i = exp_new_i_complex(interp,i_masters,FALSE,(Tcl_VarTraceProc *)0);
2172
        }
2173
 
2174
#define send_to_stderr  (clientData == &sendCD_error)
2175
#define send_to_proc    (clientData == &sendCD_proc)
2176
#define send_to_user    ((clientData == &sendCD_user) || \
2177
                         (clientData == &sendCD_tty))
2178
 
2179
        if (send_to_proc) {
2180
                want_cooked = FALSE;
2181
                debuglog("send: sending \"%s\" to {",dprintify(string));
2182
                /* if closing brace doesn't appear, that's because an error */
2183
                /* was encountered before we could send it */
2184
        } else {
2185
                if (debugfile)
2186
                        fwrite(string,1,len,debugfile);
2187
                if ((send_to_user && logfile_all) || logfile)
2188
                        fwrite(string,1,len,logfile);
2189
        }
2190
 
2191
        for (fd=i->fd_list;fd;fd=fd->next) {
2192
                m = fd->fd;
2193
 
2194
                if (send_to_proc) {
2195
                        debuglog(" %d ",m);
2196
                }
2197
 
2198
                /* true if called as Send with user_spawn_id */
2199
                if (exp_is_stdinfd(m)) m = 1;
2200
 
2201
                /* check validity of each - i.e., are they open */
2202
                if (0 == exp_fd2f(interp,m,1,0,"send")) {
2203
                        rc = TCL_ERROR;
2204
                        goto finish;
2205
                }
2206
                /* Check if Tcl is using a different fd for output */
2207
                if (exp_fs[m].tcl_handle) {
2208
                        m = exp_fs[m].tcl_output;
2209
                }
2210
 
2211
                if (want_cooked) string = exp_cook(string,&len);
2212
 
2213
                switch (send_style) {
2214
                case SEND_STYLE_PLAIN:
2215
                        rc = exact_write(m,string,len);
2216
                        break;
2217
                case SEND_STYLE_SLOW:
2218
                        rc = slow_write(interp,m,string,len,&slow_args);
2219
                        break;
2220
                case SEND_STYLE_HUMAN:
2221
                        rc = human_write(interp,m,string,&human_args);
2222
                        break;
2223
                case SEND_STYLE_ZERO:
2224
                        for (;zeros>0;zeros--) rc = write(m,"",1);
2225
                        /* catching error on last write is sufficient */
2226
                        rc = ((rc==1) ? 0 : -1);   /* normal is 1 not 0 */
2227
                        break;
2228
                case SEND_STYLE_BREAK:
2229
                        exp_tty_break(interp,m);
2230
                        rc = 0;
2231
                        break;
2232
                }
2233
 
2234
                if (rc != 0) {
2235
                        if (rc == -1) {
2236
                                exp_error(interp,"write(spawn_id=%d): %s",m,Tcl_PosixError(interp));
2237
                                rc = TCL_ERROR;
2238
                        }
2239
                        goto finish;
2240
                }
2241
        }
2242
        if (send_to_proc) debuglog("}\r\n");
2243
 
2244
        rc = TCL_OK;
2245
 finish:
2246
        exp_free_i(interp,i,(Tcl_VarTraceProc *)0);
2247
        return rc;
2248
}
2249
 
2250
/*ARGSUSED*/
2251
static int
2252
Exp_LogFileCmd(clientData, interp, argc, argv)
2253
ClientData clientData;
2254
Tcl_Interp *interp;
2255
int argc;
2256
char **argv;
2257
{
2258
        static Tcl_DString dstring;
2259
        static int first_time = TRUE;
2260
        static int current_append;      /* true if currently appending */
2261
        static char *openarg = 0;        /* Tcl file identifier from -open */
2262
        static int leaveopen = FALSE;   /* true if -leaveopen was used */
2263
 
2264
        int old_logfile_all = logfile_all;
2265
        FILE *old_logfile = logfile;
2266
        char *old_openarg = openarg;
2267
        int old_leaveopen = leaveopen;
2268
 
2269
        int aflag = FALSE;
2270
        int append = TRUE;
2271
        char *filename = 0;
2272
        char *type;
2273
        FILE *writefilePtr;
2274
        int usage_error_occurred = FALSE;
2275
 
2276
        openarg = 0;
2277
        leaveopen = FALSE;
2278
 
2279
        if (first_time) {
2280
                Tcl_DStringInit(&dstring);
2281
                first_time = FALSE;
2282
        }
2283
 
2284
 
2285
#define usage_error     if (0) ; else {\
2286
                                 usage_error_occurred = TRUE;\
2287
                                 goto error;\
2288
                        }
2289
 
2290
        /* when this function returns, we guarantee that if logfile_all */
2291
        /* is TRUE, then logfile is non-zero */
2292
 
2293
        argv++;
2294
        argc--;
2295
        for (;argc>0;argc--,argv++) {
2296
                if (streq(*argv,"-open")) {
2297
                        if (!argv[1]) usage_error;
2298
                        openarg = ckalloc(strlen(argv[1])+1);
2299
                        strcpy(openarg,argv[1]);
2300
                        argc--; argv++;
2301
                } else if (streq(*argv,"-leaveopen")) {
2302
                        if (!argv[1]) usage_error;
2303
                        openarg = ckalloc(strlen(argv[1])+1);
2304
                        strcpy(openarg,argv[1]);
2305
                        leaveopen = TRUE;
2306
                        argc--; argv++;
2307
                } else if (streq(*argv,"-a")) {
2308
                        aflag = TRUE;
2309
                } else if (streq(*argv,"-info")) {
2310
                        if (logfile) {
2311
                                if (logfile_all) strcat(interp->result,"-a ");
2312
                                if (!current_append) strcat(interp->result,"-noappend ");
2313
                                strcat(interp->result,Tcl_DStringValue(&dstring));
2314
                        }
2315
                        return TCL_OK;
2316
                } else if (streq(*argv,"-noappend")) {
2317
                        append = FALSE;
2318
                } else break;
2319
        }
2320
 
2321
        if (argc == 1) {
2322
                filename = argv[0];
2323
        } else if (argc > 1) {
2324
                /* too many arguments */
2325
                usage_error
2326
        }
2327
 
2328
        if (openarg && filename) {
2329
                usage_error
2330
        }
2331
        if (aflag && !(openarg || filename)) {
2332
                usage_error
2333
        }
2334
 
2335
        logfile = 0;
2336
        logfile_all = aflag;
2337
 
2338
        current_append = append;
2339
 
2340
        type = (append?"a":"w");
2341
 
2342
        if (filename) {
2343
                filename = Tcl_TildeSubst(interp,filename,&dstring);
2344
                if (filename == NULL) {
2345
                        goto error;
2346
                } else {
2347
                        /* Tcl_TildeSubst doesn't store into dstring */
2348
                        /* if no ~, so force string into dstring */
2349
                        /* this is only needed so that next time around */
2350
                        /* we can get dstring for -info if necessary */
2351
                        if (Tcl_DStringValue(&dstring)[0] == '\0') {
2352
                                Tcl_DStringAppend(&dstring,filename,-1);
2353
                        }
2354
                }
2355
 
2356
                errno = 0;
2357
                if (NULL == (logfile = fopen(filename,type))) {
2358
                        char *msg;
2359
 
2360
                        if (errno == 0) {
2361
                                msg = open_failed;
2362
                        } else {
2363
                                msg = Tcl_PosixError(interp);
2364
                        }
2365
                        exp_error(interp,"%s: %s",filename,msg);
2366
                        Tcl_DStringFree(&dstring);
2367
                        goto error;
2368
                }
2369
        } else if (openarg) {
2370
                int cc;
2371
                int fd;
2372
                Tcl_Channel chan;
2373
                int mode;
2374
#if TCL_MAJOR_VERSION < 8
2375
                Tcl_File tclWriteFile;
2376
#endif /* TCL_MAJOR_VERSION < 8 */
2377
 
2378
                Tcl_DStringTrunc(&dstring,0);
2379
 
2380
#ifdef __CYGWIN32__
2381
               /* This doesn't work on cygwin32, because
2382
                   Tcl_GetChannelHandle is likely to return a Windows
2383
                   handle, and passing that to dup will fail.  */
2384
               exp_error(interp,"log_file -open and -leaveopen not supported on
2385
 cygwin32");
2386
               return TCL_ERROR;
2387
#endif
2388
 
2389
#if TCL7_4
2390
                cc = Tcl_GetOpenFile(interp,openarg,1,1,&writefilePtr);
2391
                if (cc == TCL_ERROR) goto error;
2392
 
2393
                if (-1 == (fd = dup(fileno(writefilePtr)))) {
2394
                        exp_error(interp,"dup: %s",Tcl_PosixError(interp));
2395
                        goto error;
2396
                }
2397
#endif
2398
                if (!(chan = Tcl_GetChannel(interp,openarg,&mode))) {
2399
                        return TCL_ERROR;
2400
                }
2401
                if (!(mode & TCL_WRITABLE)) {
2402
                        exp_error(interp,"channel is not writable");
2403
                }
2404
#if TCL_MAJOR_VERSION < 8
2405
                tclWriteFile = Tcl_GetChannelFile(chan, TCL_WRITABLE);
2406
                fd = dup((int)Tcl_GetFileInfo(tclWriteFile, (int *)0));
2407
#else
2408
                if (TCL_ERROR == Tcl_GetChannelHandle(chan, TCL_WRITABLE, (ClientData) &fd)) {
2409
                        goto error;
2410
                }
2411
                fd = dup(fd);
2412
#endif /* TCL_MAJOR_VERSION < 8 */
2413
                if (!(logfile = fdopen(fd,type))) {
2414
                        exp_error(interp,"fdopen: %s",Tcl_PosixError(interp));
2415
                        close(fd);
2416
                        goto error;
2417
                }
2418
 
2419
                if (leaveopen) {
2420
                        Tcl_DStringAppend(&dstring,"-leaveopen ",-1);
2421
                } else {
2422
                        Tcl_DStringAppend(&dstring,"-open ",-1);
2423
                }
2424
 
2425
                Tcl_DStringAppend(&dstring,openarg,-1);
2426
 
2427
                /*
2428
                 * It would be convenient now to tell Tcl to close its
2429
                 * file descriptor.  Alas, if involved in a pipeline, Tcl
2430
                 * will be unable to complete a wait on the process.
2431
                 * So simply remember that we meant to close it.  We will
2432
                 * do so later in our own close routine.
2433
                 */
2434
        }
2435
        if (logfile) {
2436
                setbuf(logfile,(char *)0);
2437
                exp_close_on_exec(fileno(logfile));
2438
        }
2439
 
2440
        if (old_logfile) {
2441
                fclose(old_logfile);
2442
        }
2443
 
2444
        if (old_openarg) {
2445
                if (!old_leaveopen) {
2446
                        close_tcl_file(interp,old_openarg);
2447
                }
2448
                ckfree((char *)old_openarg);
2449
        }
2450
 
2451
        return TCL_OK;
2452
 
2453
 error:
2454
        if (old_logfile) {
2455
                logfile = old_logfile;
2456
                logfile_all = old_logfile_all;
2457
        }
2458
 
2459
        if (openarg) ckfree(openarg);
2460
        openarg = old_openarg;
2461
        leaveopen = old_leaveopen;
2462
 
2463
        if (usage_error_occurred) {
2464
                exp_error(interp,"usage: log_file [-info] [-noappend] [[-a] file] [-[leave]open [open ...]]");
2465
        }
2466
 
2467
        return TCL_ERROR;
2468
}
2469
 
2470
/*ARGSUSED*/
2471
static int
2472
Exp_LogUserCmd(clientData, interp, argc, argv)
2473
ClientData clientData;
2474
Tcl_Interp *interp;
2475
int argc;
2476
char **argv;
2477
{
2478
        int old_loguser = loguser;
2479
 
2480
        if (argc == 0 || (argc == 2 && streq(argv[1],"-info"))) {
2481
                /* do nothing */
2482
        } else if (argc == 2) {
2483
                if (0 == atoi(argv[1])) loguser = FALSE;
2484
                else loguser = TRUE;
2485
        } else {
2486
                exp_error(interp,"usage: [-info|1|0]");
2487
        }
2488
 
2489
        sprintf(interp->result,"%d",old_loguser);
2490
 
2491
        return(TCL_OK);
2492
}
2493
 
2494
#ifdef TCL_DEBUGGER
2495
/*ARGSUSED*/
2496
static int
2497
Exp_DebugCmd(clientData, interp, argc, argv)
2498
ClientData clientData;
2499
Tcl_Interp *interp;
2500
int argc;
2501
char **argv;
2502
{
2503
        int now = FALSE;        /* soon if FALSE, now if TRUE */
2504
        int exp_tcl_debugger_was_available = exp_tcl_debugger_available;
2505
 
2506
        if (argc > 3) goto usage;
2507
 
2508
        if (argc == 1) {
2509
                sprintf(interp->result,"%d",exp_tcl_debugger_available);
2510
                return TCL_OK;
2511
        }
2512
 
2513
        argv++;
2514
 
2515
        while (*argv) {
2516
                if (streq(*argv,"-now")) {
2517
                        now = TRUE;
2518
                        argv++;
2519
                }
2520
                else break;
2521
        }
2522
 
2523
        if (!*argv) {
2524
                if (now) {
2525
                        Dbg_On(interp,1);
2526
                        exp_tcl_debugger_available = 1;
2527
                } else {
2528
                        goto usage;
2529
                }
2530
        } else if (streq(*argv,"0")) {
2531
                Dbg_Off(interp);
2532
                exp_tcl_debugger_available = 0;
2533
        } else {
2534
                Dbg_On(interp,now);
2535
                exp_tcl_debugger_available = 1;
2536
        }
2537
        sprintf(interp->result,"%d",exp_tcl_debugger_was_available);
2538
        return(TCL_OK);
2539
 usage:
2540
        exp_error(interp,"usage: [[-now] 1|0]");
2541
        return TCL_ERROR;
2542
}
2543
#endif
2544
 
2545
/*ARGSUSED*/
2546
static int
2547
Exp_ExpInternalCmd(clientData, interp, argc, argv)
2548
ClientData clientData;
2549
Tcl_Interp *interp;
2550
int argc;
2551
char **argv;
2552
{
2553
        static Tcl_DString dstring;
2554
        static int first_time = TRUE;
2555
        int fopened = FALSE;
2556
 
2557
        if (first_time) {
2558
                Tcl_DStringInit(&dstring);
2559
                first_time = FALSE;
2560
        }
2561
 
2562
        if (argc > 1 && streq(argv[1],"-info")) {
2563
                if (debugfile) {
2564
                        sprintf(interp->result,"-f %s ",
2565
                                Tcl_DStringValue(&dstring));
2566
                }
2567
                strcat(interp->result,((exp_is_debugging==0)?"0":"1"));
2568
                return TCL_OK;
2569
        }
2570
 
2571
        argv++;
2572
        argc--;
2573
        while (argc) {
2574
                if (!streq(*argv,"-f")) break;
2575
                argc--;argv++;
2576
                if (argc < 1) goto usage;
2577
                if (debugfile) fclose(debugfile);
2578
                argv[0] = Tcl_TildeSubst(interp, argv[0],&dstring);
2579
                if (argv[0] == NULL) goto error;
2580
                else {
2581
                        /* Tcl_TildeSubst doesn't store into dstring */
2582
                        /* if no ~, so force string into dstring */
2583
                        /* this is only needed so that next time around */
2584
                        /* we can get dstring for -info if necessary */
2585
                        if (Tcl_DStringValue(&dstring)[0] == '\0') {
2586
                                Tcl_DStringAppend(&dstring,argv[0],-1);
2587
                        }
2588
                }
2589
 
2590
                errno = 0;
2591
                if (NULL == (debugfile = fopen(*argv,"a"))) {
2592
                        char *msg;
2593
 
2594
                        if (errno == 0) {
2595
                                msg = open_failed;
2596
                        } else {
2597
                                msg = Tcl_PosixError(interp);
2598
                        }
2599
 
2600
                        exp_error(interp,"%s: %s",*argv,msg);
2601
                        goto error;
2602
                }
2603
                setbuf(debugfile,(char *)0);
2604
                exp_close_on_exec(fileno(debugfile));
2605
                fopened = TRUE;
2606
                argc--;argv++;
2607
        }
2608
 
2609
        if (argc != 1) goto usage;
2610
 
2611
        /* if no -f given, close file */
2612
        if (fopened == FALSE && debugfile) {
2613
                fclose(debugfile);
2614
                debugfile = 0;
2615
                Tcl_DStringFree(&dstring);
2616
        }
2617
 
2618
        exp_is_debugging = atoi(*argv);
2619
        return(TCL_OK);
2620
 usage:
2621
        exp_error(interp,"usage: [-f file] expr");
2622
 error:
2623
        Tcl_DStringFree(&dstring);
2624
        return TCL_ERROR;
2625
}
2626
 
2627
char *exp_onexit_action = 0;
2628
 
2629
/*ARGSUSED*/
2630
static int
2631
Exp_ExitCmd(clientData, interp, argc, argv)
2632
ClientData clientData;
2633
Tcl_Interp *interp;
2634
int argc;
2635
char **argv;
2636
{
2637
        int value = 0;
2638
 
2639
        argv++;
2640
 
2641
        if (*argv) {
2642
                if (exp_flageq(*argv,"-onexit",3)) {
2643
                        argv++;
2644
                        if (*argv) {
2645
                                int len = strlen(*argv);
2646
                                if (exp_onexit_action)
2647
                                        ckfree(exp_onexit_action);
2648
                                exp_onexit_action = ckalloc(len + 1);
2649
                                strcpy(exp_onexit_action,*argv);
2650
                        } else if (exp_onexit_action) {
2651
                                Tcl_AppendResult(interp,exp_onexit_action,(char *)0);
2652
                        }
2653
                        return TCL_OK;
2654
                } else if (exp_flageq(*argv,"-noexit",3)) {
2655
                        argv++;
2656
                        exp_exit_handlers((ClientData)interp);
2657
                        return TCL_OK;
2658
                }
2659
        }
2660
 
2661
        if (*argv) {
2662
                if (Tcl_GetInt(interp, *argv, &value) != TCL_OK) {
2663
                        return TCL_ERROR;
2664
                }
2665
        }
2666
 
2667
        exp_exit(interp,value);
2668
        /*NOTREACHED*/
2669
}
2670
 
2671
/* so cmd table later is more intuitive */
2672
#define Exp_CloseObjCmd Exp_CloseCmd
2673
 
2674
/*ARGSUSED*/
2675
static int
2676
Exp_CloseCmd(clientData, interp, argc, argv)
2677
ClientData clientData;
2678
Tcl_Interp *interp;
2679
int argc;
2680
#if TCL_MAJOR_VERSION < 8
2681
char **argv;
2682
#else
2683
Tcl_Obj *CONST argv[];  /* Argument objects. */
2684
#endif
2685
{
2686
        int onexec_flag = FALSE;        /* true if -onexec seen */
2687
        int close_onexec;
2688
        int slave_flag = FALSE;
2689
        int m = -1;
2690
 
2691
        int argc_orig = argc;
2692
#if TCL_MAJOR_VERSION < 8
2693
        char **argv_orig = argv;
2694
#else
2695
        Tcl_Obj *CONST *argv_orig = argv;
2696
#endif
2697
 
2698
        argc--; argv++;
2699
 
2700
#if TCL_MAJOR_VERSION < 8
2701
#define STARARGV *argv
2702
#else
2703
#define STARARGV Tcl_GetStringFromObj(*argv,(int *)0)
2704
#endif
2705
 
2706
        for (;argc>0;argc--,argv++) {
2707
                if (streq("-i",STARARGV)) {
2708
                        argc--; argv++;
2709
                        if (argc == 0) {
2710
                                exp_error(interp,"usage: -i spawn_id");
2711
                                return(TCL_ERROR);
2712
                        }
2713
                        m = atoi(STARARGV);
2714
                } else if (streq(STARARGV,"-slave")) {
2715
                        slave_flag = TRUE;
2716
                } else if (streq(STARARGV,"-onexec")) {
2717
                        argc--; argv++;
2718
                        if (argc == 0) {
2719
                                exp_error(interp,"usage: -onexec 0|1");
2720
                                return(TCL_ERROR);
2721
                        }
2722
                        onexec_flag = TRUE;
2723
                        close_onexec = atoi(STARARGV);
2724
                } else break;
2725
        }
2726
 
2727
        if (argc) {
2728
                /* doesn't look like our format, it must be a Tcl-style file */
2729
                /* handle.  Lucky that formats are easily distinguishable. */
2730
                /* Historical note: we used "close"  long before there was a */
2731
                /* Tcl builtin by the same name. */
2732
 
2733
                Tcl_CmdInfo info;
2734
                Tcl_ResetResult(interp);
2735
                if (0 == Tcl_GetCommandInfo(interp,"close",&info)) {
2736
                        info.clientData = 0;
2737
                }
2738
#if TCL_MAJOR_VERSION < 8
2739
                return(Tcl_CloseCmd(info.clientData,interp,argc_orig,argv_orig));
2740
#else
2741
                return(Tcl_CloseObjCmd(info.clientData,interp,argc_orig,argv_orig));
2742
#endif
2743
        }
2744
 
2745
        if (m == -1) {
2746
                if (exp_update_master(interp,&m,1,0) == 0) return(TCL_ERROR);
2747
        }
2748
 
2749
        if (slave_flag) {
2750
                struct exp_f *f = exp_fd2f(interp,m,1,0,"-slave");
2751
                if (!f) return TCL_ERROR;
2752
 
2753
                if (f->slave_fd) {
2754
                        close(f->slave_fd);
2755
                        f->slave_fd = EXP_NOFD;
2756
 
2757
                        exp_slave_control(m,1);
2758
 
2759
                        return TCL_OK;
2760
                } else {
2761
                        exp_error(interp,"no such slave");
2762
                        return TCL_ERROR;
2763
                }
2764
        }
2765
 
2766
        if (onexec_flag) {
2767
                /* heck, don't even bother to check if fd is open or a real */
2768
                /* spawn id, nothing else depends on it */
2769
                fcntl(m,F_SETFD,close_onexec);
2770
                return TCL_OK;
2771
        }
2772
 
2773
        return(exp_close(interp,m));
2774
}
2775
 
2776
/*ARGSUSED*/
2777
static void
2778
tcl_tracer(clientData,interp,level,command,cmdProc,cmdClientData,argc,argv)
2779
ClientData clientData;
2780
Tcl_Interp *interp;
2781
int level;
2782
char *command;
2783
int (*cmdProc)();
2784
ClientData cmdClientData;
2785
int argc;
2786
char *argv[];
2787
{
2788
        int i;
2789
 
2790
        /* come out on stderr, by using errorlog */
2791
        errorlog("%2d",level);
2792
        for (i = 0;i<level;i++) exp_nferrorlog("  ",0/*ignored - satisfy lint*/);
2793
        errorlog("%s\r\n",command);
2794
}
2795
 
2796
/*ARGSUSED*/
2797
static int
2798
Exp_StraceCmd(clientData, interp, argc, argv)
2799
ClientData clientData;
2800
Tcl_Interp *interp;
2801
int argc;
2802
char **argv;
2803
{
2804
        static int trace_level = 0;
2805
        static Tcl_Trace trace_handle;
2806
 
2807
        if (argc > 1 && streq(argv[1],"-info")) {
2808
                sprintf(interp->result,"%d",trace_level);
2809
                return TCL_OK;
2810
        }
2811
 
2812
        if (argc != 2) {
2813
                exp_error(interp,"usage: trace level");
2814
                return(TCL_ERROR);
2815
        }
2816
        /* tracing already in effect, undo it */
2817
        if (trace_level > 0) Tcl_DeleteTrace(interp,trace_handle);
2818
 
2819
        /* get and save new trace level */
2820
        trace_level = atoi(argv[1]);
2821
        if (trace_level > 0)
2822
                trace_handle = Tcl_CreateTrace(interp,
2823
                                trace_level,tcl_tracer,(ClientData)0);
2824
        return(TCL_OK);
2825
}
2826
 
2827
/* following defn's are stolen from tclUnix.h */
2828
 
2829
/*
2830
 * The type of the status returned by wait varies from UNIX system
2831
 * to UNIX system.  The macro below defines it:
2832
 */
2833
 
2834
#if 0
2835
#ifndef NO_UNION_WAIT
2836
#   define WAIT_STATUS_TYPE union wait
2837
#else
2838
#   define WAIT_STATUS_TYPE int
2839
#endif
2840
#endif /* 0 */
2841
 
2842
/*
2843
 * following definitions stolen from tclUnix.h
2844
 * (should have been made public!)
2845
 
2846
 * Supply definitions for macros to query wait status, if not already
2847
 * defined in header files above.
2848
 */
2849
 
2850
#if 0
2851
#ifndef WIFEXITED
2852
#   define WIFEXITED(stat)  (((*((int *) &(stat))) & 0xff) == 0)
2853
#endif
2854
 
2855
#ifndef WEXITSTATUS
2856
#   define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff)
2857
#endif
2858
 
2859
#ifndef WIFSIGNALED
2860
#   define WIFSIGNALED(stat) (((*((int *) &(stat)))) && ((*((int *) &(stat))) == ((*((int *) &(stat))) & 0x00ff)))
2861
#endif
2862
 
2863
#ifndef WTERMSIG
2864
#   define WTERMSIG(stat)    ((*((int *) &(stat))) & 0x7f)
2865
#endif
2866
 
2867
#ifndef WIFSTOPPED
2868
#   define WIFSTOPPED(stat)  (((*((int *) &(stat))) & 0xff) == 0177)
2869
#endif
2870
 
2871
#ifndef WSTOPSIG
2872
#   define WSTOPSIG(stat)    (((*((int *) &(stat))) >> 8) & 0xff)
2873
#endif
2874
#endif /* 0 */
2875
 
2876
/* end of stolen definitions */
2877
 
2878
/* Describe the processes created with Expect's fork.
2879
This allows us to wait on them later.
2880
 
2881
This is maintained as a linked list.  As additional procs are forked,
2882
new links are added.  As procs disappear, links are marked so that we
2883
can reuse them later.
2884
*/
2885
 
2886
struct forked_proc {
2887
        int pid;
2888
        WAIT_STATUS_TYPE wait_status;
2889
        enum {not_in_use, wait_done, wait_not_done} link_status;
2890
        struct forked_proc *next;
2891
} *forked_proc_base = 0;
2892
 
2893
void
2894
fork_clear_all()
2895
{
2896
        struct forked_proc *f;
2897
 
2898
        for (f=forked_proc_base;f;f=f->next) {
2899
                f->link_status = not_in_use;
2900
        }
2901
}
2902
 
2903
void
2904
fork_init(f,pid)
2905
struct forked_proc *f;
2906
int pid;
2907
{
2908
        f->pid = pid;
2909
        f->link_status = wait_not_done;
2910
}
2911
 
2912
/* make an entry for a new proc */
2913
void
2914
fork_add(pid)
2915
int pid;
2916
{
2917
        struct forked_proc *f;
2918
 
2919
        for (f=forked_proc_base;f;f=f->next) {
2920
                if (f->link_status == not_in_use) break;
2921
        }
2922
 
2923
        /* add new entry to the front of the list */
2924
        if (!f) {
2925
                f = (struct forked_proc *)ckalloc(sizeof(struct forked_proc));
2926
                f->next = forked_proc_base;
2927
                forked_proc_base = f;
2928
        }
2929
        fork_init(f,pid);
2930
}
2931
 
2932
/* Provide a last-chance guess for this if not defined already */
2933
#ifndef WNOHANG
2934
#define WNOHANG WNOHANG_BACKUP_VALUE
2935
#endif
2936
 
2937
/* wait returns are a hodgepodge of things
2938
 If wait fails, something seriously has gone wrong, for example:
2939
   bogus arguments (i.e., incorrect, bogus spawn id)
2940
   no children to wait on
2941
   async event failed
2942
 If wait succeeeds, something happened on a particular pid
2943
   3rd arg is 0 if successfully reaped (if signal, additional fields supplied)
2944
   3rd arg is -1 if unsuccessfully reaped (additional fields supplied)
2945
*/
2946
/*ARGSUSED*/
2947
static int
2948
Exp_WaitCmd(clientData, interp, argc, argv)
2949
ClientData clientData;
2950
Tcl_Interp *interp;
2951
int argc;
2952
char **argv;
2953
{
2954
        int master_supplied = FALSE;
2955
        int m;                  /* master waited for */
2956
        struct exp_f *f;        /* ditto */
2957
        struct forked_proc *fp = 0;      /* handle to a pure forked proc */
2958
 
2959
        struct exp_f ftmp;      /* temporary memory for either f or fp */
2960
 
2961
        int nowait = FALSE;
2962
        int result = 0;          /* 0 means child was successfully waited on */
2963
                                /* -1 means an error occurred */
2964
                                /* -2 means no eligible children to wait on */
2965
#define NO_CHILD -2
2966
 
2967
        argv++;
2968
        argc--;
2969
        for (;argc>0;argc--,argv++) {
2970
                if (streq(*argv,"-i")) {
2971
                        argc--; argv++;
2972
                        if (argc==0) {
2973
                                exp_error(interp,"usage: -i spawn_id");
2974
                                return(TCL_ERROR);
2975
                        }
2976
                        master_supplied = TRUE;
2977
                        m = atoi(*argv);
2978
                } else if (streq(*argv,"-nowait")) {
2979
                        nowait = TRUE;
2980
                }
2981
        }
2982
 
2983
        if (!master_supplied) {
2984
                if (0 == exp_update_master(interp,&m,0,0))
2985
                        return TCL_ERROR;
2986
        }
2987
 
2988
        if (m != EXP_SPAWN_ID_ANY) {
2989
                if (0 == exp_fd2f(interp,m,0,0,"wait")) {
2990
                        return TCL_ERROR;
2991
                }
2992
 
2993
                f = exp_fs + m;
2994
 
2995
                /* check if waited on already */
2996
                /* things opened by "open" or set with -nowait */
2997
                /* are marked sys_waited already */
2998
                if (!f->sys_waited) {
2999
                        if (nowait) {
3000
                                /* should probably generate an error */
3001
                                /* if SIGCHLD is trapped. */
3002
 
3003
                                /* pass to Tcl, so it can do wait */
3004
                                /* in background */
3005
#if TCL_MAJOR_VERSION < 8
3006
                                Tcl_DetachPids(1,&f->pid);
3007
#else
3008
                                Tcl_DetachPids(1,(Tcl_Pid *)&f->pid);
3009
#endif
3010
                                exp_wait_zero(&f->wait);
3011
                        } else {
3012
                                while (1) {
3013
                                        if (Tcl_AsyncReady()) {
3014
                                                int rc = Tcl_AsyncInvoke(interp,TCL_OK);
3015
                                                if (rc != TCL_OK) return(rc);
3016
                                        }
3017
 
3018
                                        result = waitpid(f->pid,&f->wait,0);
3019
                                        if (result == f->pid) break;
3020
                                        if (result == -1) {
3021
                                                if (errno == EINTR) continue;
3022
                                                else break;
3023
                                        }
3024
                                }
3025
                        }
3026
                }
3027
 
3028
                /*
3029
                 * Now have Tcl reap anything we just detached.
3030
                 * This also allows procs user has created with "exec &"
3031
                 * and and associated with an "exec &" process to be reaped.
3032
                 */
3033
 
3034
                Tcl_ReapDetachedProcs();
3035
                exp_rearm_sigchld(interp); /* new */
3036
        } else {
3037
                /* wait for any of our own spawned processes */
3038
                /* we call waitpid rather than wait to avoid running into */
3039
                /* someone else's processes.  Yes, according to Ousterhout */
3040
                /* this is the best way to do it. */
3041
 
3042
                for (m=0;m<=exp_fd_max;m++) {
3043
                        f = exp_fs + m;
3044
                        if (!f->valid) continue;
3045
                        if (f->pid == exp_getpid) continue; /* skip ourself */
3046
                        if (f->user_waited) continue;   /* one wait only! */
3047
                        if (f->sys_waited) break;
3048
                   restart:
3049
                        result = waitpid(f->pid,&f->wait,WNOHANG);
3050
                        if (result == f->pid) break;
3051
                        if (result == 0) continue;       /* busy, try next */
3052
                        if (result == -1) {
3053
                                if (errno == EINTR) goto restart;
3054
                                else break;
3055
                        }
3056
                }
3057
 
3058
                /* if it's not a spawned process, maybe its a forked process */
3059
                for (fp=forked_proc_base;fp;fp=fp->next) {
3060
                        if (fp->link_status == not_in_use) continue;
3061
                restart2:
3062
                        result = waitpid(fp->pid,&fp->wait_status,WNOHANG);
3063
                        if (result == fp->pid) {
3064
                                m = -1; /* DOCUMENT THIS! */
3065
                                break;
3066
                        }
3067
                        if (result == 0) continue;       /* busy, try next */
3068
                        if (result == -1) {
3069
                                if (errno == EINTR) goto restart2;
3070
                                else break;
3071
                        }
3072
                }
3073
 
3074
                if (m > exp_fd_max) {
3075
                        result = NO_CHILD;      /* no children */
3076
                        Tcl_ReapDetachedProcs();
3077
                }
3078
                exp_rearm_sigchld(interp);
3079
        }
3080
 
3081
        /*  sigh, wedge forked_proc into an exp_f structure so we don't
3082
         *  have to rewrite remaining code (too much)
3083
         */
3084
        if (fp) {
3085
                f = &ftmp;
3086
                f->pid = fp->pid;
3087
                f->wait = fp->wait_status;
3088
        }
3089
 
3090
        /* non-portable assumption that pid_t can be printed with %d */
3091
 
3092
        if (result == -1) {
3093
                sprintf(interp->result,"%d %d -1 %d POSIX %s %s",
3094
                        f->pid,m,errno,Tcl_ErrnoId(),Tcl_ErrnoMsg(errno));
3095
                result = TCL_OK;
3096
        } else if (result == NO_CHILD) {
3097
                interp->result = "no children";
3098
                return TCL_ERROR;
3099
        } else {
3100
                sprintf(interp->result,"%d %d 0 %d",
3101
                                        f->pid,m,WEXITSTATUS(f->wait));
3102
                if (WIFSIGNALED(f->wait)) {
3103
                        Tcl_AppendElement(interp,"CHILDKILLED");
3104
                        Tcl_AppendElement(interp,Tcl_SignalId((int)(WTERMSIG(f->wait))));
3105
                        Tcl_AppendElement(interp,Tcl_SignalMsg((int) (WTERMSIG(f->wait))));
3106
                } else if (WIFSTOPPED(f->wait)) {
3107
                        Tcl_AppendElement(interp,"CHILDSUSP");
3108
                        Tcl_AppendElement(interp,Tcl_SignalId((int) (WSTOPSIG(f->wait))));
3109
                        Tcl_AppendElement(interp,Tcl_SignalMsg((int) (WSTOPSIG(f->wait))));
3110
                }
3111
        }
3112
 
3113
        if (fp) {
3114
                fp->link_status = not_in_use;
3115
                return ((result == -1)?TCL_ERROR:TCL_OK);
3116
        }
3117
 
3118
        f->sys_waited = TRUE;
3119
        f->user_waited = TRUE;
3120
 
3121
        /* if user has already called close, make sure fd really is closed */
3122
        /* and forget about this entry entirely */
3123
        if (f->user_closed) {
3124
                if (!f->sys_closed) {
3125
                        sys_close(m,f);
3126
                }
3127
                f->valid = FALSE;
3128
        }
3129
        return ((result == -1)?TCL_ERROR:TCL_OK);
3130
}
3131
 
3132
/*ARGSUSED*/
3133
static int
3134
Exp_ForkCmd(clientData, interp, argc, argv)
3135
ClientData clientData;
3136
Tcl_Interp *interp;
3137
int argc;
3138
char **argv;
3139
{
3140
        int rc;
3141
        if (argc > 1) {
3142
                exp_error(interp,"usage: fork");
3143
                return(TCL_ERROR);
3144
        }
3145
 
3146
        rc = fork();
3147
        if (rc == -1) {
3148
                exp_error(interp,"fork: %s",Tcl_PosixError(interp));
3149
                return TCL_ERROR;
3150
        } else if (rc == 0) {
3151
                /* child */
3152
                exp_forked = TRUE;
3153
                exp_getpid = getpid();
3154
                fork_clear_all();
3155
        } else {
3156
                /* parent */
3157
                fork_add(rc);
3158
        }
3159
 
3160
        /* both child and parent follow remainder of code */
3161
        sprintf(interp->result,"%d",rc);
3162
        debuglog("fork: returns {%s}\r\n",interp->result);
3163
        return(TCL_OK);
3164
}
3165
 
3166
/*ARGSUSED*/
3167
static int
3168
Exp_DisconnectCmd(clientData, interp, argc, argv)
3169
ClientData clientData;
3170
Tcl_Interp *interp;
3171
int argc;
3172
char **argv;
3173
{
3174
        /* tell Saber to ignore non-use of ttyfd */
3175
        /*SUPPRESS 591*/
3176
        int ttyfd;
3177
 
3178
        if (argc > 1) {
3179
                exp_error(interp,"usage: disconnect");
3180
                return(TCL_ERROR);
3181
        }
3182
 
3183
        if (exp_disconnected) {
3184
                exp_error(interp,"already disconnected");
3185
                return(TCL_ERROR);
3186
        }
3187
        if (!exp_forked) {
3188
                exp_error(interp,"can only disconnect child process");
3189
                return(TCL_ERROR);
3190
        }
3191
        exp_disconnected = TRUE;
3192
 
3193
        /* ignore hangup signals generated by testing ptys in getptymaster */
3194
        /* and other places */
3195
        signal(SIGHUP,SIG_IGN);
3196
 
3197
        /* reopen prevents confusion between send/expect_user */
3198
        /* accidentally mapping to a real spawned process after a disconnect */
3199
        if (exp_fs[0].pid != EXP_NOPID) {
3200
                exp_close(interp,0);
3201
                open("/dev/null",0);
3202
                fd_new(0, EXP_NOPID);
3203
        }
3204
        if (exp_fs[1].pid != EXP_NOPID) {
3205
                exp_close(interp,1);
3206
                open("/dev/null",1);
3207
                fd_new(1, EXP_NOPID);
3208
        }
3209
        if (exp_fs[2].pid != EXP_NOPID) {
3210
                /* reopen stderr saves error checking in error/log routines. */
3211
                exp_close(interp,2);
3212
                open("/dev/null",1);
3213
                fd_new(2, EXP_NOPID);
3214
        }
3215
 
3216
        Tcl_UnsetVar(interp,"tty_spawn_id",TCL_GLOBAL_ONLY);
3217
 
3218
#ifdef DO_SETSID
3219
        setsid();
3220
#else
3221
#ifdef SYSV3
3222
        /* put process in our own pgrp, and lose controlling terminal */
3223
#ifdef sysV88
3224
        /* With setpgrp first, child ends up with closed stdio */
3225
        /* according to Dave Schmitt <daves@techmpc.csg.gss.mot.com> */
3226
        if (fork()) exit(0);
3227
        setpgrp();
3228
#else
3229
        setpgrp();
3230
        /*signal(SIGHUP,SIG_IGN); moved out to above */
3231
        if (fork()) exit(0);     /* first child exits (as per Stevens, */
3232
        /* UNIX Network Programming, p. 79-80) */
3233
        /* second child process continues as daemon */
3234
#endif
3235
#else /* !SYSV3 */
3236
#ifdef MIPS_BSD
3237
        /* required on BSD side of MIPS OS <jmsellen@watdragon.waterloo.edu> */
3238
#       include <sysv/sys.s>
3239
        syscall(SYS_setpgrp);
3240
#endif
3241
        setpgrp(0,0);
3242
/*      setpgrp(0,getpid());*/  /* put process in our own pgrp */
3243
 
3244
/* Pyramid lacks this defn */
3245
#ifdef TIOCNOTTY
3246
        ttyfd = open("/dev/tty", O_RDWR);
3247
        if (ttyfd >= 0) {
3248
                /* zap controlling terminal if we had one */
3249
                (void) ioctl(ttyfd, TIOCNOTTY, (char *)0);
3250
                (void) close(ttyfd);
3251
        }
3252
#endif /* TIOCNOTTY */
3253
 
3254
#endif /* SYSV3 */
3255
#endif /* DO_SETSID */
3256
        return(TCL_OK);
3257
}
3258
 
3259
/*ARGSUSED*/
3260
static int
3261
Exp_OverlayCmd(clientData, interp, argc, argv)
3262
ClientData clientData;
3263
Tcl_Interp *interp;
3264
int argc;
3265
char **argv;
3266
{
3267
        int newfd, oldfd;
3268
        int dash_name = 0;
3269
        char *command;
3270
 
3271
        argc--; argv++;
3272
        while (argc) {
3273
                if (*argv[0] != '-') break;      /* not a flag */
3274
                if (streq(*argv,"-")) {         /* - by itself */
3275
                        argc--; argv++;
3276
                        dash_name = 1;
3277
                        continue;
3278
                }
3279
                newfd = atoi(argv[0]+1);
3280
                argc--; argv++;
3281
                if (argc == 0) {
3282
                        exp_error(interp,"overlay -# requires additional argument");
3283
                        return(TCL_ERROR);
3284
                }
3285
                oldfd = atoi(argv[0]);
3286
                argc--; argv++;
3287
                debuglog("overlay: mapping fd %d to %d\r\n",oldfd,newfd);
3288
                if (oldfd != newfd) (void) dup2(oldfd,newfd);
3289
                else debuglog("warning: overlay: old fd == new fd (%d)\r\n",oldfd);
3290
        }
3291
        if (argc == 0) {
3292
                exp_error(interp,"need program name");
3293
                return(TCL_ERROR);
3294
        }
3295
        command = argv[0];
3296
        if (dash_name) {
3297
                argv[0] = ckalloc(1+strlen(command));
3298
                sprintf(argv[0],"-%s",command);
3299
        }
3300
 
3301
        signal(SIGINT, SIG_DFL);
3302
        signal(SIGQUIT, SIG_DFL);
3303
        (void) execvp(command,argv);
3304
        exp_error(interp,"execvp(%s): %s\r\n",argv[0],Tcl_PosixError(interp));
3305
        return(TCL_ERROR);
3306
}
3307
 
3308
#if 0
3309
/*ARGSUSED*/
3310
int
3311
cmdReady(clientData, interp, argc, argv)
3312
ClientData clientData;
3313
Tcl_Interp *interp;
3314
int argc;
3315
char **argv;
3316
{
3317
        char num[4];    /* can hold up to "999 " */
3318
        char buf[1024]; /* can easily hold 256 spawn_ids! */
3319
        int i, j;
3320
        int *masters, *masters2;
3321
        int timeout = get_timeout();
3322
 
3323
        if (argc < 2) {
3324
                exp_error(interp,"usage: ready spawn_id1 [spawn_id2 ...]");
3325
                return(TCL_ERROR);
3326
        }
3327
 
3328
        masters = (int *)ckalloc((argc-1)*sizeof(int));
3329
        masters2 = (int *)ckalloc((argc-1)*sizeof(int));
3330
 
3331
        for (i=1;i<argc;i++) {
3332
                j = atoi(argv[i]);
3333
                if (!exp_fd2f(interp,j,1,"ready")) {
3334
                        ckfree(masters);
3335
                        return(TCL_ERROR);
3336
                }
3337
                masters[i-1] = j;
3338
        }
3339
        j = i-1;
3340
        if (TCL_ERROR == ready(masters,i-1,masters2,&j,&timeout))
3341
                return(TCL_ERROR);
3342
 
3343
        /* pack result back into out-array */
3344
        buf[0] = '\0';
3345
        for (i=0;i<j;i++) {
3346
                sprintf(num,"%d ",masters2[i]); /* note extra blank */
3347
                strcat(buf,num);
3348
        }
3349
        ckfree(masters); ckfree(masters2);
3350
        Tcl_Return(interp,buf,TCL_VOLATILE);
3351
        return(TCL_OK);
3352
}
3353
#endif
3354
 
3355
/*ARGSUSED*/
3356
int
3357
Exp_InterpreterCmd(clientData, interp, argc, argv)
3358
ClientData clientData;
3359
Tcl_Interp *interp;
3360
int argc;
3361
char **argv;
3362
{
3363
        if (argc != 1) {
3364
                exp_error(interp,"no arguments allowed");
3365
                return(TCL_ERROR);
3366
        }
3367
 
3368
        return(exp_interpreter(interp));
3369
        /* errors and ok, are caught by exp_interpreter() and discarded */
3370
        /* to return TCL_OK, type "return" */
3371
}
3372
 
3373
/* this command supercede's Tcl's builtin CONTINUE command */
3374
/*ARGSUSED*/
3375
int
3376
Exp_ExpContinueDeprecatedCmd(clientData, interp, argc, argv)
3377
ClientData clientData;
3378
Tcl_Interp *interp;
3379
int argc;
3380
char **argv;
3381
{
3382
       if (argc == 1) return(TCL_CONTINUE);
3383
       else if (argc == 2) {
3384
               if (streq(argv[1],"-expect")) {
3385
                       debuglog("continue -expect is deprecated, use exp_continue\r\n");
3386
                       return(EXP_CONTINUE);
3387
               }
3388
       }
3389
       exp_error(interp,"usage: continue [-expect]\n");
3390
       return(TCL_ERROR);
3391
}
3392
 
3393
/* this command supercede's Tcl's builtin CONTINUE command */
3394
/*ARGSUSED*/
3395
int
3396
Exp_ExpContinueCmd(clientData, interp, argc, argv)
3397
ClientData clientData;
3398
Tcl_Interp *interp;
3399
int argc;
3400
char **argv;
3401
{
3402
        if (argc == 1) {
3403
                return EXP_CONTINUE;
3404
        } else if ((argc == 2) && (0 == strcmp(argv[1],"-continue_timer"))) {
3405
                return EXP_CONTINUE_TIMER;
3406
        }
3407
 
3408
        exp_error(interp,"usage: exp_continue [-continue_timer]\n");
3409
        return(TCL_ERROR);
3410
}
3411
 
3412
#if TCL_MAJOR_VERSION < 8
3413
/* most of this is directly from Tcl's definition for return */
3414
/*ARGSUSED*/
3415
int
3416
Exp_InterReturnCmd(clientData, interp, argc, argv)
3417
ClientData clientData;
3418
Tcl_Interp *interp;
3419
int argc;
3420
char **argv;
3421
{
3422
        /* let Tcl's return command worry about args */
3423
        /* if successful (i.e., TCL_RETURN is returned) */
3424
        /* modify the result, so that we will handle it specially */
3425
 
3426
        int result = Tcl_ReturnCmd(clientData,interp,argc,argv);
3427
        if (result == TCL_RETURN)
3428
                result = EXP_TCL_RETURN;
3429
        return result;
3430
}
3431
#else
3432
/* most of this is directly from Tcl's definition for return */
3433
/*ARGSUSED*/
3434
int
3435
Exp_InterReturnObjCmd(clientData, interp, objc, objv)
3436
ClientData clientData;
3437
Tcl_Interp *interp;
3438
int objc;
3439
Tcl_Obj *CONST objv[];
3440
{
3441
    /* let Tcl's return command worry about args */
3442
    /* if successful (i.e., TCL_RETURN is returned) */
3443
    /* modify the result, so that we will handle it specially */
3444
 
3445
#if TCL_MAJOR_VERSION < 8
3446
    int result = Tcl_ReturnCmd(clientData,interp,objc,objv);
3447
#else
3448
       int result = Tcl_ReturnObjCmd(clientData,interp,objc,objv);
3449
#endif
3450
 
3451
    if (result == TCL_RETURN)
3452
        result = EXP_TCL_RETURN;
3453
    return result;
3454
}
3455
#endif
3456
 
3457
/*ARGSUSED*/
3458
int
3459
Exp_OpenCmd(clientData, interp, argc, argv)
3460
ClientData clientData;
3461
Tcl_Interp *interp;
3462
int argc;
3463
char **argv;
3464
{
3465
        struct exp_f *f;
3466
        int m = -1;
3467
        int m2;
3468
        int leaveopen = FALSE;
3469
        Tcl_Channel chan;
3470
 
3471
        argc--; argv++;
3472
 
3473
        for (;argc>0;argc--,argv++) {
3474
                if (streq(*argv,"-i")) {
3475
                        argc--; argv++;
3476
                        if (!*argv) {
3477
                                exp_error(interp,"usage: -i spawn_id");
3478
                                return TCL_ERROR;
3479
                        }
3480
                        m = atoi(*argv);
3481
                } else if (streq(*argv,"-leaveopen")) {
3482
                        leaveopen = TRUE;
3483
                        argc--; argv++;
3484
                } else break;
3485
        }
3486
 
3487
        if (m == -1) {
3488
                if (exp_update_master(interp,&m,0,0) == 0) return TCL_ERROR;
3489
        }
3490
 
3491
        if (0 == (f = exp_fd2f(interp,m,1,0,"exp_open"))) return TCL_ERROR;
3492
 
3493
        /* make a new copy of file descriptor */
3494
        if (-1 == (m2 = dup(m))) {
3495
                exp_error(interp,"fdopen: %s",Tcl_PosixError(interp));
3496
                return TCL_ERROR;
3497
        }
3498
 
3499
        if (!leaveopen) {
3500
                /* remove from Expect's memory in anticipation of passing to Tcl */
3501
                if (f->pid != EXP_NOPID) {
3502
#if TCL_MAJOR_VERSION < 8
3503
                        Tcl_DetachPids(1,&f->pid);
3504
#else
3505
                        Tcl_DetachPids(1,(Tcl_Pid *)&f->pid);
3506
#endif
3507
                        f->pid = EXP_NOPID;
3508
                        f->sys_waited = f->user_waited = TRUE;
3509
                }
3510
                exp_close(interp,m);
3511
        }
3512
 
3513
        chan = Tcl_MakeFileChannel(
3514
#if TCL_MAJOR_VERSION < 8
3515
                            (ClientData)m2,
3516
#endif
3517
                            (ClientData)m2,
3518
                            TCL_READABLE|TCL_WRITABLE);
3519
        Tcl_RegisterChannel(interp, chan);
3520
        Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
3521
        return TCL_OK;
3522
}
3523
 
3524
/* return 1 if a string is substring of a flag */
3525
/* this version is the code used by the macro that everyone calls */
3526
int
3527
exp_flageq_code(flag,string,minlen)
3528
char *flag;
3529
char *string;
3530
int minlen;             /* at least this many chars must match */
3531
{
3532
        for (;*flag;flag++,string++,minlen--) {
3533
                if (*string == '\0') break;
3534
                if (*string != *flag) return 0;
3535
        }
3536
        if (*string == '\0' && minlen <= 0) return 1;
3537
        return 0;
3538
}
3539
 
3540
void
3541
exp_create_commands(interp,c)
3542
Tcl_Interp *interp;
3543
struct exp_cmd_data *c;
3544
{
3545
#if TCL_MAJOR_VERSION < 8
3546
        Interp *iPtr = (Interp *) interp;
3547
#else
3548
        Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
3549
        Namespace *currNsPtr   = (Namespace *) Tcl_GetCurrentNamespace(interp);
3550
#endif
3551
        char cmdnamebuf[80];
3552
 
3553
        for (;c->name;c++) {
3554
#if TCL_MAJOR_VERSION < 8
3555
                int create = FALSE;
3556
                /* if already defined, don't redefine */
3557
                if (c->flags & EXP_REDEFINE) create = TRUE;
3558
                else if (!Tcl_FindHashEntry(&iPtr->commandTable,c->name)) {
3559
                        create = TRUE;
3560
                }
3561
                if (create) {
3562
                                Tcl_CreateCommand(interp,c->name,c->proc,
3563
                                                  c->data,exp_deleteProc);
3564
                        }
3565
#else
3566
                /* if already defined, don't redefine */
3567
                if ((c->flags & EXP_REDEFINE) ||
3568
                    !(Tcl_FindHashEntry(&globalNsPtr->cmdTable,c->name) ||
3569
                      Tcl_FindHashEntry(&currNsPtr->cmdTable,c->name))) {
3570
                        if (c->objproc)
3571
                                Tcl_CreateObjCommand(interp,c->name,
3572
                                                     c->objproc,c->data,exp_deleteObjProc);
3573
                        else
3574
                        Tcl_CreateCommand(interp,c->name,c->proc,
3575
                                          c->data,exp_deleteProc);
3576
                }
3577
#endif
3578
                if (!(c->name[0] == 'e' &&
3579
                      c->name[1] == 'x' &&
3580
                      c->name[2] == 'p')
3581
                    && !(c->flags & EXP_NOPREFIX)) {
3582
                        sprintf(cmdnamebuf,"exp_%s",c->name);
3583
#if TCL_MAJOR_VERSION < 8
3584
                        Tcl_CreateCommand(interp,cmdnamebuf,c->proc,
3585
                                c->data,exp_deleteProc);
3586
#else
3587
                        if (c->objproc)
3588
                                Tcl_CreateObjCommand(interp,cmdnamebuf,c->objproc,c->data,
3589
                                                     exp_deleteObjProc);
3590
                        else
3591
                        Tcl_CreateCommand(interp,cmdnamebuf,c->proc,
3592
                                             c->data,exp_deleteProc);
3593
#endif
3594
                }
3595
        }
3596
}
3597
 
3598
static struct exp_cmd_data cmd_data[]  = {
3599
#if TCL_MAJOR_VERSION < 8
3600
{"close",       Exp_CloseCmd,   0,       EXP_REDEFINE},
3601
#else
3602
{"close",       Exp_CloseObjCmd,        0,       0,       EXP_REDEFINE},
3603
#endif
3604
#ifdef TCL_DEBUGGER
3605
{"debug",       exp_proc(Exp_DebugCmd), 0,       0},
3606
#endif
3607
{"exp_internal",exp_proc(Exp_ExpInternalCmd),   0,       0},
3608
{"disconnect",  exp_proc(Exp_DisconnectCmd),    0,       0},
3609
{"exit",        exp_proc(Exp_ExitCmd),  0,       EXP_REDEFINE},
3610
{"exp_continue",exp_proc(Exp_ExpContinueCmd),0,  0},
3611
{"fork",        exp_proc(Exp_ForkCmd),  0,       0},
3612
{"exp_pid",     exp_proc(Exp_ExpPidCmd),        0,       0},
3613
{"getpid",      exp_proc(Exp_GetpidDeprecatedCmd),0,     0},
3614
{"interpreter", exp_proc(Exp_InterpreterCmd),   0,       0},
3615
{"log_file",    exp_proc(Exp_LogFileCmd),       0,       0},
3616
{"log_user",    exp_proc(Exp_LogUserCmd),       0,       0},
3617
{"exp_open",    exp_proc(Exp_OpenCmd),  0,       0},
3618
{"overlay",     exp_proc(Exp_OverlayCmd),       0,       0},
3619
#if TCL_MAJOR_VERSION < 8
3620
{"inter_return",Exp_InterReturnCmd,     0,       0},
3621
#else
3622
{"inter_return",Exp_InterReturnObjCmd,  0,       0,       0},
3623
#endif
3624
{"send",        exp_proc(Exp_SendCmd),  (ClientData)&sendCD_proc,       0},
3625
{"send_error",  exp_proc(Exp_SendCmd),  (ClientData)&sendCD_error,      0},
3626
{"send_log",    exp_proc(Exp_SendLogCmd),       0,       0},
3627
{"send_tty",    exp_proc(Exp_SendCmd),  (ClientData)&sendCD_tty,        0},
3628
{"send_user",   exp_proc(Exp_SendCmd),  (ClientData)&sendCD_user,       0},
3629
{"sleep",       exp_proc(Exp_SleepCmd), 0,       0},
3630
{"spawn",       exp_proc(Exp_SpawnCmd), 0,       0},
3631
{"strace",      exp_proc(Exp_StraceCmd),        0,       0},
3632
{"wait",        exp_proc(Exp_WaitCmd),  0,       0},
3633
{0}};
3634
 
3635
void
3636
exp_init_most_cmds(interp)
3637
Tcl_Interp *interp;
3638
{
3639
        exp_create_commands(interp,cmd_data);
3640
 
3641
#ifdef HAVE_PTYTRAP
3642
        Tcl_InitHashTable(&slaveNames,TCL_STRING_KEYS);
3643
#endif /* HAVE_PTYTRAP */
3644
 
3645
        exp_close_in_child = exp_close_tcl_files;
3646
}
3647
/* cribbed directly from tclBasic.c */
3648
int
3649
Tcl_CloseCmd(stuff, interp, argc, argv)
3650
     ClientData *stuff;
3651
     Tcl_Interp *interp;
3652
     int argc;
3653
     char **argv;
3654
{
3655
#define NUM_ARGS 20
3656
    Tcl_Obj *(argStorage[NUM_ARGS]);
3657
    register Tcl_Obj **objv = argStorage;
3658
    int i, result;
3659
    Tcl_Obj *objPtr;
3660
 
3661
    /*
3662
     * Create the object argument array "objv". Make sure objv is large
3663
     * enough to hold the objc arguments plus 1 extra for the zero
3664
     * end-of-objv word.
3665
     */
3666
 
3667
    if ((argc + 1) > NUM_ARGS) {
3668
        objv = (Tcl_Obj **)
3669
            Tcl_Alloc((unsigned)(argc + 1) * sizeof(Tcl_Obj *));
3670
    }
3671
 
3672
    for (i = 0;  i < argc;  i++) {
3673
        objPtr = Tcl_NewStringObj(argv[i], -1);
3674
        Tcl_IncrRefCount(objPtr);
3675
        objv[i] = objPtr;
3676
    }
3677
    objv[argc] = 0;
3678
 
3679
    /*
3680
     * Invoke the command's object-based Tcl_ObjCmdProc.
3681
     */
3682
 
3683
    result = Tcl_CloseObjCmd(stuff, interp, argc, objv);
3684
 
3685
    /*
3686
     * Move the interpreter's object result to the string result,
3687
     * then reset the object result.
3688
     * FAILS IF OBJECT RESULT'S STRING REPRESENTATION CONTAINS NULL BYTES.
3689
     */
3690
 
3691
    Tcl_SetResult(interp,
3692
            TclGetStringFromObj(Tcl_GetObjResult(interp), (int *) NULL),
3693
            TCL_VOLATILE);
3694
 
3695
    /*
3696
     * Decrement the ref counts for the argument objects created above,
3697
     * then free the objv array if malloc'ed storage was used.
3698
     */
3699
 
3700
    for (i = 0;  i < argc;  i++) {
3701
        objPtr = objv[i];
3702
        Tcl_DecrRefCount(objPtr);
3703
    }
3704
    if (objv != argStorage) {
3705
        Tcl_Free((char *) objv);
3706
    }
3707
    return result;
3708
#undef NUM_ARGS
3709
}

powered by: WebSVN 2.1.0

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