1/* interact (using select) - give user keyboard control 2 3Written by: Don Libes, NIST, 2/6/90 4 5Design and implementation of this program was paid for by U.S. tax 6dollars. Therefore it is public domain. However, the author and NIST 7would appreciate credit if this program or parts of it are used. 8 9*/ 10 11#include "expect_cf.h" 12#include <stdio.h> 13#ifdef HAVE_INTTYPES_H 14# include <inttypes.h> 15#endif 16#include <sys/types.h> 17#ifdef HAVE_UNISTD_H 18# include <unistd.h> 19#endif 20 21#ifdef TIME_WITH_SYS_TIME 22# include <sys/time.h> 23# include <time.h> 24#else 25# if HAVE_SYS_TIME_H 26# include <sys/time.h> 27# else 28# include <time.h> 29# endif 30#endif 31 32#ifdef HAVE_SYS_WAIT_H 33#include <sys/wait.h> 34#endif 35 36#include <ctype.h> 37 38#include "tclInt.h" 39#include "string.h" 40 41#include "exp_tty_in.h" 42#include "exp_rename.h" 43#include "exp_prog.h" 44#include "exp_command.h" 45#include "exp_log.h" 46#include "exp_event.h" /* exp_get_next_event decl */ 47 48/* Tcl 8.5+ moved this internal - needed for when I compile expect against 8.5. */ 49#ifndef TCL_REG_BOSONLY 50#define TCL_REG_BOSONLY 002000 51#endif 52 53typedef struct ThreadSpecificData { 54 Tcl_Obj *cmdObjReturn; 55 Tcl_Obj *cmdObjInterpreter; 56} ThreadSpecificData; 57 58static Tcl_ThreadDataKey dataKey; 59 60#define INTER_OUT "interact_out" 61#define out(var,val) \ 62 expDiagLog("interact: set %s(%s) ",INTER_OUT,var); \ 63 expDiagLogU(expPrintify(val)); \ 64 expDiagLogU("\"\r\n"); \ 65 Tcl_SetVar2(interp,INTER_OUT,var,val,0); 66 67/* 68 * tests if we are running this using a real tty 69 * 70 * these tests are currently only used to control what gets written to the 71 * logfile. Note that removal of the test of "..._is_tty" means that stdin 72 * or stdout could be redirected and yet stdout would still be logged. 73 * However, it's not clear why anyone would use log_file when these are 74 * redirected in the first place. On the other hand, it is reasonable to 75 * run expect as a daemon in which case, stdin/out do not appear to be 76 * ttys, yet it makes sense for them to be logged with log_file as if they 77 * were. 78 */ 79#if 0 80#define real_tty_output(x) (exp_stdout_is_tty && (((x)==1) || ((x)==exp_dev_tty))) 81#define real_tty_input(x) (exp_stdin_is_tty && (((x)==0) || ((x)==exp_dev_tty))) 82#endif 83 84#define real_tty_output(x) ((x->fdout == 1) || (expDevttyIs(x))) 85#define real_tty_input(x) (exp_stdin_is_tty && ((x->fdin==0) || (expDevttyIs(x)))) 86 87#define new(x) (x *)ckalloc(sizeof(x)) 88 89struct action { 90 Tcl_Obj *statement; 91 int tty_reset; /* if true, reset tty mode upon action */ 92 int iread; /* if true, reread indirects */ 93 int iwrite; /* if true, write spawn_id element */ 94 struct action *next; /* chain only for later for freeing */ 95}; 96 97struct keymap { 98 Tcl_Obj *keys; /* original pattern provided by user */ 99 int re; /* true if looking to match a regexp. */ 100 int null; /* true if looking to match 0 byte */ 101 int case_sensitive; 102 int echo; /* if keystrokes should be echoed */ 103 int writethru; /* if keystrokes should go through to process */ 104 int indices; /* true if should write indices */ 105 struct action action; 106 struct keymap *next; 107}; 108 109struct output { 110 struct exp_i *i_list; 111 struct action *action_eof; 112 struct output *next; 113}; 114 115struct input { 116 struct exp_i *i_list; 117 struct output *output; 118 struct action *action_eof; 119 struct action *action_timeout; 120 struct keymap *keymap; 121 int timeout_nominal; /* timeout nominal */ 122 int timeout_remaining; /* timeout remaining */ 123 struct input *next; 124}; 125 126/* 127 * Once we are handed an ExpState from the event handler, we can figure out 128 * which "struct input *" it references by using expStateToInput. This has is 129 * populated by expCreateStateToInput. 130 */ 131 132struct input * 133expStateToInput( 134 Tcl_HashTable *hash, 135 ExpState *esPtr) 136{ 137 Tcl_HashEntry *entry = Tcl_FindHashEntry(hash,(char *)esPtr); 138 139 if (!entry) { 140 /* should never happen */ 141 return 0; 142 } 143 return ((struct input *)Tcl_GetHashValue(entry)); 144} 145 146void 147expCreateStateToInput( 148 Tcl_HashTable *hash, 149 ExpState *esPtr, 150 struct input *inp) 151{ 152 Tcl_HashEntry *entry; 153 int newPtr; 154 155 entry = Tcl_CreateHashEntry(hash,(char *)esPtr,&newPtr); 156 Tcl_SetHashValue(entry,(ClientData)inp); 157} 158 159static void free_input(Tcl_Interp *interp, struct input *i); 160static void free_keymap(struct keymap *km); 161static void free_output(Tcl_Interp *interp, struct output *o); 162static void free_action(struct action *a); 163static struct action *new_action(struct action **base); 164static int inter_eval( 165 Tcl_Interp *interp, 166 struct action *action, 167 ExpState *esPtr); 168 169/* intMatch() accepts user keystrokes and returns one of MATCH, 170CANMATCH, or CANTMATCH. These describe whether the keystrokes match a 171key sequence, and could or can't if more characters arrive. The 172function assigns a matching keymap if there is a match or can-match. 173A matching keymap is assigned on can-match so we know whether to echo 174or not. 175 176intMatch is optimized (if you can call it that) towards a small 177number of key mappings, but still works well for large maps, since no 178function calls are made, and we stop as soon as there is a single-char 179mismatch, and go on to the next one. A hash table or compiled DFA 180probably would not buy very much here for most maps. 181 182The basic idea of how this works is it does a smart sequential search. 183At each position of the input string, we attempt to match each of the 184keymaps. If at least one matches, the first match is returned. 185 186If there is a CANMATCH and there are more keymaps to try, we continue 187trying. If there are no more keymaps to try, we stop trying and 188return with an indication of the first keymap that can match. 189 190Note that I've hacked up the regexp pattern matcher in two ways. One 191is to force the pattern to always be anchored at the front. That way, 192it doesn't waste time attempting to match later in the string (before 193we're ready). The other is to return can-match. 194 195*/ 196 197static int 198intMatch( 199 ExpState *esPtr, 200 struct keymap *keymap, /* linked list of keymaps */ 201 struct keymap **km_match, /* keymap that matches or can match */ 202 int *matchLen, /* # of bytes that matched */ 203 int *skip, /* # of chars to skip */ 204 Tcl_RegExpInfo *info) 205{ 206 Tcl_UniChar *string; 207 struct keymap *km; 208 char *ks; /* string from a keymap */ 209 210 Tcl_UniChar *start_search; /* where in string to start searching */ 211 int offset; /* # of chars from string to start searching */ 212 213 Tcl_UniChar *string_end; 214 int numchars; 215 int rm_nulls; /* skip nulls if true */ 216 Tcl_UniChar ch; 217 218 string = esPtr->input.buffer; 219 numchars = esPtr->input.use; /* Actually #chars */ 220 221 /* assert (*km == 0) */ 222 223 /* a shortcut that should help master output which typically */ 224 /* is lengthy and has no key maps. Otherwise it would mindlessly */ 225 /* iterate on each character anyway. */ 226 if (!keymap) { 227 *skip = numchars; 228 return(EXP_CANTMATCH); 229 } 230 231 rm_nulls = esPtr->rm_nulls; 232 233 string_end = string + numchars; 234 235 /* 236 * Maintain both a character index and a string pointer so we 237 * can easily index into either the UTF or the Unicode representations. 238 */ 239 240 for (start_search = string, offset = 0; 241 start_search < string_end; 242 start_search ++, offset++) { 243 244 ch = *start_search; 245 246 if (*km_match) break; /* if we've already found a CANMATCH */ 247 /* don't bother starting search from positions */ 248 /* further along the string */ 249 250 for (km=keymap;km;km=km->next) { 251 Tcl_UniChar *s; /* current character being examined */ 252 253 if (km->null) { 254 if (ch == 0) { 255 *skip = start_search-string; 256 *matchLen = 1; /* s - start_search == 1 */ 257 *km_match = km; 258 return(EXP_MATCH); 259 } 260 } else if (!km->re) { 261 int kslen; 262 Tcl_UniChar sch, ksch; 263 264 /* fixed string */ 265 266 ks = Tcl_GetString(km->keys); 267 for (s = start_search;; s++, ks += kslen) { 268 /* if we hit the end of this map, must've matched! */ 269 if (*ks == 0) { 270 *skip = start_search-string; 271 *matchLen = s-start_search; 272 *km_match = km; 273 return(EXP_MATCH); 274 } 275 276 /* if we ran out of user-supplied characters, and */ 277 /* still haven't matched, it might match if the user */ 278 /* supplies more characters next time */ 279 280 if (s == string_end) { 281 /* skip to next key entry, but remember */ 282 /* possibility that this entry might match */ 283 if (!*km_match) *km_match = km; 284 break; 285 } 286 287 sch = *s; 288 kslen = Tcl_UtfToUniChar(ks, &ksch); 289 290 if (sch == ksch) continue; 291 if ((sch == '\0') && rm_nulls) { 292 kslen = 0; 293 continue; 294 } 295 break; 296 } 297 } else { 298 /* regexp */ 299 Tcl_RegExp re; 300 int flags; 301 int result; 302 Tcl_Obj* buf; 303 304 re = Tcl_GetRegExpFromObj(NULL, km->keys, 305 TCL_REG_ADVANCED|TCL_REG_BOSONLY|TCL_REG_CANMATCH); 306 flags = (offset > 0) ? TCL_REG_NOTBOL : 0; 307 308 /* ZZZ: Future optimization: Avoid copying */ 309 buf = Tcl_NewUnicodeObj (esPtr->input.buffer, esPtr->input.use); 310 Tcl_IncrRefCount (buf); 311 result = Tcl_RegExpExecObj(NULL, re, buf, offset, 312 -1 /* nmatches */, flags); 313 Tcl_DecrRefCount (buf); 314 if (result > 0) { 315 *km_match = km; 316 *skip = start_search-string; 317 Tcl_RegExpGetInfo(re, info); 318 *matchLen = info->matches[0].end; 319 return EXP_MATCH; 320 } else if (result == 0) { 321 Tcl_RegExpGetInfo(re, info); 322 323 /* 324 * Check to see if there was a partial match starting 325 * at the current character. 326 */ 327 if (info->extendStart == 0) { 328 if (!*km_match) *km_match = km; 329 } 330 } 331 } 332 } 333 } 334 335 if (*km_match) { 336 /* report CANMATCH for -re and -ex */ 337 338 /* 339 * since canmatch is only detected after we've advanced too far, 340 * adjust start_search back to make other computations simpler 341 */ 342 start_search--; 343 344 *skip = start_search - string; 345 *matchLen = string_end - start_search; 346 return(EXP_CANMATCH); 347 } 348 349 *skip = start_search-string; 350 return(EXP_CANTMATCH); 351} 352 353/* put regexp result in variables */ 354static void 355intRegExpMatchProcess( 356 Tcl_Interp *interp, 357 ExpState *esPtr, 358 struct keymap *km, /* ptr for above while parsing */ 359 Tcl_RegExpInfo *info, 360 int offset) 361{ 362 char name[20], value[20]; 363 int i; 364 Tcl_Obj* buf = Tcl_NewUnicodeObj (esPtr->input.buffer,esPtr->input.use); 365 366 for (i=0;i<=info->nsubs;i++) { 367 int start, end; 368 Tcl_Obj *val; 369 370 start = info->matches[i].start + offset; 371 if (start == -1) continue; 372 end = (info->matches[i].end-1) + offset; 373 374 if (km->indices) { 375 /* start index */ 376 sprintf(name,"%d,start",i); 377 sprintf(value,"%d",start); 378 out(name,value); 379 380 /* end index */ 381 sprintf(name,"%d,end",i); 382 sprintf(value,"%d",end); 383 out(name,value); 384 } 385 386 /* string itself */ 387 sprintf(name,"%d,string",i); 388 val = Tcl_GetRange(buf, start, end); 389 expDiagLog("interact: set %s(%s) \"",INTER_OUT,name); 390 expDiagLogU(expPrintifyObj(val)); 391 expDiagLogU("\"\r\n"); 392 Tcl_SetVar2Ex(interp,INTER_OUT,name,val,0); 393 } 394 Tcl_DecrRefCount (buf); 395} 396 397/* 398 * echo chars 399 */ 400static void 401intEcho( 402 ExpState *esPtr, 403 int skipBytes, 404 int matchBytes) 405{ 406 int seenBytes; /* either printed or echoed */ 407 int echoBytes; 408 int offsetBytes; 409 410 /* write is unlikely to fail, since we just read from same descriptor */ 411 seenBytes = esPtr->printed + esPtr->echoed; 412 if (skipBytes >= seenBytes) { 413 echoBytes = matchBytes; 414 offsetBytes = skipBytes; 415 } else if ((matchBytes + skipBytes - seenBytes) > 0) { 416 echoBytes = matchBytes + skipBytes - seenBytes; 417 offsetBytes = seenBytes; 418 } 419 420 (void) expWriteCharsUni(esPtr, 421 esPtr->input.buffer + offsetBytes, 422 echoBytes); 423 424 esPtr->echoed = matchBytes + skipBytes - esPtr->printed; 425} 426 427/* 428 * intRead() does the logical equivalent of a read() for the interact command. 429 * Returns # of bytes read or negative number (EXP_XXX) indicating unusual event. 430 */ 431static int 432intRead( 433 Tcl_Interp *interp, 434 ExpState *esPtr, 435 int warnOnBufferFull, 436 int interruptible, 437 int key) 438{ 439 Tcl_UniChar *eobOld; /* old end of buffer */ 440 int cc; 441 int numchars; 442 Tcl_UniChar *str; 443 444 str = esPtr->input.buffer; 445 numchars = esPtr->input.use; 446 eobOld = str + numchars; 447 448 /* We drop one third when are at least 2/3 full */ 449 /* condition is (size >= max*2/3) <=> (size*3 >= max*2) */ 450 if (numchars*3 >= esPtr->input.max*2) { 451 /* 452 * In theory, interact could be invoked when this situation 453 * already exists, hence the "probably" in the warning below 454 */ 455 if (warnOnBufferFull) { 456 expDiagLogU("WARNING: interact buffer is full, probably because your\r\n"); 457 expDiagLogU("patterns have matched all of it but require more chars\r\n"); 458 expDiagLogU("in order to complete the match.\r\n"); 459 expDiagLogU("Dumping first half of buffer in order to continue\r\n"); 460 expDiagLogU("Recommend you enlarge the buffer or fix your patterns.\r\n"); 461 } 462 exp_buffer_shuffle(interp,esPtr,0,INTER_OUT,"interact"); 463 } 464 if (!interruptible) { 465 cc = Tcl_ReadChars(esPtr->channel, esPtr->input.newchars, 466 esPtr->input.max - esPtr->input.use, 467 0 /* no append */); 468 } else { 469#ifdef SIMPLE_EVENT 470 cc = intIRead(esPtr->channel, esPtr->input.newchars, 471 esPtr->input.max - esPtr->input.use, 472 0 /* no append */); 473#endif 474 } 475 476 if (cc > 0) { 477 memcpy (esPtr->input.buffer + esPtr->input.use, 478 Tcl_GetUnicodeFromObj (esPtr->input.newchars, NULL), 479 cc * sizeof (Tcl_UniChar)); 480 esPtr->input.use += cc; 481 482 expDiagLog("spawn id %s sent <",esPtr->name); 483 expDiagLogU(expPrintifyUni(eobOld,cc)); 484 expDiagLogU(">\r\n"); 485 486 esPtr->key = key; 487 } 488 return cc; 489} 490 491 492 493#ifdef SIMPLE_EVENT 494 495/* 496 497The way that the "simple" interact works is that the original Expect 498process reads from the tty and writes to the spawned process. A child 499process is forked to read from the spawned process and write to the 500tty. It looks like this: 501 502 user 503 --> tty >-- 504 / \ 505 ^ v 506 child original 507 process Expect 508 ^ process 509 | v 510 \ / 511 < spawned < 512 process 513 514*/ 515 516 517 518#ifndef WEXITSTATUS 519#define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff) 520#endif 521 522#include <setjmp.h> 523 524#ifdef HAVE_SIGLONGJMP 525static sigjmp_buf env; /* for interruptable read() */ 526#else 527static jmp_buf env; /* for interruptable read() */ 528#endif /* HAVE_SIGLONGJMP */ 529 530static int reading; /* while we are reading */ 531 /* really, while "env" is valid */ 532static int deferred_interrupt = FALSE; /* if signal is received, but not */ 533 /* in expIRead record this here, so it will */ 534 /* be handled next time through expIRead */ 535 536static void 537sigchld_handler() 538{ 539 if (reading) { 540#ifdef HAVE_SIGLONGJMP 541 siglongjmp(env,1); 542#else 543 longjmp(env,1); 544#endif /* HAVE_SIGLONGJMP */ 545 } 546 deferred_interrupt = TRUE; 547} 548 549#define EXP_CHILD_EOF -100 550 551/* 552 * Name: expIRead, do an interruptable read 553 * 554 * intIRead() reads from chars from the user. 555 * 556 * It returns early if it detects the death of a proc (either the spawned 557 * process or the child (surrogate). 558 */ 559static int 560intIRead( 561 Tcl_Channel channel, 562 Tcl_Obj *obj, 563 int size, 564 int flags) 565{ 566 int cc = EXP_CHILD_EOF; 567 568 if (deferred_interrupt) return(cc); 569 570 if ( 571#ifdef HAVE_SIGLONGJMP 572 0 == sigsetjmp(env,1) 573#else 574 0 == setjmp(env) 575#endif /* HAVE_SIGLONGJMP */ 576 ) { 577 reading = TRUE; 578 cc = Tcl_ReadChars(channel,obj,size,flags); 579 } 580 reading = FALSE; 581 return(cc); 582} 583 584/* exit status for the child process created by cmdInteract */ 585#define CHILD_DIED -2 586#define SPAWNED_PROCESS_DIED -3 587 588static void 589clean_up_after_child( 590 Tcl_Interp *interp, 591 ExpState *esPtr) 592{ 593 expWaitOnOne(); /* wait for slave */ 594 expWaitOnOne(); /* wait for child */ 595 596 deferred_interrupt = FALSE; 597 if (esPtr->close_on_eof) { 598 exp_close(interp,esPtr); 599} 600} 601#endif /*SIMPLE_EVENT*/ 602 603static int 604update_interact_fds( 605 Tcl_Interp *interp, 606 int *esPtrCount, 607 Tcl_HashTable **esPtrToInput, /* map from ExpStates to "struct inputs" */ 608 ExpState ***esPtrs, 609 struct input *input_base, 610 int do_indirect, /* if true do indirects */ 611 int *config_count, 612 int *real_tty_caller) 613{ 614 struct input *inp; 615 struct output *outp; 616 struct exp_state_list *fdp; 617 int count; 618 619 int real_tty = FALSE; 620 621 *config_count = exp_configure_count; 622 623 count = 0; 624 for (inp = input_base;inp;inp=inp->next) { 625 626 if (do_indirect) { 627 /* do not update "direct" entries (again) */ 628 /* they were updated upon creation */ 629 if (inp->i_list->direct == EXP_INDIRECT) { 630 exp_i_update(interp,inp->i_list); 631 } 632 for (outp = inp->output;outp;outp=outp->next) { 633 if (outp->i_list->direct == EXP_INDIRECT) { 634 exp_i_update(interp,outp->i_list); 635 } 636 } 637 } 638 639 /* revalidate all input descriptors */ 640 for (fdp = inp->i_list->state_list;fdp;fdp=fdp->next) { 641 count++; 642 /* have to "adjust" just in case spawn id hasn't had */ 643 /* a buffer sized yet */ 644 if (!expStateCheck(interp,fdp->esPtr,1,1,"interact")) { 645 return(TCL_ERROR); 646 } 647 } 648 649 /* revalidate all output descriptors */ 650 for (outp = inp->output;outp;outp=outp->next) { 651 for (fdp = outp->i_list->state_list;fdp;fdp=fdp->next) { 652 /* make user_spawn_id point to stdout */ 653 if (!expStdinoutIs(fdp->esPtr)) { 654 if (!expStateCheck(interp,fdp->esPtr,1,0,"interact")) 655 return(TCL_ERROR); 656 } 657 } 658 } 659 } 660 if (!do_indirect) return TCL_OK; 661 662 if (*esPtrToInput == 0) { 663 *esPtrToInput = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable)); 664 *esPtrs = (ExpState **)ckalloc(count * sizeof(ExpState *)); 665 } else { 666 /* if hash table already exists, delete it and start over */ 667 Tcl_DeleteHashTable(*esPtrToInput); 668 *esPtrs = (ExpState **)ckrealloc((char *)*esPtrs,count * sizeof(ExpState *)); 669 } 670 Tcl_InitHashTable(*esPtrToInput,TCL_ONE_WORD_KEYS); 671 672 count = 0; 673 for (inp = input_base;inp;inp=inp->next) { 674 for (fdp = inp->i_list->state_list;fdp;fdp=fdp->next) { 675 /* build map to translate from spawn_id to struct input */ 676 expCreateStateToInput(*esPtrToInput,fdp->esPtr,inp); 677 678 /* build input to ready() */ 679 (*esPtrs)[count] = fdp->esPtr; 680 681 if (real_tty_input(fdp->esPtr)) real_tty = TRUE; 682 683 count++; 684 } 685 } 686 *esPtrCount = count; 687 688 *real_tty_caller = real_tty; /* tell caller if we have found that */ 689 /* we are using real tty */ 690 691 return TCL_OK; 692} 693 694/*ARGSUSED*/ 695static char * 696inter_updateproc( 697 ClientData clientData, 698 Tcl_Interp *interp, /* Interpreter containing variable. */ 699 char *name1, /* Name of variable. */ 700 char *name2, /* Second part of variable name. */ 701 int flags) /* Information about what happened. */ 702{ 703 exp_configure_count++; 704 return 0; 705} 706 707#define finish(x) { status = x; goto done; } 708 709static char return_cmd[] = "return"; 710static char interpreter_cmd[] = "interpreter"; 711 712/*ARGSUSED*/ 713int 714Exp_InteractObjCmd( 715 ClientData clientData, 716 Tcl_Interp *interp, 717 int objc, 718 Tcl_Obj *CONST initial_objv[]) /* Argument objects. */ 719{ 720 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 721 722 Tcl_Obj *CONST *objv_copy; /* original, for error messages */ 723 Tcl_Obj **objv = (Tcl_Obj **) initial_objv; 724 char *string; 725 Tcl_UniChar *ustring; 726 727#ifdef SIMPLE_EVENT 728 int pid; 729#endif /*SIMPLE_EVENT*/ 730 731 /*declarations*/ 732 int input_count; /* count of struct input descriptors */ 733 734 Tcl_HashTable *esPtrToInput = 0; /* map from ExpState to "struct inputs" */ 735 ExpState **esPtrs; 736 struct keymap *km; /* ptr for above while parsing */ 737 Tcl_RegExpInfo reInfo; 738 ExpState *u = 0; 739 ExpState *esPtr = 0; 740 Tcl_Obj *chanName = 0; 741 int need_to_close_master = FALSE; /* if an eof is received */ 742 /* we use this to defer close until later */ 743 744 int next_tty_reset = FALSE; /* if we've seen a single -reset */ 745 int next_iread = FALSE;/* if we've seen a single -iread */ 746 int next_iwrite = FALSE;/* if we've seen a single -iread */ 747 int next_re = FALSE; /* if we've seen a single -re */ 748 int next_null = FALSE; /* if we've seen the null keyword */ 749 int next_writethru = FALSE;/*if macros should also go to proc output */ 750 int next_indices = FALSE;/* if we should write indices */ 751 int next_echo = FALSE; /* if macros should be echoed */ 752 int status = TCL_OK; /* final return value */ 753 int i; /* misc temp */ 754 int size; /* size temp */ 755 756 int timeout_simple = TRUE; /* if no or global timeout */ 757 758 int real_tty; /* TRUE if we are interacting with real tty */ 759 int tty_changed = FALSE;/* true if we had to change tty modes for */ 760 /* interact to work (i.e., to raw, noecho) */ 761 int was_raw; 762 int was_echo; 763 exp_tty tty_old; 764 765 Tcl_Obj *replace_user_by_process = 0; /* for -u flag */ 766 767 struct input *input_base; 768#define input_user input_base 769 struct input *input_default; 770 struct input *inp; /* overused ptr to struct input */ 771 struct output *outp; /* overused ptr to struct output */ 772 773 int dash_input_count = 0; /* # of "-input"s seen */ 774 int dash_o_count = 0; /* # of "-o"s seen */ 775 int arbitrary_timeout; 776 int default_timeout; 777 struct action action_timeout; /* common to all */ 778 struct action action_eof; /* common to all */ 779 struct action **action_eof_ptr; /* allow -input/ouput to */ 780 /* leave their eof-action assignable by a later */ 781 /* -eof */ 782 struct action *action_base = 0; 783 struct keymap **end_km; 784 785 int key; 786 int configure_count; /* monitor reconfigure events */ 787 Tcl_Obj* new_cmd = NULL; 788 789 if ((objc == 2) && exp_one_arg_braced(objv[1])) { 790 /* expect {...} */ 791 792 new_cmd = exp_eval_with_one_arg(clientData,interp,objv); 793 if (!new_cmd) return TCL_ERROR; 794 795 /* Replace old arguments with result of reparse */ 796 Tcl_ListObjGetElements (interp, new_cmd, &objc, &objv); 797 798 } else if ((objc == 3) && streq(Tcl_GetString(objv[1]),"-brace")) { 799 /* expect -brace {...} ... fake command line for reparsing */ 800 801 Tcl_Obj *new_objv[2]; 802 new_objv[0] = objv[0]; 803 new_objv[1] = objv[2]; 804 805 new_cmd = exp_eval_with_one_arg(clientData,interp,new_objv); 806 if (!new_cmd) return TCL_ERROR; 807 /* Replace old arguments with result of reparse */ 808 Tcl_ListObjGetElements (interp, new_cmd, &objc, &objv); 809 } 810 811 objv_copy = objv; 812 813 objv++; 814 objc--; 815 816 default_timeout = EXP_TIME_INFINITY; 817 arbitrary_timeout = EXP_TIME_INFINITY; /* if user specifies */ 818 /* a bunch of timeouts with EXP_TIME_INFINITY, this will be */ 819 /* left around for us to find. */ 820 821 input_user = new(struct input); 822 input_user->i_list = exp_new_i_simple(expStdinoutGet(),EXP_TEMPORARY); /* stdin by default */ 823 input_user->output = 0; 824 input_user->action_eof = &action_eof; 825 input_user->timeout_nominal = EXP_TIME_INFINITY; 826 input_user->action_timeout = 0; 827 input_user->keymap = 0; 828 829 end_km = &input_user->keymap; 830 inp = input_user; 831 action_eof_ptr = &input_user->action_eof; 832 833 input_default = new(struct input); 834 input_default->i_list = exp_new_i_simple((ExpState *)0,EXP_TEMPORARY); /* fix up later */ 835 input_default->output = 0; 836 input_default->action_eof = &action_eof; 837 input_default->timeout_nominal = EXP_TIME_INFINITY; 838 input_default->action_timeout = 0; 839 input_default->keymap = 0; 840 input_default->next = 0; /* no one else */ 841 input_user->next = input_default; 842 843 /* default and common -eof action */ 844 action_eof.statement = tsdPtr->cmdObjReturn; 845 action_eof.tty_reset = FALSE; 846 action_eof.iread = FALSE; 847 action_eof.iwrite = FALSE; 848 849 /* 850 * Parse the command arguments. 851 */ 852 for (;objc>0;objc--,objv++) { 853 string = Tcl_GetString(*objv); 854 if (string[0] == '-') { 855 static char *switches[] = { 856 "--", "-exact", "-re", "-input", 857 "-output", "-u", "-o", "-i", 858 "-echo", "-nobuffer", "-indices", "-f", 859 "-reset", "-F", "-iread", "-iwrite", 860 "-eof", "-timeout", "-nobrace", (char *)0 861 }; 862 enum switches { 863 EXP_SWITCH_DASH, EXP_SWITCH_EXACT, 864 EXP_SWITCH_REGEXP, EXP_SWITCH_INPUT, 865 EXP_SWITCH_OUTPUT, EXP_SWITCH_USER, 866 EXP_SWITCH_OPPOSITE, EXP_SWITCH_SPAWN_ID, 867 EXP_SWITCH_ECHO, EXP_SWITCH_NOBUFFER, 868 EXP_SWITCH_INDICES, EXP_SWITCH_FAST, 869 EXP_SWITCH_RESET, EXP_SWITCH_CAPFAST, 870 EXP_SWITCH_IREAD, EXP_SWITCH_IWRITE, 871 EXP_SWITCH_EOF, EXP_SWITCH_TIMEOUT, 872 EXP_SWITCH_NOBRACE 873 }; 874 int index; 875 876 /* 877 * Allow abbreviations of switches and report an error if we 878 * get an invalid switch. 879 */ 880 881 if (Tcl_GetIndexFromObj(interp, *objv, switches, "switch", 0, 882 &index) != TCL_OK) { 883 goto error; 884 } 885 switch ((enum switches) index) { 886 case EXP_SWITCH_DASH: 887 case EXP_SWITCH_EXACT: 888 objc--; 889 objv++; 890 goto pattern; 891 case EXP_SWITCH_REGEXP: 892 if (objc < 1) { 893 Tcl_WrongNumArgs(interp,1,objv_copy,"-re pattern"); 894 goto error; 895 } 896 next_re = TRUE; 897 objc--; 898 objv++; 899 900 /* 901 * Try compiling the expression so we can report 902 * any errors now rather then when we first try to 903 * use it. 904 */ 905 906 if (!(Tcl_GetRegExpFromObj(interp, *objv, 907 TCL_REG_ADVANCED|TCL_REG_BOSONLY))) { 908 goto error; 909 } 910 goto pattern; 911 case EXP_SWITCH_INPUT: 912 dash_input_count++; 913 if (dash_input_count == 2) { 914 inp = input_default; 915 input_user->next = input_default; 916 } else if (dash_input_count > 2) { 917 struct input *previous_input = inp; 918 inp = new(struct input); 919 previous_input->next = inp; 920 } 921 inp->output = 0; 922 inp->action_eof = &action_eof; 923 action_eof_ptr = &inp->action_eof; 924 inp->timeout_nominal = default_timeout; 925 inp->action_timeout = &action_timeout; 926 inp->keymap = 0; 927 end_km = &inp->keymap; 928 inp->next = 0; 929 objc--;objv++; 930 if (objc < 1) { 931 Tcl_WrongNumArgs(interp,1,objv_copy,"-input spawn_id"); 932 goto error; 933 } 934 inp->i_list = exp_new_i_complex(interp,Tcl_GetString(*objv), 935 EXP_TEMPORARY,inter_updateproc); 936 if (!inp->i_list) { 937 goto error; 938 } 939 break; 940 case EXP_SWITCH_OUTPUT: { 941 struct output *tmp; 942 943 /* imply a "-input" */ 944 if (dash_input_count == 0) dash_input_count = 1; 945 946 outp = new(struct output); 947 948 /* link new output in front of others */ 949 tmp = inp->output; 950 inp->output = outp; 951 outp->next = tmp; 952 953 objc--;objv++; 954 if (objc < 1) { 955 Tcl_WrongNumArgs(interp,1,objv_copy,"-output spawn_id"); 956 goto error; 957 } 958 outp->i_list = exp_new_i_complex(interp,Tcl_GetString(*objv), 959 EXP_TEMPORARY,inter_updateproc); 960 if (!outp->i_list) { 961 goto error; 962 } 963 outp->action_eof = &action_eof; 964 action_eof_ptr = &outp->action_eof; 965 break; 966 } 967 case EXP_SWITCH_USER: 968 objc--;objv++; 969 if (objc < 1) { 970 Tcl_WrongNumArgs(interp,1,objv_copy,"-u spawn_id"); 971 goto error; 972 } 973 replace_user_by_process = *objv; 974 975 /* imply a "-input" */ 976 if (dash_input_count == 0) dash_input_count = 1; 977 break; 978 case EXP_SWITCH_OPPOSITE: 979 /* apply following patterns to opposite side */ 980 /* of interaction */ 981 982 end_km = &input_default->keymap; 983 984 if (dash_o_count > 0) { 985 exp_error(interp,"cannot use -o more than once"); 986 goto error; 987 } 988 dash_o_count++; 989 990 /* imply two "-input" */ 991 if (dash_input_count < 2) { 992 dash_input_count = 2; 993 inp = input_default; 994 action_eof_ptr = &inp->action_eof; 995 } 996 break; 997 case EXP_SWITCH_SPAWN_ID: 998 /* substitute master */ 999 1000 objc--;objv++; 1001 chanName = *objv; 1002 /* will be used later on */ 1003 1004 end_km = &input_default->keymap; 1005 1006 /* imply two "-input" */ 1007 if (dash_input_count < 2) { 1008 dash_input_count = 2; 1009 inp = input_default; 1010 action_eof_ptr = &inp->action_eof; 1011 } 1012 break; 1013 case EXP_SWITCH_ECHO: 1014 next_echo = TRUE; 1015 break; 1016 case EXP_SWITCH_NOBUFFER: 1017 next_writethru = TRUE; 1018 break; 1019 case EXP_SWITCH_INDICES: 1020 next_indices = TRUE; 1021 break; 1022 case EXP_SWITCH_RESET: 1023 next_tty_reset = TRUE; 1024 break; 1025 case EXP_SWITCH_IREAD: 1026 next_iread = TRUE; 1027 break; 1028 case EXP_SWITCH_IWRITE: 1029 next_iwrite= TRUE; 1030 break; 1031 case EXP_SWITCH_EOF: { 1032 struct action *action; 1033 1034 objc--;objv++; 1035 expDiagLogU("-eof is deprecated, use eof\r\n"); 1036 *action_eof_ptr = action = new_action(&action_base); 1037 action->statement = *objv; 1038 action->tty_reset = next_tty_reset; 1039 next_tty_reset = FALSE; 1040 action->iwrite = next_iwrite; 1041 next_iwrite = FALSE; 1042 action->iread = next_iread; 1043 next_iread = FALSE; 1044 break; 1045 } 1046 case EXP_SWITCH_TIMEOUT: { 1047 int t; 1048 struct action *action; 1049 expDiagLogU("-timeout is deprecated, use timeout\r\n"); 1050 1051 objc--;objv++; 1052 if (objc < 1) { 1053 Tcl_WrongNumArgs(interp,1,objv_copy,"-timeout time"); 1054 goto error; 1055 } 1056 1057 if (Tcl_GetIntFromObj(interp, *objv, &t) != TCL_OK) { 1058 goto error; 1059 } 1060 objc--;objv++; 1061 if (t != -1) 1062 arbitrary_timeout = t; 1063 /* we need an arbitrary timeout to start */ 1064 /* search for lowest one later */ 1065 1066 timeout_simple = FALSE; 1067 action = inp->action_timeout = new_action(&action_base); 1068 inp->timeout_nominal = t; 1069 1070 action->statement = *objv; 1071 action->tty_reset = next_tty_reset; 1072 next_tty_reset = FALSE; 1073 action->iwrite = next_iwrite; 1074 next_iwrite = FALSE; 1075 action->iread = next_iread; 1076 next_iread = FALSE; 1077 break; 1078 } 1079 case EXP_SWITCH_FAST: 1080 case EXP_SWITCH_CAPFAST: 1081 /* noop compatibility switches for fast mode */ 1082 break; 1083 case EXP_SWITCH_NOBRACE: 1084 /* nobrace does nothing but take up space */ 1085 /* on the command line which prevents */ 1086 /* us from re-expanding any command lines */ 1087 /* of one argument that looks like it should */ 1088 /* be expanded to multiple arguments. */ 1089 break; 1090 } 1091 continue; 1092 } else { 1093 static char *options[] = { 1094 "eof", "timeout", "null", (char *)0 1095 }; 1096 enum options { 1097 EXP_OPTION_EOF, EXP_OPTION_TIMEOUT, EXP_OPTION_NULL 1098 }; 1099 int index; 1100 1101 /* 1102 * Match keywords exactly, otherwise they are patterns. 1103 */ 1104 1105 if (Tcl_GetIndexFromObj(interp, *objv, options, "option", 1106 1 /* exact */, &index) != TCL_OK) { 1107 Tcl_ResetResult(interp); 1108 goto pattern; 1109 } 1110 switch ((enum options) index) { 1111 case EXP_OPTION_EOF: { 1112 struct action *action; 1113 1114 objc--;objv++; 1115 *action_eof_ptr = action = new_action(&action_base); 1116 1117 action->statement = *objv; 1118 1119 action->tty_reset = next_tty_reset; 1120 next_tty_reset = FALSE; 1121 action->iwrite = next_iwrite; 1122 next_iwrite = FALSE; 1123 action->iread = next_iread; 1124 next_iread = FALSE; 1125 break; 1126 } 1127 case EXP_OPTION_TIMEOUT: { 1128 int t; 1129 struct action *action; 1130 1131 objc--;objv++; 1132 if (objc < 1) { 1133 Tcl_WrongNumArgs(interp,1,objv_copy,"timeout time [action]"); 1134 goto error; 1135 } 1136 if (Tcl_GetIntFromObj(interp, *objv, &t) != TCL_OK) { 1137 goto error; 1138 } 1139 objc--;objv++; 1140 1141 /* we need an arbitrary timeout to start */ 1142 /* search for lowest one later */ 1143 if (t != -1) arbitrary_timeout = t; 1144 1145 timeout_simple = FALSE; 1146 action = inp->action_timeout = new_action(&action_base); 1147 inp->timeout_nominal = t; 1148 1149 if (objc >= 1) { 1150 action->statement = *objv; 1151 } else { 1152 action->statement = 0; 1153 } 1154 1155 action->tty_reset = next_tty_reset; 1156 next_tty_reset = FALSE; 1157 action->iwrite = next_iwrite; 1158 next_iwrite = FALSE; 1159 action->iread = next_iread; 1160 next_iread = FALSE; 1161 break; 1162 } 1163 case EXP_OPTION_NULL: 1164 next_null = TRUE; 1165 goto pattern; 1166 } 1167 continue; 1168 } 1169 1170 /* 1171 * pick up the pattern 1172 */ 1173 1174 pattern: 1175 km = new(struct keymap); 1176 1177 /* so that we can match in order user specified */ 1178 /* link to end of keymap list */ 1179 *end_km = km; 1180 km->next = 0; 1181 end_km = &km->next; 1182 1183 km->echo = next_echo; 1184 km->writethru = next_writethru; 1185 km->indices = next_indices; 1186 km->action.tty_reset = next_tty_reset; 1187 km->action.iwrite = next_iwrite; 1188 km->action.iread = next_iread; 1189 1190 next_indices = next_echo = next_writethru = FALSE; 1191 next_tty_reset = FALSE; 1192 next_iwrite = next_iread = FALSE; 1193 1194 km->keys = *objv; 1195 1196 km->null = FALSE; 1197 km->re = 0; 1198 if (next_re) { 1199 km->re = TRUE; 1200 next_re = FALSE; 1201 } 1202 if (next_null) { 1203 km->null = TRUE; 1204 next_null = FALSE; 1205 } 1206 1207 objc--;objv++; 1208 if (objc >= 1) { 1209 km->action.statement = *objv; 1210 } else { 1211 km->action.statement = 0; 1212 } 1213 1214 expDiagLogU("defining key "); 1215 expDiagLogU(Tcl_GetString(km->keys)); 1216 expDiagLogU(", action "); 1217 expDiagLogU(km->action.statement?expPrintify(Tcl_GetString(km->action.statement)):"interpreter"); 1218 expDiagLogU("\r\n"); 1219 1220 /* imply a "-input" */ 1221 if (dash_input_count == 0) dash_input_count = 1; 1222 } 1223 1224 /* if the user has not supplied either "-output" for the */ 1225 /* default two "-input"s, fix them up here */ 1226 1227 if (!input_user->output) { 1228 struct output *o = new(struct output); 1229 if (!chanName) { 1230 if (!(esPtr = expStateCurrent(interp,1,1,0))) { 1231 goto error; 1232 } 1233 o->i_list = exp_new_i_simple(esPtr,EXP_TEMPORARY); 1234 } else { 1235 o->i_list = exp_new_i_complex(interp,Tcl_GetString(chanName), 1236 EXP_TEMPORARY,inter_updateproc); 1237 if (!o->i_list) { 1238 goto error; 1239 } 1240 } 1241 o->next = 0; /* no one else */ 1242 o->action_eof = &action_eof; 1243 input_user->output = o; 1244 } 1245 1246 if (!input_default->output) { 1247 struct output *o = new(struct output); 1248 o->i_list = exp_new_i_simple(expStdinoutGet(),EXP_TEMPORARY);/* stdout by default */ 1249 o->next = 0; /* no one else */ 1250 o->action_eof = &action_eof; 1251 input_default->output = o; 1252 } 1253 1254 /* if user has given "-u" flag, substitute process for user */ 1255 /* in first two -inputs */ 1256 if (replace_user_by_process) { 1257 /* through away old ones */ 1258 exp_free_i(interp,input_user->i_list, inter_updateproc); 1259 exp_free_i(interp,input_default->output->i_list,inter_updateproc); 1260 1261 /* replace with arg to -u */ 1262 input_user->i_list = exp_new_i_complex(interp, 1263 Tcl_GetString(replace_user_by_process), 1264 EXP_TEMPORARY,inter_updateproc); 1265 if (!input_user->i_list) 1266 goto error; 1267 input_default->output->i_list = exp_new_i_complex(interp, 1268 Tcl_GetString(replace_user_by_process), 1269 EXP_TEMPORARY,inter_updateproc); 1270 if (!input_default->output->i_list) 1271 goto error; 1272 } 1273 1274 /* 1275 * now fix up for default spawn id 1276 */ 1277 1278 /* user could have replaced it with an indirect, so force update */ 1279 if (input_default->i_list->direct == EXP_INDIRECT) { 1280 exp_i_update(interp,input_default->i_list); 1281 } 1282 1283 if (input_default->i_list->state_list 1284 && (input_default->i_list->state_list->esPtr == EXP_SPAWN_ID_BAD)) { 1285 if (!chanName) { 1286 if (!(esPtr = expStateCurrent(interp,1,1,0))) { 1287 goto error; 1288 } 1289 input_default->i_list->state_list->esPtr = esPtr; 1290 } else { 1291 /* discard old one and install new one */ 1292 exp_free_i(interp,input_default->i_list,inter_updateproc); 1293 input_default->i_list = exp_new_i_complex(interp,Tcl_GetString(chanName), 1294 EXP_TEMPORARY,inter_updateproc); 1295 if (!input_default->i_list) 1296 goto error; 1297 } 1298 } 1299 1300 /* 1301 * check for user attempting to interact with self 1302 * they're almost certainly just fooling around 1303 */ 1304 1305 /* user could have replaced it with an indirect, so force update */ 1306 if (input_user->i_list->direct == EXP_INDIRECT) { 1307 exp_i_update(interp,input_user->i_list); 1308 } 1309 1310 if (input_user->i_list->state_list && input_default->i_list->state_list 1311 && (input_user->i_list->state_list->esPtr == input_default->i_list->state_list->esPtr)) { 1312 exp_error(interp,"cannot interact with self - set spawn_id to a spawned process"); 1313 goto error; 1314 } 1315 1316 esPtrs = 0; 1317 1318 /* 1319 * all data structures are sufficiently set up that we can now 1320 * "finish()" to terminate this procedure 1321 */ 1322 1323 status = update_interact_fds(interp,&input_count,&esPtrToInput,&esPtrs,input_base,1,&configure_count,&real_tty); 1324 if (status == TCL_ERROR) finish(TCL_ERROR); 1325 1326 if (real_tty) { 1327 tty_changed = exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo); 1328 } 1329 1330 for (inp = input_base,i=0;inp;inp=inp->next,i++) { 1331 /* start timers */ 1332 inp->timeout_remaining = inp->timeout_nominal; 1333 } 1334 1335 key = expect_key++; 1336 1337 /* declare ourselves "in sync" with external view of close/indirect */ 1338 configure_count = exp_configure_count; 1339 1340#ifndef SIMPLE_EVENT 1341 /* loop waiting (in event handler) for input */ 1342 for (;;) { 1343 int te; /* result of Tcl_Eval */ 1344 int rc; /* return code from ready. This is further refined by matcher. */ 1345 int cc; /* # of chars from read() */ 1346 struct action *action = 0; 1347 time_t previous_time; 1348 time_t current_time; 1349 int matchLen; /* # of chars matched */ 1350 int skip; /* # of chars not involved in match */ 1351 int print; /* # of chars to print */ 1352 int oldprinted; /* old version of u->printed */ 1353 int change; /* if action requires cooked mode */ 1354 int attempt_match = TRUE; 1355 struct input *soonest_input; 1356 int timeout; /* current as opposed to default_timeout */ 1357 Tcl_Time temp_time; 1358 1359 /* calculate how long to wait */ 1360 /* by finding shortest remaining timeout */ 1361 if (timeout_simple) { 1362 timeout = default_timeout; 1363 } else { 1364 timeout = arbitrary_timeout; 1365 1366 for (inp=input_base;inp;inp=inp->next) { 1367 if ((inp->timeout_remaining != EXP_TIME_INFINITY) && 1368 (inp->timeout_remaining <= timeout)) { 1369 soonest_input = inp; 1370 timeout = inp->timeout_remaining; 1371 } 1372 } 1373 1374 Tcl_GetTime (&temp_time); 1375 previous_time = temp_time.sec; 1376 /* timestamp here rather than simply saving old */ 1377 /* current time (after ready()) to account for */ 1378 /* possibility of slow actions */ 1379 1380 /* timeout can actually be EXP_TIME_INFINITY here if user */ 1381 /* explicitly supplied it in a few cases (or */ 1382 /* the count-down code is broken) */ 1383 } 1384 1385 /* update the world, if necessary */ 1386 if (configure_count != exp_configure_count) { 1387 status = update_interact_fds(interp,&input_count, 1388 &esPtrToInput,&esPtrs,input_base,1, 1389 &configure_count,&real_tty); 1390 if (status) finish(status); 1391 } 1392 1393 rc = exp_get_next_event(interp,esPtrs,input_count,&u,timeout,key); 1394 if (rc == EXP_TCLERROR) 1395 goto error; 1396 if (rc == EXP_RECONFIGURE) continue; 1397 if (rc == EXP_TIMEOUT) { 1398 if (timeout_simple) { 1399 action = &action_timeout; 1400 goto got_action; 1401 } else { 1402 action = soonest_input->action_timeout; 1403 /* arbitrarily pick first fd out of list */ 1404 u = soonest_input->i_list->state_list->esPtr; 1405 } 1406 } 1407 if (!timeout_simple) { 1408 int time_diff; 1409 1410 Tcl_GetTime (&temp_time); 1411 current_time = temp_time.sec; 1412 time_diff = current_time - previous_time; 1413 1414 /* update all timers */ 1415 for (inp=input_base;inp;inp=inp->next) { 1416 if (inp->timeout_remaining != EXP_TIME_INFINITY) { 1417 inp->timeout_remaining -= time_diff; 1418 if (inp->timeout_remaining < 0) 1419 inp->timeout_remaining = 0; 1420 } 1421 } 1422 } 1423 1424 /* at this point, we have some kind of event which can be */ 1425 /* immediately processed - i.e. something that doesn't block */ 1426 1427 /* figure out who we are */ 1428 inp = expStateToInput(esPtrToInput,u); 1429 1430 /* reset timer */ 1431 inp->timeout_remaining = inp->timeout_nominal; 1432 1433 switch (rc) { 1434 case EXP_DATA_NEW: 1435 cc = intRead(interp,u,1,0,key); 1436 if (cc > 0) break; 1437 1438 rc = EXP_EOF; 1439 /* 1440 * FALLTHRU 1441 * 1442 * Most systems have read() return 0, allowing 1443 * control to fall thru and into this code. On some 1444 * systems (currently HP and new SGI), read() does 1445 * see eof, and it must be detected earlier. Then 1446 * control jumps directly to this EXP_EOF label. 1447 */ 1448 case EXP_EOF: 1449 action = inp->action_eof; 1450 attempt_match = FALSE; 1451 skip = expSizeGet(u); 1452 expDiagLog("interact: received eof from spawn_id %s\r\n",u->name); 1453 /* actual close is done later so that we have a */ 1454 /* chance to flush out any remaining characters */ 1455 need_to_close_master = TRUE; 1456 break; 1457 case EXP_DATA_OLD: 1458 cc = 0; 1459 break; 1460 case EXP_TIMEOUT: 1461 action = inp->action_timeout; 1462 attempt_match = FALSE; 1463 skip = expSizeGet(u); 1464 break; 1465 } 1466 1467 km = 0; 1468 1469 if (attempt_match) { 1470 rc = intMatch(u,inp->keymap,&km,&matchLen,&skip,&reInfo); 1471 if ((rc == EXP_MATCH) && km && km->re) { 1472 intRegExpMatchProcess(interp,u,km,&reInfo,skip); 1473 } 1474 } else { 1475 attempt_match = TRUE; 1476 } 1477 1478 /* 1479 * dispose of chars that should be skipped 1480 * i.e., chars that cannot possibly be part of a match. 1481 */ 1482 if (km && km->writethru) { 1483 print = skip + matchLen; 1484 } else print = skip; 1485 1486 if (km && km->echo) { 1487 intEcho(u,skip,matchLen); 1488 } 1489 oldprinted = u->printed; 1490 1491 /* 1492 * If expect has left characters in buffer, it has 1493 * already echoed them to the screen, thus we must 1494 * prevent them being rewritten. Unfortunately this 1495 * gives the possibility of matching chars that have 1496 * already been output, but we do so since the user 1497 * could have avoided it by flushing the output 1498 * buffers directly. 1499 */ 1500 if (print > u->printed) { /* usual case */ 1501 for (outp = inp->output;outp;outp=outp->next) { 1502 struct exp_state_list *fdp; 1503 for (fdp = outp->i_list->state_list;fdp;fdp=fdp->next) { 1504 /* send to channel (and log if chan is stdout or devtty) */ 1505 /* 1506 * Following should eventually be rewritten to ...WriteCharsAnd... 1507 */ 1508 int wc = expWriteBytesAndLogIfTtyU(fdp->esPtr, 1509 u->input.buffer + u->printed, 1510 print - u->printed); 1511 if (wc < 0) { 1512 expDiagLog("interact: write on spawn id %s failed (%s)\r\n",fdp->esPtr->name,Tcl_PosixError(interp)); 1513 action = outp->action_eof; 1514 change = (action && action->tty_reset); 1515 1516 if (change && tty_changed) 1517 exp_tty_set(interp,&tty_old,was_raw,was_echo); 1518 te = inter_eval(interp,action,u); 1519 1520 if (change && real_tty) tty_changed = 1521 exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo); 1522 switch (te) { 1523 case TCL_BREAK: 1524 case TCL_CONTINUE: 1525 finish(te); 1526 case EXP_TCL_RETURN: 1527 finish(TCL_RETURN); 1528 case TCL_RETURN: 1529 finish(TCL_OK); 1530 case TCL_OK: 1531 /* god knows what the user might */ 1532 /* have done to us in the way of */ 1533 /* closed fds, so .... */ 1534 action = 0; /* reset action */ 1535 continue; 1536 default: 1537 finish(te); 1538 } 1539 } 1540 } 1541 } 1542 u->printed = print; 1543 } 1544 1545 /* u->printed is now accurate with respect to the buffer */ 1546 /* However, we're about to shift the old data out of the */ 1547 /* buffer. Thus size, printed, and echoed must be */ 1548 /* updated */ 1549 1550 /* first update size based on skip information */ 1551 /* then set skip to the total amount skipped */ 1552 1553 size = expSizeGet(u); 1554 if (rc == EXP_MATCH) { 1555 action = &km->action; 1556 1557 skip += matchLen; 1558 size -= skip; 1559 if (size) { 1560 ustring = u->input.buffer; 1561 memmove(ustring, ustring + skip, size * sizeof(Tcl_UniChar)); 1562 } 1563 } else { 1564 ustring = u->input.buffer; 1565 if (skip) { 1566 size -= skip; 1567 memcpy(ustring, ustring + skip, size * sizeof(Tcl_UniChar)); 1568 } 1569 } 1570 u->input.use = size; 1571 1572 /* now update printed based on total amount skipped */ 1573 1574 u->printed -= skip; 1575 /* if more skipped than printed (i.e., keymap encountered) */ 1576 /* for printed positive */ 1577 if (u->printed < 0) u->printed = 0; 1578 1579 /* if we are in the middle of a match, force the next event */ 1580 /* to wait for more data to arrive */ 1581 u->force_read = (rc == EXP_CANMATCH); 1582 1583 /* finally reset echoed if necessary */ 1584 if (rc != EXP_CANMATCH) { 1585 if (skip >= oldprinted + u->echoed) u->echoed = 0; 1586 } 1587 1588 if (rc == EXP_EOF) { 1589 if (u->close_on_eof) { 1590 exp_close(interp,u); 1591 } 1592 need_to_close_master = FALSE; 1593 } 1594 1595 if (action) { 1596got_action: 1597 change = (action && action->tty_reset); 1598 if (change && tty_changed) 1599 exp_tty_set(interp,&tty_old,was_raw,was_echo); 1600 1601 te = inter_eval(interp,action,u); 1602 1603 if (change && real_tty) tty_changed = 1604 exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo); 1605 switch (te) { 1606 case TCL_BREAK: 1607 case TCL_CONTINUE: 1608 finish(te); 1609 case EXP_TCL_RETURN: 1610 finish(TCL_RETURN); 1611 case TCL_RETURN: 1612 finish(TCL_OK); 1613 case TCL_OK: 1614 /* god knows what the user might */ 1615 /* have done to us in the way of */ 1616 /* closed fds, so .... */ 1617 action = 0; /* reset action */ 1618 continue; 1619 default: 1620 finish(te); 1621 } 1622 } 1623 } 1624 1625#else /* SIMPLE_EVENT */ 1626/* deferred_interrupt = FALSE;*/ 1627{ 1628 int te; /* result of Tcl_Eval */ 1629 ExpState *u; /*master*/ 1630 int rc; /* return code from ready. This is further */ 1631 /* refined by matcher. */ 1632 int cc; /* chars count from read() */ 1633 struct action *action = 0; 1634 time_t previous_time; 1635 time_t current_time; 1636 int matchLen, skip; 1637 int change; /* if action requires cooked mode */ 1638 int attempt_match = TRUE; 1639 struct input *soonest_input; 1640 int print; /* # of chars to print */ 1641 int oldprinted; /* old version of u->printed */ 1642 1643 int timeout; /* current as opposed to default_timeout */ 1644 1645 if (-1 == (pid = fork())) { 1646 exp_error(interp,"fork: %s",Tcl_PosixError(interp)); 1647 finish(TCL_ERROR); 1648 } 1649 if (pid == 0) { 1650 /* 1651 * This is a new child process. 1652 * It exists only for this interact command and will go away when 1653 * the interact returns. 1654 * 1655 * The purpose of this child process is to read output from the 1656 * spawned process and send it to the user tty. 1657 * (See diagram above.) 1658 */ 1659 1660 exp_close(interp,expStdinoutGet()); 1661 1662 u = esPtrs[1]; /* get 2nd ExpState */ 1663 input_count = 1; 1664 1665 while (1) { 1666 1667 /* calculate how long to wait */ 1668 /* by finding shortest remaining timeout */ 1669 if (timeout_simple) { 1670 timeout = default_timeout; 1671 } else { 1672 timeout = arbitrary_timeout; 1673 1674 for (inp=input_base;inp;inp=inp->next) { 1675 if ((inp->timeout_remaining != EXP_TIME_INFINITY) && 1676 (inp->timeout_remaining < timeout)) 1677 soonest_input = inp; 1678 timeout = inp->timeout_remaining; 1679 } 1680 1681 Tcl_GetTime (&temp_time); 1682 previous_time = temp_time.sec; 1683 /* timestamp here rather than simply saving old */ 1684 /* current time (after ready()) to account for */ 1685 /* possibility of slow actions */ 1686 1687 /* timeout can actually be EXP_TIME_INFINITY here if user */ 1688 /* explicitly supplied it in a few cases (or */ 1689 /* the count-down code is broken) */ 1690 } 1691 1692 /* +1 so we can look at the "other" file descriptor */ 1693 rc = exp_get_next_event(interp,esPtrs+1,input_count,&u,timeout,key); 1694 if (!timeout_simple) { 1695 int time_diff; 1696 1697 Tcl_GetTime (&temp_time); 1698 current_time = temp_time.sec; 1699 time_diff = current_time - previous_time; 1700 1701 /* update all timers */ 1702 for (inp=input_base;inp;inp=inp->next) { 1703 if (inp->timeout_remaining != EXP_TIME_INFINITY) { 1704 inp->timeout_remaining -= time_diff; 1705 if (inp->timeout_remaining < 0) 1706 inp->timeout_remaining = 0; 1707 } 1708 } 1709 } 1710 1711 /* at this point, we have some kind of event which can be */ 1712 /* immediately processed - i.e. something that doesn't block */ 1713 1714 /* figure out who we are */ 1715 inp = expStateToInput(esPtrToInput,u); 1716 1717 switch (rc) { 1718 case EXP_DATA_NEW: 1719 cc = intRead(interp,u,0,0,key); 1720 if (cc > 0) break; 1721 /* 1722 * FALLTHRU 1723 * 1724 * Most systems have read() return 0, allowing 1725 * control to fall thru and into this code. On some 1726 * systems (currently HP and new SGI), read() does 1727 * see eof, and it must be detected earlier. Then 1728 * control jumps directly to this EXP_EOF label. 1729 */ 1730 case EXP_EOF: 1731 action = inp->action_eof; 1732 attempt_match = FALSE; 1733 skip = expSizeGet(u); 1734 rc = EXP_EOF; 1735 expDiagLog("interact: child received eof from spawn_id %s\r\n",u->name); 1736 exp_close(interp,u); 1737 break; 1738 case EXP_DATA_OLD: 1739 cc = 0; 1740 break; 1741 } 1742 1743 km = 0; 1744 1745 if (attempt_match) { 1746 rc = intMatch(u,inp->keymap,&km,&matchLen,&skip,&reInfo); 1747 if ((rc == EXP_MATCH) && km && km->re) { 1748 intRegExpMatchProcess(interp,u,km,&reInfo,skip); 1749 } 1750 } else { 1751 attempt_match = TRUE; 1752 } 1753 1754 /* dispose of chars that should be skipped */ 1755 1756 /* skip is chars not involved in match */ 1757 /* print is with chars involved in match */ 1758 1759 if (km && km->writethru) { 1760 print = skip + matchLen; 1761 } else print = skip; 1762 1763 if (km && km->echo) { 1764 intEcho(u,skip,matchLen); 1765 } 1766 oldprinted = u->printed; 1767 1768 /* If expect has left characters in buffer, it has */ 1769 /* already echoed them to the screen, thus we must */ 1770 /* prevent them being rewritten. Unfortunately this */ 1771 /* gives the possibility of matching chars that have */ 1772 /* already been output, but we do so since the user */ 1773 /* could have avoided it by flushing the output */ 1774 /* buffers directly. */ 1775 if (print > u->printed) { /* usual case */ 1776 for (outp = inp->output;outp;outp=outp->next) { 1777 struct exp_state_list *fdp; 1778 for (fdp = outp->i_list->state_list;fdp;fdp=fdp->next) { 1779 /* send to channel (and log if chan is stdout or devtty) */ 1780 int wc = expWriteBytesAndLogIfTtyU(fdp->esPtr, 1781 u->input.buffer + u->printed, 1782 print - u->printed); 1783 if (wc < 0) { 1784 expDiagLog("interact: write on spawn id %s failed (%s)\r\n",fdp->esPtr->name,Tcl_PosixError(interp)); 1785 action = outp->action_eof; 1786 1787 te = inter_eval(interp,action,u); 1788 1789 switch (te) { 1790 case TCL_BREAK: 1791 case TCL_CONTINUE: 1792 finish(te); 1793 case EXP_TCL_RETURN: 1794 finish(TCL_RETURN); 1795 case TCL_RETURN: 1796 finish(TCL_OK); 1797 case TCL_OK: 1798 /* god knows what the user might */ 1799 /* have done to us in the way of */ 1800 /* closed fds, so .... */ 1801 action = 0; /* reset action */ 1802 continue; 1803 default: 1804 finish(te); 1805 } 1806 } 1807 } 1808 } 1809 u->printed = print; 1810 } 1811 1812 /* u->printed is now accurate with respect to the buffer */ 1813 /* However, we're about to shift the old data out of the */ 1814 /* buffer. Thus size, printed, and echoed must be */ 1815 /* updated */ 1816 1817 /* first update size based on skip information */ 1818 /* then set skip to the total amount skipped */ 1819 1820 size = expSizeGet(u); 1821 if (rc == EXP_MATCH) { 1822 action = &km->action; 1823 1824 skip += matchLen; 1825 size -= skip; 1826 if (size) { 1827 memcpy(u->buffer, u->buffer + skip, size); 1828 } 1829 } else { 1830 if (skip) { 1831 size -= skip; 1832 memcpy(u->buffer, u->buffer + skip, size); 1833 } 1834 } 1835 Tcl_SetObjLength(size); 1836 1837 /* now update printed based on total amount skipped */ 1838 1839 u->printed -= skip; 1840 /* if more skipped than printed (i.e., keymap encountered) */ 1841 /* for printed positive */ 1842 if (u->printed < 0) u->printed = 0; 1843 1844 /* if we are in the middle of a match, force the next event */ 1845 /* to wait for more data to arrive */ 1846 u->force_read = (rc == EXP_CANMATCH); 1847 1848 /* finally reset echoed if necessary */ 1849 if (rc != EXP_CANMATCH) { 1850 if (skip >= oldprinted + u->echoed) u->echoed = 0; 1851 } 1852 1853 if (action) { 1854 te = inter_eval(interp,action,u); 1855 switch (te) { 1856 case TCL_BREAK: 1857 case TCL_CONTINUE: 1858 finish(te); 1859 case EXP_TCL_RETURN: 1860 finish(TCL_RETURN); 1861 case TCL_RETURN: 1862 finish(TCL_OK); 1863 case TCL_OK: 1864 /* god knows what the user might */ 1865 /* have done to us in the way of */ 1866 /* closed fds, so .... */ 1867 action = 0; /* reset action */ 1868 continue; 1869 default: 1870 finish(te); 1871 } 1872 } 1873 } 1874 } else { 1875 /* 1876 * This is the original Expect process. 1877 * 1878 * It now loops, reading keystrokes from the user tty 1879 * and sending them to the spawned process. 1880 * (See diagram above.) 1881 */ 1882 1883#include <signal.h> 1884 1885#if defined(SIGCLD) && !defined(SIGCHLD) 1886#define SIGCHLD SIGCLD 1887#endif 1888 expDiagLog("fork = %d\r\n",pid); 1889 signal(SIGCHLD,sigchld_handler); 1890/* restart:*/ 1891/* tty_changed = exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);*/ 1892 1893 u = esPtrs[0]; /* get 1st ExpState */ 1894 input_count = 1; 1895 1896 while (1) { 1897 /* calculate how long to wait */ 1898 /* by finding shortest remaining timeout */ 1899 if (timeout_simple) { 1900 timeout = default_timeout; 1901 } else { 1902 timeout = arbitrary_timeout; 1903 1904 for (inp=input_base;inp;inp=inp->next) { 1905 if ((inp->timeout_remaining != EXP_TIME_INFINITY) && 1906 (inp->timeout_remaining < timeout)) 1907 soonest_input = inp; 1908 timeout = inp->timeout_remaining; 1909 } 1910 1911 Tcl_GetTime (&temp_time); 1912 previous_time = temp_time.sec; 1913 /* timestamp here rather than simply saving old */ 1914 /* current time (after ready()) to account for */ 1915 /* possibility of slow actions */ 1916 1917 /* timeout can actually be EXP_TIME_INFINITY here if user */ 1918 /* explicitly supplied it in a few cases (or */ 1919 /* the count-down code is broken) */ 1920 } 1921 1922 rc = exp_get_next_event(interp,esPtrs,input_count,&u,timeout,key); 1923 if (!timeout_simple) { 1924 int time_diff; 1925 1926 Tcl_GetTime (&temp_time); 1927 current_time = temp_time.sec; 1928 time_diff = current_time - previous_time; 1929 1930 /* update all timers */ 1931 for (inp=input_base;inp;inp=inp->next) { 1932 if (inp->timeout_remaining != EXP_TIME_INFINITY) { 1933 inp->timeout_remaining -= time_diff; 1934 if (inp->timeout_remaining < 0) 1935 inp->timeout_remaining = 0; 1936 } 1937 } 1938 } 1939 1940 /* at this point, we have some kind of event which can be */ 1941 /* immediately processed - i.e. something that doesn't block */ 1942 1943 /* figure out who we are */ 1944 inp = expStateToInput(esPtrToInput,u); 1945 1946 switch (rc) { 1947 case EXP_DATA_NEW: 1948 cc = intRead(interp,u,0,1,key); 1949 if (cc > 0) { 1950 break; 1951 } else if (cc == EXP_CHILD_EOF) { 1952 /* user could potentially have two outputs in which */ 1953 /* case we might be looking at the wrong one, but */ 1954 /* the likelihood of this is nil */ 1955 action = inp->output->action_eof; 1956 attempt_match = FALSE; 1957 skip = expSizeGet(u); 1958 rc = EXP_EOF; 1959 expDiagLogU("interact: process died/eof\r\n"); 1960 clean_up_after_child(interp,esPtrs[1]); 1961 break; 1962 } 1963 /* 1964 * FALLTHRU 1965 * 1966 * Most systems have read() return 0, allowing 1967 * control to fall thru and into this code. On some 1968 * systems (currently HP and new SGI), read() does 1969 * see eof, and it must be detected earlier. Then 1970 * control jumps directly to this EXP_EOF label. 1971 */ 1972 case EXP_EOF: 1973 action = inp->action_eof; 1974 attempt_match = FALSE; 1975 skip = expSizeGet(u); 1976 rc = EXP_EOF; 1977 expDiagLogU("user sent EOF or disappeared\n\n"); 1978 break; 1979 case EXP_DATA_OLD: 1980 cc = 0; 1981 break; 1982 } 1983 1984 km = 0; 1985 1986 if (attempt_match) { 1987 rc = intMatch(u,inp->keymap,&km,&matchLen,&skip,&reInfo); 1988 if ((rc == EXP_MATCH) && km && km->re) { 1989 intRegExpMatchProcess(interp,u,km,&reInfo,skip); 1990 } 1991 } else { 1992 attempt_match = TRUE; 1993 } 1994 1995 /* dispose of chars that should be skipped */ 1996 1997 /* skip is chars not involved in match */ 1998 /* print is with chars involved in match */ 1999 2000 if (km && km->writethru) { 2001 print = skip + matchLen; 2002 } else print = skip; 2003 2004 if (km && km->echo) { 2005 intEcho(u,skip,matchLen); 2006 } 2007 oldprinted = u->printed; 2008 2009 /* If expect has left characters in buffer, it has */ 2010 /* already echoed them to the screen, thus we must */ 2011 /* prevent them being rewritten. Unfortunately this */ 2012 /* gives the possibility of matching chars that have */ 2013 /* already been output, but we do so since the user */ 2014 /* could have avoided it by flushing the output */ 2015 /* buffers directly. */ 2016 if (print > u->printed) { /* usual case */ 2017 for (outp = inp->output;outp;outp=outp->next) { 2018 struct exp_state_list *fdp; 2019 for (fdp = outp->i_list->state_list;fdp;fdp=fdp->next) { 2020 /* send to channel (and log if chan is stdout or devtty) */ 2021 int wc = expWriteBytesAndLogIfTtyU(fdp->esPtr, 2022 u->input.buffer + u->printed, 2023 print - u->printed); 2024 if (wc < 0) { 2025 expDiagLog("interact: write on spawn id %s failed (%s)\r\n",fdp->esPtr->name,Tcl_PosixError(interp)); 2026 clean_up_after_child(interp,fdp->esPtr); 2027 action = outp->action_eof; 2028 change = (action && action->tty_reset); 2029 if (change && tty_changed) 2030 exp_tty_set(interp,&tty_old,was_raw,was_echo); 2031 te = inter_eval(interp,action,u); 2032 2033 if (change && real_tty) tty_changed = 2034 exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo); 2035 switch (te) { 2036 case TCL_BREAK: 2037 case TCL_CONTINUE: 2038 finish(te); 2039 case EXP_TCL_RETURN: 2040 finish(TCL_RETURN); 2041 case TCL_RETURN: 2042 finish(TCL_OK); 2043 case TCL_OK: 2044 /* god knows what the user might */ 2045 /* have done to us in the way of */ 2046 /* closed fds, so .... */ 2047 action = 0; /* reset action */ 2048 continue; 2049 default: 2050 finish(te); 2051 } 2052 } 2053 } 2054 } 2055 u->printed = print; 2056 } 2057 2058 /* u->printed is now accurate with respect to the buffer */ 2059 /* However, we're about to shift the old data out of the */ 2060 /* buffer. Thus size, printed, and echoed must be */ 2061 /* updated */ 2062 2063 /* first update size based on skip information */ 2064 /* then set skip to the total amount skipped */ 2065 2066 size = expSizeGet(u); 2067 if (rc == EXP_MATCH) { 2068 action = &km->action; 2069 2070 skip += matchLen; 2071 size -= skip; 2072 if (size) { 2073 memcpy(u->buffer, u->buffer + skip, size); 2074 } 2075 } else { 2076 if (skip) { 2077 size -= skip; 2078 memcpy(u->buffer, u->buffer + skip, size); 2079 } 2080 } 2081 Tcl_SetObjLength(size); 2082 2083 /* now update printed based on total amount skipped */ 2084 2085 u->printed -= skip; 2086 /* if more skipped than printed (i.e., keymap encountered) */ 2087 /* for printed positive */ 2088 if (u->printed < 0) u->printed = 0; 2089 2090 /* if we are in the middle of a match, force the next event */ 2091 /* to wait for more data to arrive */ 2092 u->force_read = (rc == EXP_CANMATCH); 2093 2094 /* finally reset echoed if necessary */ 2095 if (rc != EXP_CANMATCH) { 2096 if (skip >= oldprinted + u->echoed) u->echoed = 0; 2097 } 2098 2099 if (action) { 2100 change = (action && action->tty_reset); 2101 if (change && tty_changed) 2102 exp_tty_set(interp,&tty_old,was_raw,was_echo); 2103 2104 te = inter_eval(interp,action,u); 2105 2106 if (change && real_tty) tty_changed = 2107 exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo); 2108 switch (te) { 2109 case TCL_BREAK: 2110 case TCL_CONTINUE: 2111 finish(te); 2112 case EXP_TCL_RETURN: 2113 finish(TCL_RETURN); 2114 case TCL_RETURN: 2115 finish(TCL_OK); 2116 case TCL_OK: 2117 /* god knows what the user might */ 2118 /* have done to us in the way of */ 2119 /* closed fds, so .... */ 2120 action = 0; /* reset action */ 2121 continue; 2122 default: 2123 finish(te); 2124 } 2125 } 2126 } 2127 } 2128} 2129#endif /* SIMPLE_EVENT */ 2130 2131 done: 2132#ifdef SIMPLE_EVENT 2133 /* force child to exit upon eof from master */ 2134 if (pid == 0) { 2135 exit(SPAWNED_PROCESS_DIED); 2136 } 2137#endif /* SIMPLE_EVENT */ 2138 2139 if (need_to_close_master && u->close_on_eof) exp_close(interp,u); 2140 2141 if (tty_changed) exp_tty_set(interp,&tty_old,was_raw,was_echo); 2142 if (esPtrs) ckfree((char *)esPtrs); 2143 if (esPtrToInput) Tcl_DeleteHashTable(esPtrToInput); 2144 free_input(interp,input_base); 2145 free_action(action_base); 2146 2147 if (new_cmd) { Tcl_DecrRefCount (new_cmd); } 2148 return(status); 2149 2150 error: 2151 if (new_cmd) { Tcl_DecrRefCount (new_cmd); } 2152 return TCL_ERROR; 2153} 2154 2155/* version of Tcl_Eval for interact */ 2156static int 2157inter_eval( 2158 Tcl_Interp *interp, 2159 struct action *action, 2160 ExpState *esPtr) 2161{ 2162 int status; 2163 2164 if (action->iwrite) { 2165 out("spawn_id",esPtr->name); 2166 } 2167 2168 if (action->statement) { 2169 status = Tcl_EvalObjEx(interp,action->statement,0); 2170 } else { 2171 expStdoutLogU("\r\n",1); 2172 status = exp_interpreter(interp,(Tcl_Obj *)0); 2173 } 2174 2175 return status; 2176} 2177 2178static void 2179free_keymap(struct keymap *km) 2180{ 2181 if (km == 0) return; 2182 free_keymap(km->next); 2183 2184 ckfree((char *)km); 2185} 2186 2187static void 2188free_action(struct action *a) 2189{ 2190 struct action *next; 2191 2192 while (a) { 2193 next = a->next; 2194 ckfree((char *)a); 2195 a = next; 2196 } 2197} 2198 2199static void 2200free_input( 2201 Tcl_Interp *interp, 2202 struct input *i) 2203{ 2204 if (i == 0) return; 2205 free_input(interp,i->next); 2206 2207 exp_free_i(interp,i->i_list,inter_updateproc); 2208 free_output(interp,i->output); 2209 free_keymap(i->keymap); 2210 ckfree((char *)i); 2211} 2212 2213static struct action * 2214new_action(struct action **base) 2215{ 2216 struct action *o = new(struct action); 2217 2218 /* stick new action into beginning of list of all actions */ 2219 o->next = *base; 2220 *base = o; 2221 2222 return o; 2223} 2224 2225static void 2226free_output( 2227 Tcl_Interp *interp, 2228 struct output *o) 2229{ 2230 if (o == 0) return; 2231 free_output(interp,o->next); 2232 exp_free_i(interp,o->i_list,inter_updateproc); 2233 2234 ckfree((char *)o); 2235} 2236 2237 2238static struct exp_cmd_data cmd_data[] = { 2239{"interact", Exp_InteractObjCmd, 0, 0, 0}, 2240{0}}; 2241 2242void 2243exp_init_interact_cmds(Tcl_Interp *interp) 2244{ 2245 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 2246 2247 exp_create_commands(interp,cmd_data); 2248 2249 tsdPtr->cmdObjReturn = Tcl_NewStringObj("return",6); 2250 Tcl_IncrRefCount(tsdPtr->cmdObjReturn); 2251#if 0 2252 tsdPtr->cmdObjInterpreter = Tcl_NewStringObj("interpreter",11); 2253 Tcl_IncrRefCount(tsdPtr->cmdObjInterpreter); 2254#endif 2255} 2256 2257/* 2258 * Local Variables: 2259 * mode: c 2260 * c-basic-offset: 4 2261 * fill-column: 78 2262 * End: 2263 */ 2264