Imported Upstream version 5.45.4
[platform/upstream/expect.git] / exp_chan.c
1 /* 
2  * exp_chan.c
3  *
4  *      Channel driver for Expect channels.
5  *      Based on UNIX File channel from TclUnixChan.c
6  *
7  */
8
9 #include <sys/types.h>
10 #include <stdio.h>
11 #include <signal.h>
12 #include <errno.h>
13 #include <ctype.h>      /* for isspace */
14 #include <time.h>       /* for time(3) */
15
16 #include "expect_cf.h"
17
18 #ifdef HAVE_SYS_WAIT_H
19 #include <sys/wait.h>
20 #endif
21
22 #ifdef HAVE_UNISTD_H
23 # include <unistd.h>
24 #endif
25
26 #include <errno.h>
27
28 #include        "tclInt.h"      /* Internal definitions for Tcl. */
29
30 #include "tcl.h"
31
32 #include "string.h"
33
34 #include "exp_rename.h"
35 #include "exp_prog.h"
36 #include "exp_command.h"
37 #include "exp_log.h"
38 #include "tcldbg.h" /* Dbg_StdinMode */
39
40 extern int              expSetBlockModeProc _ANSI_ARGS_((int fd, int mode));
41 static int              ExpBlockModeProc _ANSI_ARGS_((ClientData instanceData,
42                             int mode));
43 static int              ExpCloseProc _ANSI_ARGS_((ClientData instanceData,
44                             Tcl_Interp *interp));
45 static int              ExpInputProc _ANSI_ARGS_((ClientData instanceData,
46                             char *buf, int toRead, int *errorCode));
47 static int              ExpOutputProc _ANSI_ARGS_((
48                             ClientData instanceData, char *buf, int toWrite,
49                             int *errorCode));
50 static void             ExpWatchProc _ANSI_ARGS_((ClientData instanceData,
51                             int mask));
52 static int              ExpGetHandleProc _ANSI_ARGS_((ClientData instanceData,
53                             int direction, ClientData *handlePtr));
54
55 /*
56  * This structure describes the channel type structure for Expect-based IO:
57  */
58
59 Tcl_ChannelType expChannelType = {
60     "exp",                              /* Type name. */
61     ExpBlockModeProc,                   /* Set blocking/nonblocking mode.*/
62     ExpCloseProc,                       /* Close proc. */
63     ExpInputProc,                       /* Input proc. */
64     ExpOutputProc,                      /* Output proc. */
65     NULL,                               /* Seek proc. */
66     NULL,                               /* Set option proc. */
67     NULL,                               /* Get option proc. */
68     ExpWatchProc,                       /* Initialize notifier. */
69     ExpGetHandleProc,                   /* Get OS handles out of channel. */
70     NULL,                               /* Close2 proc */
71 };
72
73 typedef struct ThreadSpecificData {
74     /*
75      * List of all exp channels currently open.  This is per thread and is
76      * used to match up fd's to channels, which rarely occurs.
77      */
78     
79     ExpState *firstExpPtr;
80     int channelCount;    /* this is process-wide as it is used to
81                              give user some hint as to why a spawn has failed
82                              by looking at process-wide resource usage */
83 } ThreadSpecificData;
84
85 static Tcl_ThreadDataKey dataKey;
86
87 \f/*
88  *----------------------------------------------------------------------
89  *
90  * ExpBlockModeProc --
91  *
92  *      Helper procedure to set blocking and nonblocking modes on a
93  *      file based channel. Invoked by generic IO level code.
94  *
95  * Results:
96  *      0 if successful, errno when failed.
97  *
98  * Side effects:
99  *      Sets the device into blocking or non-blocking mode.
100  *
101  *----------------------------------------------------------------------
102  */
103
104         /* ARGSUSED */
105 static int
106 ExpBlockModeProc(instanceData, mode)
107     ClientData instanceData;            /* Exp state. */
108     int mode;                           /* The mode to set. Can be one of
109                                          * TCL_MODE_BLOCKING or
110                                          * TCL_MODE_NONBLOCKING. */
111 {
112     ExpState *esPtr = (ExpState *) instanceData;
113
114     if (esPtr->fdin == 0) {
115         /* Forward status to debugger. Required for FIONBIO systems,
116          * which are unable to query the fd for its current state.
117          */
118         Dbg_StdinMode (mode);
119     }
120
121     /* [Expect SF Bug 1108551] (July 7 2005)
122      * Exclude manipulation of the blocking status for stdin/stderr.
123      *
124      * This is handled by the Tcl core itself and we must absolutely
125      * not pull the rug out from under it. The standard setting to
126      * non-blocking will mess with the core which had them set to
127      * blocking, and makes all its decisions based on that assumption.
128      * Setting to non-blocking can cause hangs and crashes.
129      *
130      * Stdin is ok however, apparently.
131      * (Sep 9 2005) No, it is not.
132      */
133
134     if ((esPtr->fdin == 0) ||
135         (esPtr->fdin == 1) ||
136         (esPtr->fdin == 2)) {
137       return 0;
138     }
139
140     return expSetBlockModeProc (esPtr->fdin, mode);
141 }
142
143 int
144 expSetBlockModeProc(fd, mode)
145     int fd;
146     int mode;                           /* The mode to set. Can be one of
147                                          * TCL_MODE_BLOCKING or
148                                          * TCL_MODE_NONBLOCKING. */
149 {
150     int curStatus;
151     /*printf("ExpBlockModeProc(%d)\n",mode);
152       printf("fdin = %d\n",fd);*/
153
154 #ifndef USE_FIONBIO
155     curStatus = fcntl(fd, F_GETFL);
156     /*printf("curStatus = %d\n",curStatus);*/
157     if (mode == TCL_MODE_BLOCKING) {
158         curStatus &= (~(O_NONBLOCK));
159     } else {
160         curStatus |= O_NONBLOCK;
161     }
162     /*printf("new curStatus %d\n",curStatus);*/
163     if (fcntl(fd, F_SETFL, curStatus) < 0) {
164         return errno;
165     }
166     curStatus = fcntl(fd, F_GETFL);
167 #else /* USE_FIONBIO */
168     if (mode == TCL_MODE_BLOCKING) {
169         curStatus = 0;
170     } else {
171         curStatus = 1;
172     }
173     if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) {
174         return errno;
175     }
176 #endif /* !USE_FIONBIO */
177     return 0;
178 }
179 /*
180  *----------------------------------------------------------------------
181  *
182  * ExpInputProc --
183  *
184  *      This procedure is invoked from the generic IO level to read
185  *      input from an exp-based channel.
186  *
187  * Results:
188  *      The number of bytes read is returned or -1 on error. An output
189  *      argument contains a POSIX error code if an error occurs, or zero.
190  *
191  * Side effects:
192  *      Reads input from the input device of the channel.
193  *
194  *----------------------------------------------------------------------
195  */
196
197 static int
198 ExpInputProc(instanceData, buf, toRead, errorCodePtr)
199     ClientData instanceData;            /* Exp state. */
200     char *buf;                          /* Where to store data read. */
201     int toRead;                         /* How much space is available
202                                          * in the buffer? */
203     int *errorCodePtr;                  /* Where to store error code. */
204 {
205     ExpState *esPtr = (ExpState *) instanceData;
206     int bytesRead;                      /* How many bytes were actually
207                                          * read from the input device? */
208
209     *errorCodePtr = 0;
210     
211     /*
212      * Assume there is always enough input available. This will block
213      * appropriately, and read will unblock as soon as a short read is
214      * possible, if the channel is in blocking mode. If the channel is
215      * nonblocking, the read will never block.
216      */
217
218     bytesRead = read(esPtr->fdin, buf, (size_t) toRead);
219     /*printf("ExpInputProc: read(%d,,) = %d\r\n",esPtr->fdin,bytesRead);*/
220
221     /* Emulate EOF on tty for tcl */
222     if ((bytesRead == -1) && (errno == EIO) && isatty(esPtr->fdin)) {
223         bytesRead = 0;
224     }
225     if (bytesRead > -1) {
226         /* strip parity if requested */
227         if (esPtr->parity == 0) {
228             char *end = buf+bytesRead;
229             for (;buf < end;buf++) {
230                 *buf &= 0x7f;
231             }
232         }
233         return bytesRead;
234     }
235     *errorCodePtr = errno;
236     return -1;
237 }
238 \f
239 /*
240  *----------------------------------------------------------------------
241  *
242  * ExpOutputProc--
243  *
244  *      This procedure is invoked from the generic IO level to write
245  *      output to an exp channel.
246  *
247  * Results:
248  *      The number of bytes written is returned or -1 on error. An
249  *      output argument contains a POSIX error code if an error occurred,
250  *      or zero.
251  *
252  * Side effects:
253  *      Writes output on the output device of the channel.
254  *
255  *----------------------------------------------------------------------
256  */
257
258 static int
259 ExpOutputProc(instanceData, buf, toWrite, errorCodePtr)
260     ClientData instanceData;            /* Exp state. */
261     char *buf;                          /* The data buffer. */
262     int toWrite;                        /* How many bytes to write? */
263     int *errorCodePtr;                  /* Where to store error code. */
264 {
265     ExpState *esPtr = (ExpState *) instanceData;
266     int written = 0;
267
268     *errorCodePtr = 0;
269
270     if (toWrite < 0) Tcl_Panic("ExpOutputProc: called with negative char count");
271     if (toWrite ==0) {
272         return 0;
273     }
274
275     written = write(esPtr->fdout, buf, (size_t) toWrite);
276     if (written == 0) {
277       /* This shouldn't happen but I'm told that it does
278        * nonetheless (at least on SunOS 4.1.3).  Since this is
279        * not a documented return value, the most reasonable
280        * thing is to complain here and retry in the hopes that
281        * it is some transient condition.  */
282       sleep(1);
283       expDiagLogU("write() failed to write anything - will sleep(1) and retry...\n");
284       *errorCodePtr = EAGAIN;
285       return -1;
286     } else if (written < 0) {
287       *errorCodePtr = errno;
288       return -1;
289     }
290     return written;
291 }
292 \f
293 /*
294  *----------------------------------------------------------------------
295  *
296  * ExpCloseProc --
297  *
298  *      This procedure is called from the generic IO level to perform
299  *      channel-type-specific cleanup when an exp-based channel is closed.
300  *
301  * Results:
302  *      0 if successful, errno if failed.
303  *
304  * Side effects:
305  *      Closes the device of the channel.
306  *
307  *----------------------------------------------------------------------
308  */
309
310 /*ARGSUSED*/
311 static int
312 ExpCloseProc(instanceData, interp)
313     ClientData instanceData;    /* Exp state. */
314     Tcl_Interp *interp;         /* For error reporting - unused. */
315 {
316     ExpState *esPtr = (ExpState *) instanceData;
317     ExpState **nextPtrPtr;
318     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
319
320     esPtr->registered = FALSE;
321
322 #if 0
323     /*
324       Really should check that we created one first.  Since we're sharing fds
325       with Tcl, perhaps a filehandler was created with a plain tcl file - we
326       wouldn't want to delete that.  Although if user really close Expect's
327       user_spawn_id, it probably doesn't matter anyway.
328     */
329
330     Tcl_DeleteFileHandler(esPtr->fdin);
331 #endif /*0*/
332
333     Tcl_Free((char*)esPtr->input.buffer);
334     Tcl_DecrRefCount (esPtr->input.newchars);
335
336     /* Actually file descriptor should have been closed earlier. */
337     /* So do nothing here */
338
339     /*
340      * Conceivably, the process may not yet have been waited for.  If this
341      * becomes a requirement, we'll have to revisit this code.  But for now, if
342      * it's just Tcl exiting, the processes will exit on their own soon
343      * anyway.
344      */
345
346     for (nextPtrPtr = &(tsdPtr->firstExpPtr); (*nextPtrPtr) != NULL;
347          nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
348         if ((*nextPtrPtr) == esPtr) {
349             (*nextPtrPtr) = esPtr->nextPtr;
350             break;
351         }
352     }
353     tsdPtr->channelCount--;
354
355     if (esPtr->bg_status == blocked ||
356             esPtr->bg_status == disarm_req_while_blocked) {
357         esPtr->freeWhenBgHandlerUnblocked = 1;
358         /*
359          * If we're in the middle of a bg event handler, then the event
360          * handler will have to take care of freeing esPtr.
361          */
362     } else {
363         expStateFree(esPtr);
364     }
365     return 0;
366 }
367 \f
368 /*
369  *----------------------------------------------------------------------
370  *
371  * ExpWatchProc --
372  *
373  *      Initialize the notifier to watch the fd from this channel.
374  *
375  * Results:
376  *      None.
377  *
378  * Side effects:
379  *      Sets up the notifier so that a future event on the channel will
380  *      be seen by Tcl.
381  *
382  *----------------------------------------------------------------------
383  */
384
385 static void
386 ExpWatchProc(instanceData, mask)
387     ClientData instanceData;            /* The exp state. */
388     int mask;                           /* Events of interest; an OR-ed
389                                          * combination of TCL_READABLE,
390                                          * TCL_WRITABLE and TCL_EXCEPTION. */
391 {
392     ExpState *esPtr = (ExpState *) instanceData;
393
394     /*
395      * Make sure we only register for events that are valid on this exp.
396      * Note that we are passing Tcl_NotifyChannel directly to
397      * Tcl_CreateExpHandler with the channel pointer as the client data.
398      */
399
400     mask &= esPtr->validMask;
401     if (mask) {
402         /*printf("  CreateFileHandler: %d (mask = %d)\r\n",esPtr->fdin,mask);*/
403         Tcl_CreateFileHandler(esPtr->fdin, mask,
404                 (Tcl_FileProc *) Tcl_NotifyChannel,
405                 (ClientData) esPtr->channel);
406     } else {
407         /*printf("  DeleteFileHandler: %d (mask = %d)\r\n",esPtr->fdin,mask);*/
408         Tcl_DeleteFileHandler(esPtr->fdin);
409     }
410 }
411 \f
412 /*
413  *----------------------------------------------------------------------
414  *
415  * ExpGetHandleProc --
416  *
417  *      Called from Tcl_GetChannelHandle to retrieve OS handles from
418  *      an exp-based channel.
419  *
420  * Results:
421  *      Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
422  *      there is no handle for the specified direction. 
423  *
424  * Side effects:
425  *      None.
426  *
427  *----------------------------------------------------------------------
428  */
429
430 static int
431 ExpGetHandleProc(instanceData, direction, handlePtr)
432     ClientData instanceData;    /* The exp state. */
433     int direction;              /* TCL_READABLE or TCL_WRITABLE */
434     ClientData *handlePtr;      /* Where to store the handle.  */
435 {
436     ExpState *esPtr = (ExpState *) instanceData;
437
438     if (direction & TCL_WRITABLE) {
439         *handlePtr = (ClientData) esPtr->fdin;
440     }
441     if (direction & TCL_READABLE) {
442         *handlePtr = (ClientData) esPtr->fdin;
443     } else {
444         return TCL_ERROR;
445     }
446     return TCL_OK;
447 }
448
449 int
450 expChannelCountGet()
451 {
452     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
453     return tsdPtr->channelCount;
454 }
455
456 int
457 expChannelStillAlive(esBackupPtr, backupName)
458      ExpState *esBackupPtr;
459      char *backupName;
460 {
461     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
462     ExpState *esPtr;
463
464     /* 
465      * This utility function is called from 'exp_background_channelhandler'
466      * and checks to make sure that backupName can still be found in the 
467      * channels linked list at the same address as before.
468      *
469      * If it can't be (or if the memory address has changed) then it
470      * means that it was lost in the background (and possibly another
471      * channel was opened and reassigned the same name).
472      */
473
474     for (esPtr = tsdPtr->firstExpPtr; esPtr; esPtr = esPtr->nextPtr) {
475         if (0 == strcmp(esPtr->name, backupName)) 
476             return (esPtr == esBackupPtr);
477     }
478         
479     /* not found; must have been lost in the background */
480     return 0;
481 }
482
483 #if 0 /* Converted to macros */
484 int
485 expSizeGet(esPtr)
486     ExpState *esPtr;
487 {
488     return esPtr->input.use;
489 }
490
491 int
492 expSizeZero(esPtr)
493     ExpState *esPtr;
494 {
495     return (esPtr->input.use == 0);
496 }
497 #endif
498 /* return 0 for success or negative for failure */
499 int
500 expWriteChars(esPtr,buffer,lenBytes)
501      ExpState *esPtr;
502      char *buffer;
503      int lenBytes;
504 {
505   int rc;
506  retry:
507   rc = Tcl_WriteChars(esPtr->channel,buffer,lenBytes);
508   if ((rc == -1) && (errno == EAGAIN)) goto retry;
509
510   if (!exp_strict_write) {
511     /*
512      * 5.41 compatbility behaviour. Ignore any and all write errors
513      * the OS may have thrown.
514      */
515     return 0;
516   }
517
518   /* just return 0 rather than positive byte counts */
519   return ((rc > 0) ? 0 : rc);
520 }
521
522 int
523 expWriteCharsUni(esPtr,buffer,lenChars)
524      ExpState *esPtr;
525      Tcl_UniChar *buffer;
526      int lenChars;
527 {
528   int rc;
529   Tcl_DString ds;
530
531   Tcl_DStringInit (&ds);
532   Tcl_UniCharToUtfDString (buffer,lenChars,&ds);
533
534   rc = expWriteChars(esPtr,Tcl_DStringValue (&ds), Tcl_DStringLength (&ds));
535
536   Tcl_DStringFree (&ds);
537
538   return rc;
539 }
540
541 void
542 expStateFree(esPtr)
543     ExpState *esPtr;
544 {
545   if (esPtr->fdBusy) {
546     close(esPtr->fdin);
547   }
548
549     esPtr->valid = FALSE;
550     
551     if (!esPtr->keepForever) {
552         ckfree((char *)esPtr);
553     }
554 }
555
556 /* close all connections
557  * 
558  * The kernel would actually do this by default, however Tcl is going to come
559  * along later and try to reap its exec'd processes.  If we have inherited any
560  * via spawn -open, Tcl can hang if we don't close the connections first.
561  */
562 void
563 exp_close_all(interp)
564 Tcl_Interp *interp;
565 {
566     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
567     ExpState *esPtr;
568     ExpState *esNextPtr;
569
570     /* Save the nextPtr in a local variable before calling 'exp_close'
571        as 'expStateFree' can be called from it under some
572        circumstances, possibly causing the memory allocator to smash
573        the value in 'esPtr'. - Andreas Kupries
574     */
575
576     /* no need to keep things in sync (i.e., tsdPtr, count) since we could only
577        be doing this if we're exiting.  Just close everything down. */
578
579     for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esNextPtr) {
580         esNextPtr = esPtr->nextPtr;
581         exp_close(interp,esPtr);
582     }
583 }
584
585 /* wait for any of our own spawned processes we call waitpid rather
586  * than wait to avoid running into someone else's processes.  Yes,
587  * according to Ousterhout this is the best way to do it.
588  * returns the ExpState or 0 if nothing to wait on */
589 ExpState *
590 expWaitOnAny()
591 {
592     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
593     int result;
594     ExpState *esPtr;
595
596     for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esPtr->nextPtr) {
597         if (esPtr->pid == exp_getpid) continue; /* skip ourself */
598         if (esPtr->user_waited) continue;       /* one wait only! */
599         if (esPtr->sys_waited) break;
600       restart:
601         result = waitpid(esPtr->pid,&esPtr->wait,WNOHANG);
602         if (result == esPtr->pid) break;
603         if (result == 0) continue;      /* busy, try next */
604         if (result == -1) {
605             if (errno == EINTR) goto restart;
606             else break;
607         }
608     }
609     return esPtr;
610 }
611
612 ExpState *
613 expWaitOnOne() {
614     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
615     ExpState *esPtr;
616     int pid;
617     /* should really be recoded using the common wait code in command.c */
618     WAIT_STATUS_TYPE status;
619
620     pid = wait(&status);
621     for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esPtr->nextPtr) {
622         if (esPtr->pid == pid) {
623             esPtr->sys_waited = TRUE;
624             esPtr->wait = status;
625             return esPtr;
626         }
627     }
628     /* Should not reach this location. If it happens return a value
629      * causing an easy crash */
630     return NULL;
631 }
632
633 void
634 exp_background_channelhandlers_run_all()
635 {
636     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
637     ExpState *esPtr;
638     ExpState *esNextPtr;
639     ExpState *esPriorPtr = 0;
640
641     /* kick off any that already have input waiting */
642     for (esPtr = tsdPtr->firstExpPtr;esPtr; esPriorPtr = esPtr, esPtr = esPtr->nextPtr) {
643         /* is bg_interp the best way to check if armed? */
644         if (esPtr->bg_interp && !expSizeZero(esPtr)) {
645             /* 
646              * We save the nextPtr in a local variable before calling
647              * 'exp_background_channelhandler' since in some cases
648              * 'expStateFree' could end up getting called before it
649              * returns, leading to a likely segfault on the next
650              * interaction through the for loop.
651              */
652             esNextPtr = esPtr->nextPtr;
653             exp_background_channelhandler((ClientData)esPtr,0);
654             if (esNextPtr != esPtr->nextPtr) {
655                 /* 
656                  * 'expStateFree' must have been called from
657                  * underneath us so we know that esPtr->nextPtr is
658                  * invalid.  However, it is possible that either the
659                  * original nextPtr and/or the priorPtr have been
660                  * freed too.  If the esPriorPtr->nextPtr is now
661                  * esNextPtr it seems safe to proceed.  Otherwise we
662                  * break and end early for safety.
663                  */
664                 if (esPriorPtr && esPriorPtr->nextPtr == esNextPtr) {
665                     esPtr = esPriorPtr;
666                 } else {
667                     break; /* maybe set esPtr = tsdPtr->firstExpPtr again? */
668                 }
669             }
670         }
671     }
672 }
673
674 ExpState *
675 expCreateChannel(interp,fdin,fdout,pid)
676     Tcl_Interp *interp;
677     int fdin;
678     int fdout;
679     int pid;
680 {
681     ExpState *esPtr;
682     int mask;
683     Tcl_ChannelType *channelTypePtr;
684     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
685
686     channelTypePtr = &expChannelType;
687
688     esPtr = (ExpState *) ckalloc((unsigned) sizeof(ExpState));
689
690     esPtr->nextPtr = tsdPtr->firstExpPtr;
691     tsdPtr->firstExpPtr = esPtr;
692
693     sprintf(esPtr->name,"exp%d",fdin);
694
695     /*
696      * For now, stupidly assume this.  We we will likely have to revisit this
697      * later to prevent people from doing stupid things.
698      */
699     mask = TCL_READABLE | TCL_WRITABLE;
700
701     /* not sure about this - what about adopted channels */
702     esPtr->validMask = mask | TCL_EXCEPTION;
703     esPtr->fdin = fdin;
704     esPtr->fdout = fdout;
705
706     /* set close-on-exec for everything but std channels */
707     /* (system and stty commands need access to std channels) */
708     if (fdin != 0 && fdin != 2) {
709       expCloseOnExec(fdin);
710       if (fdin != fdout) expCloseOnExec(fdout);
711     }
712
713     esPtr->fdBusy = FALSE;
714     esPtr->channel = Tcl_CreateChannel(channelTypePtr, esPtr->name,
715             (ClientData) esPtr, mask);
716     Tcl_RegisterChannel(interp,esPtr->channel);
717     esPtr->registered = TRUE;
718     Tcl_SetChannelOption(interp,esPtr->channel,"-buffering","none");
719     Tcl_SetChannelOption(interp,esPtr->channel,"-blocking","0");
720     Tcl_SetChannelOption(interp,esPtr->channel,"-translation","lf");
721
722     esPtr->pid = pid;
723
724     esPtr->input.max    = 1;
725     esPtr->input.use    = 0;
726     esPtr->input.buffer = (Tcl_UniChar*) Tcl_Alloc (sizeof (Tcl_UniChar));
727     esPtr->input.newchars = Tcl_NewObj();
728     Tcl_IncrRefCount (esPtr->input.newchars);
729
730     esPtr->umsize = exp_default_match_max;
731     /* this will reallocate object with an appropriate sized buffer */
732     expAdjust(esPtr);
733
734     esPtr->printed = 0;
735     esPtr->echoed = 0;
736     esPtr->rm_nulls = exp_default_rm_nulls;
737     esPtr->parity = exp_default_parity;
738     esPtr->close_on_eof = exp_default_close_on_eof;
739     esPtr->key = expect_key++;
740     esPtr->force_read = FALSE;
741     esPtr->fg_armed = FALSE;
742     esPtr->chan_orig = 0;
743     esPtr->fd_slave = EXP_NOFD;
744 #ifdef HAVE_PTYTRAP
745     esPtr->slave_name = 0;
746 #endif /* HAVE_PTYTRAP */
747     esPtr->open = TRUE;
748     esPtr->notified = FALSE;
749     esPtr->user_waited = FALSE;
750     esPtr->sys_waited = FALSE;
751     esPtr->bg_interp = 0;
752     esPtr->bg_status = unarmed;
753     esPtr->bg_ecount = 0;
754     esPtr->freeWhenBgHandlerUnblocked = FALSE;
755     esPtr->keepForever = FALSE;
756     esPtr->valid = TRUE;
757     tsdPtr->channelCount++;
758
759     return esPtr;
760 }
761
762 void
763 expChannelInit() {
764     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
765
766     tsdPtr->channelCount = 0;
767 }