/[dtapublic]/projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclpipe.c
ViewVC logotype

Diff of /projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclpipe.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 64 by dashley, Sun Oct 30 04:21:11 2016 UTC revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC
# Line 1  Line 1 
1  /*$Header$ */  /* $Header$ */
2  /*  /*
3   * tclPipe.c --   * tclPipe.c --
4   *   *
5   *      This file contains the generic portion of the command channel   *      This file contains the generic portion of the command channel
6   *      driver as well as various utility routines used in managing   *      driver as well as various utility routines used in managing
7   *      subprocesses.   *      subprocesses.
8   *   *
9   * Copyright (c) 1997 by Sun Microsystems, Inc.   * Copyright (c) 1997 by Sun Microsystems, Inc.
10   *   *
11   * See the file "license.terms" for information on usage and redistribution   * See the file "license.terms" for information on usage and redistribution
12   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13   *   *
14   * RCS: @(#) $Id: tclpipe.c,v 1.1.1.1 2001/06/13 04:44:53 dtashley Exp $   * RCS: @(#) $Id: tclpipe.c,v 1.1.1.1 2001/06/13 04:44:53 dtashley Exp $
15   */   */
16    
17  #include "tclInt.h"  #include "tclInt.h"
18  #include "tclPort.h"  #include "tclPort.h"
19    
20  /*  /*
21   * A linked list of the following structures is used to keep track   * A linked list of the following structures is used to keep track
22   * of child processes that have been detached but haven't exited   * of child processes that have been detached but haven't exited
23   * yet, so we can make sure that they're properly "reaped" (officially   * yet, so we can make sure that they're properly "reaped" (officially
24   * waited for) and don't lie around as zombies cluttering the   * waited for) and don't lie around as zombies cluttering the
25   * system.   * system.
26   */   */
27    
28  typedef struct Detached {  typedef struct Detached {
29      Tcl_Pid pid;                        /* Id of process that's been detached      Tcl_Pid pid;                        /* Id of process that's been detached
30                                           * but isn't known to have exited. */                                           * but isn't known to have exited. */
31      struct Detached *nextPtr;           /* Next in list of all detached      struct Detached *nextPtr;           /* Next in list of all detached
32                                           * processes. */                                           * processes. */
33  } Detached;  } Detached;
34    
35  static Detached *detList = NULL;        /* List of all detached proceses. */  static Detached *detList = NULL;        /* List of all detached proceses. */
36  TCL_DECLARE_MUTEX(pipeMutex)            /* Guard access to detList. */  TCL_DECLARE_MUTEX(pipeMutex)            /* Guard access to detList. */
37    
38  /*  /*
39   * Declarations for local procedures defined in this file:   * Declarations for local procedures defined in this file:
40   */   */
41    
42  static TclFile  FileForRedirect _ANSI_ARGS_((Tcl_Interp *interp,  static TclFile  FileForRedirect _ANSI_ARGS_((Tcl_Interp *interp,
43                      char *spec, int atOk, char *arg, char *nextArg,                      char *spec, int atOk, char *arg, char *nextArg,
44                      int flags, int *skipPtr, int *closePtr, int *releasePtr));                      int flags, int *skipPtr, int *closePtr, int *releasePtr));
45    
46  /*  /*
47   *----------------------------------------------------------------------   *----------------------------------------------------------------------
48   *   *
49   * FileForRedirect --   * FileForRedirect --
50   *   *
51   *      This procedure does much of the work of parsing redirection   *      This procedure does much of the work of parsing redirection
52   *      operators.  It handles "@" if specified and allowed, and a file   *      operators.  It handles "@" if specified and allowed, and a file
53   *      name, and opens the file if necessary.   *      name, and opens the file if necessary.
54   *   *
55   * Results:   * Results:
56   *      The return value is the descriptor number for the file.  If an   *      The return value is the descriptor number for the file.  If an
57   *      error occurs then NULL is returned and an error message is left   *      error occurs then NULL is returned and an error message is left
58   *      in the interp's result.  Several arguments are side-effected; see   *      in the interp's result.  Several arguments are side-effected; see
59   *      the argument list below for details.   *      the argument list below for details.
60   *   *
61   * Side effects:   * Side effects:
62   *      None.   *      None.
63   *   *
64   *----------------------------------------------------------------------   *----------------------------------------------------------------------
65   */   */
66    
67  static TclFile  static TclFile
68  FileForRedirect(interp, spec, atOK, arg, nextArg, flags, skipPtr, closePtr,  FileForRedirect(interp, spec, atOK, arg, nextArg, flags, skipPtr, closePtr,
69          releasePtr)          releasePtr)
70      Tcl_Interp *interp;         /* Intepreter to use for error reporting. */      Tcl_Interp *interp;         /* Intepreter to use for error reporting. */
71      char *spec;                 /* Points to character just after      char *spec;                 /* Points to character just after
72                                   * redirection character. */                                   * redirection character. */
73      char *arg;                  /* Pointer to entire argument containing      char *arg;                  /* Pointer to entire argument containing
74                                   * spec:  used for error reporting. */                                   * spec:  used for error reporting. */
75      int atOK;                   /* Non-zero means that '@' notation can be      int atOK;                   /* Non-zero means that '@' notation can be
76                                   * used to specify a channel, zero means that                                   * used to specify a channel, zero means that
77                                   * it isn't. */                                   * it isn't. */
78      char *nextArg;              /* Next argument in argc/argv array, if needed      char *nextArg;              /* Next argument in argc/argv array, if needed
79                                   * for file name or channel name.  May be                                   * for file name or channel name.  May be
80                                   * NULL. */                                   * NULL. */
81      int flags;                  /* Flags to use for opening file or to      int flags;                  /* Flags to use for opening file or to
82                                   * specify mode for channel. */                                   * specify mode for channel. */
83      int *skipPtr;               /* Filled with 1 if redirection target was      int *skipPtr;               /* Filled with 1 if redirection target was
84                                   * in spec, 2 if it was in nextArg. */                                   * in spec, 2 if it was in nextArg. */
85      int *closePtr;              /* Filled with one if the caller should      int *closePtr;              /* Filled with one if the caller should
86                                   * close the file when done with it, zero                                   * close the file when done with it, zero
87                                   * otherwise. */                                   * otherwise. */
88      int *releasePtr;      int *releasePtr;
89  {  {
90      int writing = (flags & O_WRONLY);      int writing = (flags & O_WRONLY);
91      Tcl_Channel chan;      Tcl_Channel chan;
92      TclFile file;      TclFile file;
93    
94      *skipPtr = 1;      *skipPtr = 1;
95      if ((atOK != 0)  && (*spec == '@')) {      if ((atOK != 0)  && (*spec == '@')) {
96          spec++;          spec++;
97          if (*spec == '\0') {          if (*spec == '\0') {
98              spec = nextArg;              spec = nextArg;
99              if (spec == NULL) {              if (spec == NULL) {
100                  goto badLastArg;                  goto badLastArg;
101              }              }
102              *skipPtr = 2;              *skipPtr = 2;
103          }          }
104          chan = Tcl_GetChannel(interp, spec, NULL);          chan = Tcl_GetChannel(interp, spec, NULL);
105          if (chan == (Tcl_Channel) NULL) {          if (chan == (Tcl_Channel) NULL) {
106              return NULL;              return NULL;
107          }          }
108          file = TclpMakeFile(chan, writing ? TCL_WRITABLE : TCL_READABLE);          file = TclpMakeFile(chan, writing ? TCL_WRITABLE : TCL_READABLE);
109          if (file == NULL) {          if (file == NULL) {
110              Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan),              Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan),
111                      "\" wasn't opened for ",                      "\" wasn't opened for ",
112                      ((writing) ? "writing" : "reading"), (char *) NULL);                      ((writing) ? "writing" : "reading"), (char *) NULL);
113              return NULL;              return NULL;
114          }          }
115          *releasePtr = 1;          *releasePtr = 1;
116          if (writing) {          if (writing) {
117    
118              /*              /*
119               * Be sure to flush output to the file, so that anything               * Be sure to flush output to the file, so that anything
120               * written by the child appears after stuff we've already               * written by the child appears after stuff we've already
121               * written.               * written.
122               */               */
123    
124              Tcl_Flush(chan);              Tcl_Flush(chan);
125          }          }
126      } else {      } else {
127          char *name;          char *name;
128          Tcl_DString nameString;          Tcl_DString nameString;
129    
130          if (*spec == '\0') {          if (*spec == '\0') {
131              spec = nextArg;              spec = nextArg;
132              if (spec == NULL) {              if (spec == NULL) {
133                  goto badLastArg;                  goto badLastArg;
134              }              }
135              *skipPtr = 2;              *skipPtr = 2;
136          }          }
137          name = Tcl_TranslateFileName(interp, spec, &nameString);          name = Tcl_TranslateFileName(interp, spec, &nameString);
138          if (name != NULL) {          if (name != NULL) {
139              file = TclpOpenFile(name, flags);              file = TclpOpenFile(name, flags);
140          } else {          } else {
141              file = NULL;              file = NULL;
142          }          }
143          Tcl_DStringFree(&nameString);          Tcl_DStringFree(&nameString);
144          if (file == NULL) {          if (file == NULL) {
145              Tcl_AppendResult(interp, "couldn't ",              Tcl_AppendResult(interp, "couldn't ",
146                      ((writing) ? "write" : "read"), " file \"", spec, "\": ",                      ((writing) ? "write" : "read"), " file \"", spec, "\": ",
147                      Tcl_PosixError(interp), (char *) NULL);                      Tcl_PosixError(interp), (char *) NULL);
148              return NULL;              return NULL;
149          }          }
150          *closePtr = 1;          *closePtr = 1;
151      }      }
152      return file;      return file;
153    
154      badLastArg:      badLastArg:
155      Tcl_AppendResult(interp, "can't specify \"", arg,      Tcl_AppendResult(interp, "can't specify \"", arg,
156              "\" as last word in command", (char *) NULL);              "\" as last word in command", (char *) NULL);
157      return NULL;      return NULL;
158  }  }
159    
160  /*  /*
161   *----------------------------------------------------------------------   *----------------------------------------------------------------------
162   *   *
163   * Tcl_DetachPids --   * Tcl_DetachPids --
164   *   *
165   *      This procedure is called to indicate that one or more child   *      This procedure is called to indicate that one or more child
166   *      processes have been placed in background and will never be   *      processes have been placed in background and will never be
167   *      waited for;  they should eventually be reaped by   *      waited for;  they should eventually be reaped by
168   *      Tcl_ReapDetachedProcs.   *      Tcl_ReapDetachedProcs.
169   *   *
170   * Results:   * Results:
171   *      None.   *      None.
172   *   *
173   * Side effects:   * Side effects:
174   *      None.   *      None.
175   *   *
176   *----------------------------------------------------------------------   *----------------------------------------------------------------------
177   */   */
178    
179  void  void
180  Tcl_DetachPids(numPids, pidPtr)  Tcl_DetachPids(numPids, pidPtr)
181      int numPids;                /* Number of pids to detach:  gives size      int numPids;                /* Number of pids to detach:  gives size
182                                   * of array pointed to by pidPtr. */                                   * of array pointed to by pidPtr. */
183      Tcl_Pid *pidPtr;            /* Array of pids to detach. */      Tcl_Pid *pidPtr;            /* Array of pids to detach. */
184  {  {
185      register Detached *detPtr;      register Detached *detPtr;
186      int i;      int i;
187    
188      Tcl_MutexLock(&pipeMutex);      Tcl_MutexLock(&pipeMutex);
189      for (i = 0; i < numPids; i++) {      for (i = 0; i < numPids; i++) {
190          detPtr = (Detached *) ckalloc(sizeof(Detached));          detPtr = (Detached *) ckalloc(sizeof(Detached));
191          detPtr->pid = pidPtr[i];          detPtr->pid = pidPtr[i];
192          detPtr->nextPtr = detList;          detPtr->nextPtr = detList;
193          detList = detPtr;          detList = detPtr;
194      }      }
195      Tcl_MutexUnlock(&pipeMutex);      Tcl_MutexUnlock(&pipeMutex);
196    
197  }  }
198    
199  /*  /*
200   *----------------------------------------------------------------------   *----------------------------------------------------------------------
201   *   *
202   * Tcl_ReapDetachedProcs --   * Tcl_ReapDetachedProcs --
203   *   *
204   *      This procedure checks to see if any detached processes have   *      This procedure checks to see if any detached processes have
205   *      exited and, if so, it "reaps" them by officially waiting on   *      exited and, if so, it "reaps" them by officially waiting on
206   *      them.  It should be called "occasionally" to make sure that   *      them.  It should be called "occasionally" to make sure that
207   *      all detached processes are eventually reaped.   *      all detached processes are eventually reaped.
208   *   *
209   * Results:   * Results:
210   *      None.   *      None.
211   *   *
212   * Side effects:   * Side effects:
213   *      Processes are waited on, so that they can be reaped by the   *      Processes are waited on, so that they can be reaped by the
214   *      system.   *      system.
215   *   *
216   *----------------------------------------------------------------------   *----------------------------------------------------------------------
217   */   */
218    
219  void  void
220  Tcl_ReapDetachedProcs()  Tcl_ReapDetachedProcs()
221  {  {
222      register Detached *detPtr;      register Detached *detPtr;
223      Detached *nextPtr, *prevPtr;      Detached *nextPtr, *prevPtr;
224      int status;      int status;
225      Tcl_Pid pid;      Tcl_Pid pid;
226    
227      Tcl_MutexLock(&pipeMutex);      Tcl_MutexLock(&pipeMutex);
228      for (detPtr = detList, prevPtr = NULL; detPtr != NULL; ) {      for (detPtr = detList, prevPtr = NULL; detPtr != NULL; ) {
229          pid = Tcl_WaitPid(detPtr->pid, &status, WNOHANG);          pid = Tcl_WaitPid(detPtr->pid, &status, WNOHANG);
230          if ((pid == 0) || ((pid == (Tcl_Pid) -1) && (errno != ECHILD))) {          if ((pid == 0) || ((pid == (Tcl_Pid) -1) && (errno != ECHILD))) {
231              prevPtr = detPtr;              prevPtr = detPtr;
232              detPtr = detPtr->nextPtr;              detPtr = detPtr->nextPtr;
233              continue;              continue;
234          }          }
235          nextPtr = detPtr->nextPtr;          nextPtr = detPtr->nextPtr;
236          if (prevPtr == NULL) {          if (prevPtr == NULL) {
237              detList = detPtr->nextPtr;              detList = detPtr->nextPtr;
238          } else {          } else {
239              prevPtr->nextPtr = detPtr->nextPtr;              prevPtr->nextPtr = detPtr->nextPtr;
240          }          }
241          ckfree((char *) detPtr);          ckfree((char *) detPtr);
242          detPtr = nextPtr;          detPtr = nextPtr;
243      }      }
244      Tcl_MutexUnlock(&pipeMutex);      Tcl_MutexUnlock(&pipeMutex);
245  }  }
246    
247  /*  /*
248   *----------------------------------------------------------------------   *----------------------------------------------------------------------
249   *   *
250   * TclCleanupChildren --   * TclCleanupChildren --
251   *   *
252   *      This is a utility procedure used to wait for child processes   *      This is a utility procedure used to wait for child processes
253   *      to exit, record information about abnormal exits, and then   *      to exit, record information about abnormal exits, and then
254   *      collect any stderr output generated by them.   *      collect any stderr output generated by them.
255   *   *
256   * Results:   * Results:
257   *      The return value is a standard Tcl result.  If anything at   *      The return value is a standard Tcl result.  If anything at
258   *      weird happened with the child processes, TCL_ERROR is returned   *      weird happened with the child processes, TCL_ERROR is returned
259   *      and a message is left in the interp's result.   *      and a message is left in the interp's result.
260   *   *
261   * Side effects:   * Side effects:
262   *      If the last character of the interp's result is a newline, then it   *      If the last character of the interp's result is a newline, then it
263   *      is removed unless keepNewline is non-zero.  File errorId gets   *      is removed unless keepNewline is non-zero.  File errorId gets
264   *      closed, and pidPtr is freed back to the storage allocator.   *      closed, and pidPtr is freed back to the storage allocator.
265   *   *
266   *----------------------------------------------------------------------   *----------------------------------------------------------------------
267   */   */
268    
269  int  int
270  TclCleanupChildren(interp, numPids, pidPtr, errorChan)  TclCleanupChildren(interp, numPids, pidPtr, errorChan)
271      Tcl_Interp *interp;         /* Used for error messages. */      Tcl_Interp *interp;         /* Used for error messages. */
272      int numPids;                /* Number of entries in pidPtr array. */      int numPids;                /* Number of entries in pidPtr array. */
273      Tcl_Pid *pidPtr;            /* Array of process ids of children. */      Tcl_Pid *pidPtr;            /* Array of process ids of children. */
274      Tcl_Channel errorChan;      /* Channel for file containing stderr output      Tcl_Channel errorChan;      /* Channel for file containing stderr output
275                                   * from pipeline.  NULL means there isn't any                                   * from pipeline.  NULL means there isn't any
276                                   * stderr output. */                                   * stderr output. */
277  {  {
278      int result = TCL_OK;      int result = TCL_OK;
279      int i, abnormalExit, anyErrorInfo;      int i, abnormalExit, anyErrorInfo;
280      Tcl_Pid pid;      Tcl_Pid pid;
281      WAIT_STATUS_TYPE waitStatus;      WAIT_STATUS_TYPE waitStatus;
282      char *msg;      char *msg;
283    
284      abnormalExit = 0;      abnormalExit = 0;
285      for (i = 0; i < numPids; i++) {      for (i = 0; i < numPids; i++) {
286          pid = Tcl_WaitPid(pidPtr[i], (int *) &waitStatus, 0);          pid = Tcl_WaitPid(pidPtr[i], (int *) &waitStatus, 0);
287          if (pid == (Tcl_Pid) -1) {          if (pid == (Tcl_Pid) -1) {
288              result = TCL_ERROR;              result = TCL_ERROR;
289              if (interp != (Tcl_Interp *) NULL) {              if (interp != (Tcl_Interp *) NULL) {
290                  msg = Tcl_PosixError(interp);                  msg = Tcl_PosixError(interp);
291                  if (errno == ECHILD) {                  if (errno == ECHILD) {
292                      /*                      /*
293                       * This changeup in message suggested by Mark Diekhans                       * This changeup in message suggested by Mark Diekhans
294                       * to remind people that ECHILD errors can occur on                       * to remind people that ECHILD errors can occur on
295                       * some systems if SIGCHLD isn't in its default state.                       * some systems if SIGCHLD isn't in its default state.
296                       */                       */
297    
298                      msg =                      msg =
299                          "child process lost (is SIGCHLD ignored or trapped?)";                          "child process lost (is SIGCHLD ignored or trapped?)";
300                  }                  }
301                  Tcl_AppendResult(interp, "error waiting for process to exit: ",                  Tcl_AppendResult(interp, "error waiting for process to exit: ",
302                          msg, (char *) NULL);                          msg, (char *) NULL);
303              }              }
304              continue;              continue;
305          }          }
306    
307          /*          /*
308           * Create error messages for unusual process exits.  An           * Create error messages for unusual process exits.  An
309           * extra newline gets appended to each error message, but           * extra newline gets appended to each error message, but
310           * it gets removed below (in the same fashion that an           * it gets removed below (in the same fashion that an
311           * extra newline in the command's output is removed).           * extra newline in the command's output is removed).
312           */           */
313    
314          if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) {          if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) {
315              char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE];              char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE];
316    
317              result = TCL_ERROR;              result = TCL_ERROR;
318              TclFormatInt(msg1, (long) TclpGetPid(pid));              TclFormatInt(msg1, (long) TclpGetPid(pid));
319              if (WIFEXITED(waitStatus)) {              if (WIFEXITED(waitStatus)) {
320                  if (interp != (Tcl_Interp *) NULL) {                  if (interp != (Tcl_Interp *) NULL) {
321                      TclFormatInt(msg2, WEXITSTATUS(waitStatus));                      TclFormatInt(msg2, WEXITSTATUS(waitStatus));
322                      Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2,                      Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2,
323                              (char *) NULL);                              (char *) NULL);
324                  }                  }
325                  abnormalExit = 1;                  abnormalExit = 1;
326              } else if (WIFSIGNALED(waitStatus)) {              } else if (WIFSIGNALED(waitStatus)) {
327                  if (interp != (Tcl_Interp *) NULL) {                  if (interp != (Tcl_Interp *) NULL) {
328                      char *p;                      char *p;
329                                            
330                      p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus)));                      p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus)));
331                      Tcl_SetErrorCode(interp, "CHILDKILLED", msg1,                      Tcl_SetErrorCode(interp, "CHILDKILLED", msg1,
332                              Tcl_SignalId((int) (WTERMSIG(waitStatus))), p,                              Tcl_SignalId((int) (WTERMSIG(waitStatus))), p,
333                              (char *) NULL);                              (char *) NULL);
334                      Tcl_AppendResult(interp, "child killed: ", p, "\n",                      Tcl_AppendResult(interp, "child killed: ", p, "\n",
335                              (char *) NULL);                              (char *) NULL);
336                  }                  }
337              } else if (WIFSTOPPED(waitStatus)) {              } else if (WIFSTOPPED(waitStatus)) {
338                  if (interp != (Tcl_Interp *) NULL) {                  if (interp != (Tcl_Interp *) NULL) {
339                      char *p;                      char *p;
340    
341                      p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus)));                      p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus)));
342                      Tcl_SetErrorCode(interp, "CHILDSUSP", msg1,                      Tcl_SetErrorCode(interp, "CHILDSUSP", msg1,
343                              Tcl_SignalId((int) (WSTOPSIG(waitStatus))),                              Tcl_SignalId((int) (WSTOPSIG(waitStatus))),
344                              p, (char *) NULL);                              p, (char *) NULL);
345                      Tcl_AppendResult(interp, "child suspended: ", p, "\n",                      Tcl_AppendResult(interp, "child suspended: ", p, "\n",
346                              (char *) NULL);                              (char *) NULL);
347                  }                  }
348              } else {              } else {
349                  if (interp != (Tcl_Interp *) NULL) {                  if (interp != (Tcl_Interp *) NULL) {
350                      Tcl_AppendResult(interp,                      Tcl_AppendResult(interp,
351                              "child wait status didn't make sense\n",                              "child wait status didn't make sense\n",
352                              (char *) NULL);                              (char *) NULL);
353                  }                  }
354              }              }
355          }          }
356      }      }
357    
358      /*      /*
359       * Read the standard error file.  If there's anything there,       * Read the standard error file.  If there's anything there,
360       * then return an error and add the file's contents to the result       * then return an error and add the file's contents to the result
361       * string.       * string.
362       */       */
363    
364      anyErrorInfo = 0;      anyErrorInfo = 0;
365      if (errorChan != NULL) {      if (errorChan != NULL) {
366    
367          /*          /*
368           * Make sure we start at the beginning of the file.           * Make sure we start at the beginning of the file.
369           */           */
370    
371          if (interp != NULL) {          if (interp != NULL) {
372              int count;              int count;
373              Tcl_Obj *objPtr;              Tcl_Obj *objPtr;
374                            
375              Tcl_Seek(errorChan, 0L, SEEK_SET);              Tcl_Seek(errorChan, 0L, SEEK_SET);
376              objPtr = Tcl_NewObj();              objPtr = Tcl_NewObj();
377              count = Tcl_ReadChars(errorChan, objPtr, -1, 0);              count = Tcl_ReadChars(errorChan, objPtr, -1, 0);
378              if (count < 0) {              if (count < 0) {
379                  result = TCL_ERROR;                  result = TCL_ERROR;
380                  Tcl_DecrRefCount(objPtr);                  Tcl_DecrRefCount(objPtr);
381                  Tcl_ResetResult(interp);                  Tcl_ResetResult(interp);
382                  Tcl_AppendResult(interp, "error reading stderr output file: ",                  Tcl_AppendResult(interp, "error reading stderr output file: ",
383                          Tcl_PosixError(interp), NULL);                          Tcl_PosixError(interp), NULL);
384              } else if (count > 0) {              } else if (count > 0) {
385                  anyErrorInfo = 1;                  anyErrorInfo = 1;
386                  Tcl_SetObjResult(interp, objPtr);                  Tcl_SetObjResult(interp, objPtr);
387                  result = TCL_ERROR;                  result = TCL_ERROR;
388              } else {              } else {
389                  Tcl_DecrRefCount(objPtr);                  Tcl_DecrRefCount(objPtr);
390              }              }
391          }          }
392          Tcl_Close(NULL, errorChan);          Tcl_Close(NULL, errorChan);
393      }      }
394    
395      /*      /*
396       * If a child exited abnormally but didn't output any error information       * If a child exited abnormally but didn't output any error information
397       * at all, generate an error message here.       * at all, generate an error message here.
398       */       */
399    
400      if ((abnormalExit != 0) && (anyErrorInfo == 0) && (interp != NULL)) {      if ((abnormalExit != 0) && (anyErrorInfo == 0) && (interp != NULL)) {
401          Tcl_AppendResult(interp, "child process exited abnormally",          Tcl_AppendResult(interp, "child process exited abnormally",
402                  (char *) NULL);                  (char *) NULL);
403      }      }
404      return result;      return result;
405  }  }
406    
407  /*  /*
408   *----------------------------------------------------------------------   *----------------------------------------------------------------------
409   *   *
410   * TclCreatePipeline --   * TclCreatePipeline --
411   *   *
412   *      Given an argc/argv array, instantiate a pipeline of processes   *      Given an argc/argv array, instantiate a pipeline of processes
413   *      as described by the argv.   *      as described by the argv.
414   *   *
415   *      This procedure is unofficially exported for use by BLT.   *      This procedure is unofficially exported for use by BLT.
416   *   *
417   * Results:   * Results:
418   *      The return value is a count of the number of new processes   *      The return value is a count of the number of new processes
419   *      created, or -1 if an error occurred while creating the pipeline.   *      created, or -1 if an error occurred while creating the pipeline.
420   *      *pidArrayPtr is filled in with the address of a dynamically   *      *pidArrayPtr is filled in with the address of a dynamically
421   *      allocated array giving the ids of all of the processes.  It   *      allocated array giving the ids of all of the processes.  It
422   *      is up to the caller to free this array when it isn't needed   *      is up to the caller to free this array when it isn't needed
423   *      anymore.  If inPipePtr is non-NULL, *inPipePtr is filled in   *      anymore.  If inPipePtr is non-NULL, *inPipePtr is filled in
424   *      with the file id for the input pipe for the pipeline (if any):   *      with the file id for the input pipe for the pipeline (if any):
425   *      the caller must eventually close this file.  If outPipePtr   *      the caller must eventually close this file.  If outPipePtr
426   *      isn't NULL, then *outPipePtr is filled in with the file id   *      isn't NULL, then *outPipePtr is filled in with the file id
427   *      for the output pipe from the pipeline:  the caller must close   *      for the output pipe from the pipeline:  the caller must close
428   *      this file.  If errFilePtr isn't NULL, then *errFilePtr is filled   *      this file.  If errFilePtr isn't NULL, then *errFilePtr is filled
429   *      with a file id that may be used to read error output after the   *      with a file id that may be used to read error output after the
430   *      pipeline completes.   *      pipeline completes.
431   *   *
432   * Side effects:   * Side effects:
433   *      Processes and pipes are created.   *      Processes and pipes are created.
434   *   *
435   *----------------------------------------------------------------------   *----------------------------------------------------------------------
436   */   */
437    
438  int  int
439  TclCreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,  TclCreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,
440          outPipePtr, errFilePtr)          outPipePtr, errFilePtr)
441      Tcl_Interp *interp;         /* Interpreter to use for error reporting. */      Tcl_Interp *interp;         /* Interpreter to use for error reporting. */
442      int argc;                   /* Number of entries in argv. */      int argc;                   /* Number of entries in argv. */
443      char **argv;                /* Array of strings describing commands in      char **argv;                /* Array of strings describing commands in
444                                   * pipeline plus I/O redirection with <,                                   * pipeline plus I/O redirection with <,
445                                   * <<,  >, etc.  Argv[argc] must be NULL. */                                   * <<,  >, etc.  Argv[argc] must be NULL. */
446      Tcl_Pid **pidArrayPtr;      /* Word at *pidArrayPtr gets filled in with      Tcl_Pid **pidArrayPtr;      /* Word at *pidArrayPtr gets filled in with
447                                   * address of array of pids for processes                                   * address of array of pids for processes
448                                   * in pipeline (first pid is first process                                   * in pipeline (first pid is first process
449                                   * in pipeline). */                                   * in pipeline). */
450      TclFile *inPipePtr;         /* If non-NULL, input to the pipeline comes      TclFile *inPipePtr;         /* If non-NULL, input to the pipeline comes
451                                   * from a pipe (unless overridden by                                   * from a pipe (unless overridden by
452                                   * redirection in the command).  The file                                   * redirection in the command).  The file
453                                   * id with which to write to this pipe is                                   * id with which to write to this pipe is
454                                   * stored at *inPipePtr.  NULL means command                                   * stored at *inPipePtr.  NULL means command
455                                   * specified its own input source. */                                   * specified its own input source. */
456      TclFile *outPipePtr;        /* If non-NULL, output to the pipeline goes      TclFile *outPipePtr;        /* If non-NULL, output to the pipeline goes
457                                   * to a pipe, unless overriden by redirection                                   * to a pipe, unless overriden by redirection
458                                   * in the command.  The file id with which to                                   * in the command.  The file id with which to
459                                   * read frome this pipe is stored at                                   * read frome this pipe is stored at
460                                   * *outPipePtr.  NULL means command specified                                   * *outPipePtr.  NULL means command specified
461                                   * its own output sink. */                                   * its own output sink. */
462      TclFile *errFilePtr;        /* If non-NULL, all stderr output from the      TclFile *errFilePtr;        /* If non-NULL, all stderr output from the
463                                   * pipeline will go to a temporary file                                   * pipeline will go to a temporary file
464                                   * created here, and a descriptor to read                                   * created here, and a descriptor to read
465                                   * the file will be left at *errFilePtr.                                   * the file will be left at *errFilePtr.
466                                   * The file will be removed already, so                                   * The file will be removed already, so
467                                   * closing this descriptor will be the end                                   * closing this descriptor will be the end
468                                   * of the file.  If this is NULL, then                                   * of the file.  If this is NULL, then
469                                   * all stderr output goes to our stderr.                                   * all stderr output goes to our stderr.
470                                   * If the pipeline specifies redirection                                   * If the pipeline specifies redirection
471                                   * then the file will still be created                                   * then the file will still be created
472                                   * but it will never get any data. */                                   * but it will never get any data. */
473  {  {
474      Tcl_Pid *pidPtr = NULL;     /* Points to malloc-ed array holding all      Tcl_Pid *pidPtr = NULL;     /* Points to malloc-ed array holding all
475                                   * the pids of child processes. */                                   * the pids of child processes. */
476      int numPids;                /* Actual number of processes that exist      int numPids;                /* Actual number of processes that exist
477                                   * at *pidPtr right now. */                                   * at *pidPtr right now. */
478      int cmdCount;               /* Count of number of distinct commands      int cmdCount;               /* Count of number of distinct commands
479                                   * found in argc/argv. */                                   * found in argc/argv. */
480      char *inputLiteral = NULL;  /* If non-null, then this points to a      char *inputLiteral = NULL;  /* If non-null, then this points to a
481                                   * string containing input data (specified                                   * string containing input data (specified
482                                   * via <<) to be piped to the first process                                   * via <<) to be piped to the first process
483                                   * in the pipeline. */                                   * in the pipeline. */
484      TclFile inputFile = NULL;   /* If != NULL, gives file to use as input for      TclFile inputFile = NULL;   /* If != NULL, gives file to use as input for
485                                   * first process in pipeline (specified via <                                   * first process in pipeline (specified via <
486                                   * or <@). */                                   * or <@). */
487      int inputClose = 0;         /* If non-zero, then inputFile should be      int inputClose = 0;         /* If non-zero, then inputFile should be
488                                   * closed when cleaning up. */                                   * closed when cleaning up. */
489      int inputRelease = 0;      int inputRelease = 0;
490      TclFile outputFile = NULL;  /* Writable file for output from last command      TclFile outputFile = NULL;  /* Writable file for output from last command
491                                   * in pipeline (could be file or pipe).  NULL                                   * in pipeline (could be file or pipe).  NULL
492                                   * means use stdout. */                                   * means use stdout. */
493      int outputClose = 0;        /* If non-zero, then outputFile should be      int outputClose = 0;        /* If non-zero, then outputFile should be
494                                   * closed when cleaning up. */                                   * closed when cleaning up. */
495      int outputRelease = 0;      int outputRelease = 0;
496      TclFile errorFile = NULL;   /* Writable file for error output from all      TclFile errorFile = NULL;   /* Writable file for error output from all
497                                   * commands in pipeline.  NULL means use                                   * commands in pipeline.  NULL means use
498                                   * stderr. */                                   * stderr. */
499      int errorClose = 0;         /* If non-zero, then errorFile should be      int errorClose = 0;         /* If non-zero, then errorFile should be
500                                   * closed when cleaning up. */                                   * closed when cleaning up. */
501      int errorRelease = 0;      int errorRelease = 0;
502      char *p;      char *p;
503      int skip, lastBar, lastArg, i, j, atOK, flags, errorToOutput;      int skip, lastBar, lastArg, i, j, atOK, flags, errorToOutput;
504      Tcl_DString execBuffer;      Tcl_DString execBuffer;
505      TclFile pipeIn;      TclFile pipeIn;
506      TclFile curInFile, curOutFile, curErrFile;      TclFile curInFile, curOutFile, curErrFile;
507      Tcl_Channel channel;      Tcl_Channel channel;
508    
509      if (inPipePtr != NULL) {      if (inPipePtr != NULL) {
510          *inPipePtr = NULL;          *inPipePtr = NULL;
511      }      }
512      if (outPipePtr != NULL) {      if (outPipePtr != NULL) {
513          *outPipePtr = NULL;          *outPipePtr = NULL;
514      }      }
515      if (errFilePtr != NULL) {      if (errFilePtr != NULL) {
516          *errFilePtr = NULL;          *errFilePtr = NULL;
517      }      }
518    
519      Tcl_DStringInit(&execBuffer);      Tcl_DStringInit(&execBuffer);
520            
521      pipeIn = NULL;      pipeIn = NULL;
522      curInFile = NULL;      curInFile = NULL;
523      curOutFile = NULL;      curOutFile = NULL;
524      numPids = 0;      numPids = 0;
525    
526      /*      /*
527       * First, scan through all the arguments to figure out the structure       * First, scan through all the arguments to figure out the structure
528       * of the pipeline.  Process all of the input and output redirection       * of the pipeline.  Process all of the input and output redirection
529       * arguments and remove them from the argument list in the pipeline.       * arguments and remove them from the argument list in the pipeline.
530       * Count the number of distinct processes (it's the number of "|"       * Count the number of distinct processes (it's the number of "|"
531       * arguments plus one) but don't remove the "|" arguments because       * arguments plus one) but don't remove the "|" arguments because
532       * they'll be used in the second pass to seperate the individual       * they'll be used in the second pass to seperate the individual
533       * child processes.  Cannot start the child processes in this pass       * child processes.  Cannot start the child processes in this pass
534       * because the redirection symbols may appear anywhere in the       * because the redirection symbols may appear anywhere in the
535       * command line -- e.g., the '<' that specifies the input to the       * command line -- e.g., the '<' that specifies the input to the
536       * entire pipe may appear at the very end of the argument list.       * entire pipe may appear at the very end of the argument list.
537       */       */
538    
539      lastBar = -1;      lastBar = -1;
540      cmdCount = 1;      cmdCount = 1;
541      for (i = 0; i < argc; i++) {      for (i = 0; i < argc; i++) {
542          skip = 0;          skip = 0;
543          p = argv[i];          p = argv[i];
544          switch (*p++) {          switch (*p++) {
545          case '|':          case '|':
546              if (*p == '&') {              if (*p == '&') {
547                  p++;                  p++;
548              }              }
549              if (*p == '\0') {              if (*p == '\0') {
550                  if ((i == (lastBar + 1)) || (i == (argc - 1))) {                  if ((i == (lastBar + 1)) || (i == (argc - 1))) {
551                      Tcl_SetResult(interp,                      Tcl_SetResult(interp,
552                              "illegal use of | or |& in command",                              "illegal use of | or |& in command",
553                              TCL_STATIC);                              TCL_STATIC);
554                      goto error;                      goto error;
555                  }                  }
556              }              }
557              lastBar = i;              lastBar = i;
558              cmdCount++;              cmdCount++;
559              break;              break;
560    
561          case '<':          case '<':
562              if (inputClose != 0) {              if (inputClose != 0) {
563                  inputClose = 0;                  inputClose = 0;
564                  TclpCloseFile(inputFile);                  TclpCloseFile(inputFile);
565              }              }
566              if (inputRelease != 0) {              if (inputRelease != 0) {
567                  inputRelease = 0;                  inputRelease = 0;
568                  TclpReleaseFile(inputFile);                  TclpReleaseFile(inputFile);
569              }              }
570              if (*p == '<') {              if (*p == '<') {
571                  inputFile = NULL;                  inputFile = NULL;
572                  inputLiteral = p + 1;                  inputLiteral = p + 1;
573                  skip = 1;                  skip = 1;
574                  if (*inputLiteral == '\0') {                  if (*inputLiteral == '\0') {
575                      inputLiteral = argv[i + 1];                      inputLiteral = argv[i + 1];
576                      if (inputLiteral == NULL) {                      if (inputLiteral == NULL) {
577                          Tcl_AppendResult(interp, "can't specify \"", argv[i],                          Tcl_AppendResult(interp, "can't specify \"", argv[i],
578                                  "\" as last word in command", (char *) NULL);                                  "\" as last word in command", (char *) NULL);
579                          goto error;                          goto error;
580                      }                      }
581                      skip = 2;                      skip = 2;
582                  }                  }
583              } else {              } else {
584                  inputLiteral = NULL;                  inputLiteral = NULL;
585                  inputFile = FileForRedirect(interp, p, 1, argv[i],                  inputFile = FileForRedirect(interp, p, 1, argv[i],
586                          argv[i + 1], O_RDONLY, &skip, &inputClose, &inputRelease);                          argv[i + 1], O_RDONLY, &skip, &inputClose, &inputRelease);
587                  if (inputFile == NULL) {                  if (inputFile == NULL) {
588                      goto error;                      goto error;
589                  }                  }
590              }              }
591              break;              break;
592    
593          case '>':          case '>':
594              atOK = 1;              atOK = 1;
595              flags = O_WRONLY | O_CREAT | O_TRUNC;              flags = O_WRONLY | O_CREAT | O_TRUNC;
596              errorToOutput = 0;              errorToOutput = 0;
597              if (*p == '>') {              if (*p == '>') {
598                  p++;                  p++;
599                  atOK = 0;                  atOK = 0;
600                  flags = O_WRONLY | O_CREAT;                  flags = O_WRONLY | O_CREAT;
601              }              }
602              if (*p == '&') {              if (*p == '&') {
603                  if (errorClose != 0) {                  if (errorClose != 0) {
604                      errorClose = 0;                      errorClose = 0;
605                      TclpCloseFile(errorFile);                      TclpCloseFile(errorFile);
606                  }                  }
607                  errorToOutput = 1;                  errorToOutput = 1;
608                  p++;                  p++;
609              }              }
610    
611              /*              /*
612               * Close the old output file, but only if the error file is               * Close the old output file, but only if the error file is
613               * not also using it.               * not also using it.
614               */               */
615    
616              if (outputClose != 0) {              if (outputClose != 0) {
617                  outputClose = 0;                  outputClose = 0;
618                  if (errorFile == outputFile) {                  if (errorFile == outputFile) {
619                      errorClose = 1;                      errorClose = 1;
620                  } else {                  } else {
621                      TclpCloseFile(outputFile);                      TclpCloseFile(outputFile);
622                  }                  }
623              }              }
624              if (outputRelease != 0) {              if (outputRelease != 0) {
625                  outputRelease = 0;                  outputRelease = 0;
626                  if (errorFile == outputFile) {                  if (errorFile == outputFile) {
627                      errorRelease = 1;                      errorRelease = 1;
628                  } else {                  } else {
629                      TclpReleaseFile(outputFile);                      TclpReleaseFile(outputFile);
630                  }                  }
631              }              }
632              outputFile = FileForRedirect(interp, p, atOK, argv[i],              outputFile = FileForRedirect(interp, p, atOK, argv[i],
633                      argv[i + 1], flags, &skip, &outputClose, &outputRelease);                      argv[i + 1], flags, &skip, &outputClose, &outputRelease);
634              if (outputFile == NULL) {              if (outputFile == NULL) {
635                  goto error;                  goto error;
636              }              }
637              if (errorToOutput) {              if (errorToOutput) {
638                  if (errorClose != 0) {                  if (errorClose != 0) {
639                      errorClose = 0;                      errorClose = 0;
640                      TclpCloseFile(errorFile);                      TclpCloseFile(errorFile);
641                  }                  }
642                  if (errorRelease != 0) {                  if (errorRelease != 0) {
643                      errorRelease = 0;                      errorRelease = 0;
644                      TclpReleaseFile(errorFile);                      TclpReleaseFile(errorFile);
645                  }                  }
646                  errorFile = outputFile;                  errorFile = outputFile;
647              }              }
648              break;              break;
649    
650          case '2':          case '2':
651              if (*p != '>') {              if (*p != '>') {
652                  break;                  break;
653              }              }
654              p++;              p++;
655              atOK = 1;              atOK = 1;
656              flags = O_WRONLY | O_CREAT | O_TRUNC;              flags = O_WRONLY | O_CREAT | O_TRUNC;
657              if (*p == '>') {              if (*p == '>') {
658                  p++;                  p++;
659                  atOK = 0;                  atOK = 0;
660                  flags = O_WRONLY | O_CREAT;                  flags = O_WRONLY | O_CREAT;
661              }              }
662              if (errorClose != 0) {              if (errorClose != 0) {
663                  errorClose = 0;                  errorClose = 0;
664                  TclpCloseFile(errorFile);                  TclpCloseFile(errorFile);
665              }              }
666              if (errorRelease != 0) {              if (errorRelease != 0) {
667                  errorRelease = 0;                  errorRelease = 0;
668                  TclpReleaseFile(errorFile);                  TclpReleaseFile(errorFile);
669              }              }
670              errorFile = FileForRedirect(interp, p, atOK, argv[i],              errorFile = FileForRedirect(interp, p, atOK, argv[i],
671                      argv[i + 1], flags, &skip, &errorClose, &errorRelease);                      argv[i + 1], flags, &skip, &errorClose, &errorRelease);
672              if (errorFile == NULL) {              if (errorFile == NULL) {
673                  goto error;                  goto error;
674              }              }
675              break;              break;
676          }          }
677    
678          if (skip != 0) {          if (skip != 0) {
679              for (j = i + skip; j < argc; j++) {              for (j = i + skip; j < argc; j++) {
680                  argv[j - skip] = argv[j];                  argv[j - skip] = argv[j];
681              }              }
682              argc -= skip;              argc -= skip;
683              i -= 1;              i -= 1;
684          }          }
685      }      }
686    
687      if (inputFile == NULL) {      if (inputFile == NULL) {
688          if (inputLiteral != NULL) {          if (inputLiteral != NULL) {
689              /*              /*
690               * The input for the first process is immediate data coming from               * The input for the first process is immediate data coming from
691               * Tcl.  Create a temporary file for it and put the data into the               * Tcl.  Create a temporary file for it and put the data into the
692               * file.               * file.
693               */               */
694              inputFile = TclpCreateTempFile(inputLiteral);              inputFile = TclpCreateTempFile(inputLiteral);
695              if (inputFile == NULL) {              if (inputFile == NULL) {
696                  Tcl_AppendResult(interp,                  Tcl_AppendResult(interp,
697                          "couldn't create input file for command: ",                          "couldn't create input file for command: ",
698                          Tcl_PosixError(interp), (char *) NULL);                          Tcl_PosixError(interp), (char *) NULL);
699                  goto error;                  goto error;
700              }              }
701              inputClose = 1;              inputClose = 1;
702          } else if (inPipePtr != NULL) {          } else if (inPipePtr != NULL) {
703              /*              /*
704               * The input for the first process in the pipeline is to               * The input for the first process in the pipeline is to
705               * come from a pipe that can be written from by the caller.               * come from a pipe that can be written from by the caller.
706               */               */
707    
708              if (TclpCreatePipe(&inputFile, inPipePtr) == 0) {              if (TclpCreatePipe(&inputFile, inPipePtr) == 0) {
709                  Tcl_AppendResult(interp,                  Tcl_AppendResult(interp,
710                          "couldn't create input pipe for command: ",                          "couldn't create input pipe for command: ",
711                          Tcl_PosixError(interp), (char *) NULL);                          Tcl_PosixError(interp), (char *) NULL);
712                  goto error;                  goto error;
713              }              }
714              inputClose = 1;              inputClose = 1;
715          } else {          } else {
716              /*              /*
717               * The input for the first process comes from stdin.               * The input for the first process comes from stdin.
718               */               */
719    
720              channel = Tcl_GetStdChannel(TCL_STDIN);              channel = Tcl_GetStdChannel(TCL_STDIN);
721              if (channel != NULL) {              if (channel != NULL) {
722                  inputFile = TclpMakeFile(channel, TCL_READABLE);                  inputFile = TclpMakeFile(channel, TCL_READABLE);
723                  if (inputFile != NULL) {                  if (inputFile != NULL) {
724                      inputRelease = 1;                      inputRelease = 1;
725                  }                  }
726              }              }
727          }          }
728      }      }
729    
730      if (outputFile == NULL) {      if (outputFile == NULL) {
731          if (outPipePtr != NULL) {          if (outPipePtr != NULL) {
732              /*              /*
733               * Output from the last process in the pipeline is to go to a               * Output from the last process in the pipeline is to go to a
734               * pipe that can be read by the caller.               * pipe that can be read by the caller.
735               */               */
736    
737              if (TclpCreatePipe(outPipePtr, &outputFile) == 0) {              if (TclpCreatePipe(outPipePtr, &outputFile) == 0) {
738                  Tcl_AppendResult(interp,                  Tcl_AppendResult(interp,
739                          "couldn't create output pipe for command: ",                          "couldn't create output pipe for command: ",
740                          Tcl_PosixError(interp), (char *) NULL);                          Tcl_PosixError(interp), (char *) NULL);
741                  goto error;                  goto error;
742              }              }
743              outputClose = 1;              outputClose = 1;
744          } else {          } else {
745              /*              /*
746               * The output for the last process goes to stdout.               * The output for the last process goes to stdout.
747               */               */
748    
749              channel = Tcl_GetStdChannel(TCL_STDOUT);              channel = Tcl_GetStdChannel(TCL_STDOUT);
750              if (channel) {              if (channel) {
751                  outputFile = TclpMakeFile(channel, TCL_WRITABLE);                  outputFile = TclpMakeFile(channel, TCL_WRITABLE);
752                  if (outputFile != NULL) {                  if (outputFile != NULL) {
753                      outputRelease = 1;                      outputRelease = 1;
754                  }                  }
755              }              }
756          }          }
757      }      }
758    
759      if (errorFile == NULL) {      if (errorFile == NULL) {
760          if (errFilePtr != NULL) {          if (errFilePtr != NULL) {
761              /*              /*
762               * Set up the standard error output sink for the pipeline, if               * Set up the standard error output sink for the pipeline, if
763               * requested.  Use a temporary file which is opened, then deleted.               * requested.  Use a temporary file which is opened, then deleted.
764               * Could potentially just use pipe, but if it filled up it could               * Could potentially just use pipe, but if it filled up it could
765               * cause the pipeline to deadlock:  we'd be waiting for processes               * cause the pipeline to deadlock:  we'd be waiting for processes
766               * to complete before reading stderr, and processes couldn't               * to complete before reading stderr, and processes couldn't
767               * complete because stderr was backed up.               * complete because stderr was backed up.
768               */               */
769    
770              errorFile = TclpCreateTempFile(NULL);              errorFile = TclpCreateTempFile(NULL);
771              if (errorFile == NULL) {              if (errorFile == NULL) {
772                  Tcl_AppendResult(interp,                  Tcl_AppendResult(interp,
773                          "couldn't create error file for command: ",                          "couldn't create error file for command: ",
774                          Tcl_PosixError(interp), (char *) NULL);                          Tcl_PosixError(interp), (char *) NULL);
775                  goto error;                  goto error;
776              }              }
777              *errFilePtr = errorFile;              *errFilePtr = errorFile;
778          } else {          } else {
779              /*              /*
780               * Errors from the pipeline go to stderr.               * Errors from the pipeline go to stderr.
781               */               */
782    
783              channel = Tcl_GetStdChannel(TCL_STDERR);              channel = Tcl_GetStdChannel(TCL_STDERR);
784              if (channel) {              if (channel) {
785                  errorFile = TclpMakeFile(channel, TCL_WRITABLE);                  errorFile = TclpMakeFile(channel, TCL_WRITABLE);
786                  if (errorFile != NULL) {                  if (errorFile != NULL) {
787                      errorRelease = 1;                      errorRelease = 1;
788                  }                  }
789              }              }
790          }          }
791      }      }
792                    
793      /*      /*
794       * Scan through the argc array, creating a process for each       * Scan through the argc array, creating a process for each
795       * group of arguments between the "|" characters.       * group of arguments between the "|" characters.
796       */       */
797    
798      Tcl_ReapDetachedProcs();      Tcl_ReapDetachedProcs();
799      pidPtr = (Tcl_Pid *) ckalloc((unsigned) (cmdCount * sizeof(Tcl_Pid)));      pidPtr = (Tcl_Pid *) ckalloc((unsigned) (cmdCount * sizeof(Tcl_Pid)));
800    
801      curInFile = inputFile;      curInFile = inputFile;
802    
803      for (i = 0; i < argc; i = lastArg + 1) {      for (i = 0; i < argc; i = lastArg + 1) {
804          int result, joinThisError;          int result, joinThisError;
805          Tcl_Pid pid;          Tcl_Pid pid;
806          char *oldName;          char *oldName;
807    
808          /*          /*
809           * Convert the program name into native form.           * Convert the program name into native form.
810           */           */
811    
812          if (Tcl_TranslateFileName(interp, argv[i], &execBuffer) == NULL) {          if (Tcl_TranslateFileName(interp, argv[i], &execBuffer) == NULL) {
813              goto error;              goto error;
814          }          }
815    
816          /*          /*
817           * Find the end of the current segment of the pipeline.           * Find the end of the current segment of the pipeline.
818           */           */
819    
820          joinThisError = 0;          joinThisError = 0;
821          for (lastArg = i; lastArg < argc; lastArg++) {          for (lastArg = i; lastArg < argc; lastArg++) {
822              if (argv[lastArg][0] == '|') {              if (argv[lastArg][0] == '|') {
823                  if (argv[lastArg][1] == '\0') {                  if (argv[lastArg][1] == '\0') {
824                      break;                      break;
825                  }                  }
826                  if ((argv[lastArg][1] == '&') && (argv[lastArg][2] == '\0')) {                  if ((argv[lastArg][1] == '&') && (argv[lastArg][2] == '\0')) {
827                      joinThisError = 1;                      joinThisError = 1;
828                      break;                      break;
829                  }                  }
830              }              }
831          }          }
832          argv[lastArg] = NULL;          argv[lastArg] = NULL;
833    
834          /*          /*
835           * If this is the last segment, use the specified outputFile.           * If this is the last segment, use the specified outputFile.
836           * Otherwise create an intermediate pipe.  pipeIn will become the           * Otherwise create an intermediate pipe.  pipeIn will become the
837           * curInFile for the next segment of the pipe.           * curInFile for the next segment of the pipe.
838           */           */
839    
840          if (lastArg == argc) {          if (lastArg == argc) {
841              curOutFile = outputFile;              curOutFile = outputFile;
842          } else {          } else {
843              if (TclpCreatePipe(&pipeIn, &curOutFile) == 0) {              if (TclpCreatePipe(&pipeIn, &curOutFile) == 0) {
844                  Tcl_AppendResult(interp, "couldn't create pipe: ",                  Tcl_AppendResult(interp, "couldn't create pipe: ",
845                          Tcl_PosixError(interp), (char *) NULL);                          Tcl_PosixError(interp), (char *) NULL);
846                  goto error;                  goto error;
847              }              }
848          }          }
849    
850          if (joinThisError != 0) {          if (joinThisError != 0) {
851              curErrFile = curOutFile;              curErrFile = curOutFile;
852          } else {          } else {
853              curErrFile = errorFile;              curErrFile = errorFile;
854          }          }
855    
856          /*          /*
857           * Restore argv[i], since a caller wouldn't expect the contents of           * Restore argv[i], since a caller wouldn't expect the contents of
858           * argv to be modified.           * argv to be modified.
859           */           */
860                    
861          oldName = argv[i];          oldName = argv[i];
862          argv[i] = Tcl_DStringValue(&execBuffer);          argv[i] = Tcl_DStringValue(&execBuffer);
863          result = TclpCreateProcess(interp, lastArg - i, argv + i,          result = TclpCreateProcess(interp, lastArg - i, argv + i,
864                  curInFile, curOutFile, curErrFile, &pid);                  curInFile, curOutFile, curErrFile, &pid);
865          argv[i] = oldName;          argv[i] = oldName;
866          if (result != TCL_OK) {          if (result != TCL_OK) {
867              goto error;              goto error;
868          }          }
869          Tcl_DStringFree(&execBuffer);          Tcl_DStringFree(&execBuffer);
870    
871          pidPtr[numPids] = pid;          pidPtr[numPids] = pid;
872          numPids++;          numPids++;
873    
874          /*          /*
875           * Close off our copies of file descriptors that were set up for           * Close off our copies of file descriptors that were set up for
876           * this child, then set up the input for the next child.           * this child, then set up the input for the next child.
877           */           */
878    
879          if ((curInFile != NULL) && (curInFile != inputFile)) {          if ((curInFile != NULL) && (curInFile != inputFile)) {
880              TclpCloseFile(curInFile);              TclpCloseFile(curInFile);
881          }          }
882          curInFile = pipeIn;          curInFile = pipeIn;
883          pipeIn = NULL;          pipeIn = NULL;
884    
885          if ((curOutFile != NULL) && (curOutFile != outputFile)) {          if ((curOutFile != NULL) && (curOutFile != outputFile)) {
886              TclpCloseFile(curOutFile);              TclpCloseFile(curOutFile);
887          }          }
888          curOutFile = NULL;          curOutFile = NULL;
889      }      }
890    
891      *pidArrayPtr = pidPtr;      *pidArrayPtr = pidPtr;
892    
893      /*      /*
894       * All done.  Cleanup open files lying around and then return.       * All done.  Cleanup open files lying around and then return.
895       */       */
896    
897  cleanup:  cleanup:
898      Tcl_DStringFree(&execBuffer);      Tcl_DStringFree(&execBuffer);
899    
900      if (inputClose) {      if (inputClose) {
901          TclpCloseFile(inputFile);          TclpCloseFile(inputFile);
902      } else if (inputRelease) {      } else if (inputRelease) {
903          TclpReleaseFile(inputFile);          TclpReleaseFile(inputFile);
904      }      }
905      if (outputClose) {      if (outputClose) {
906          TclpCloseFile(outputFile);          TclpCloseFile(outputFile);
907      } else if (outputRelease) {      } else if (outputRelease) {
908          TclpReleaseFile(outputFile);          TclpReleaseFile(outputFile);
909      }      }
910      if (errorClose) {      if (errorClose) {
911          TclpCloseFile(errorFile);          TclpCloseFile(errorFile);
912      } else if (errorRelease) {      } else if (errorRelease) {
913          TclpReleaseFile(errorFile);          TclpReleaseFile(errorFile);
914      }      }
915      return numPids;      return numPids;
916    
917      /*      /*
918       * An error occurred.  There could have been extra files open, such       * An error occurred.  There could have been extra files open, such
919       * as pipes between children.  Clean them all up.  Detach any child       * as pipes between children.  Clean them all up.  Detach any child
920       * processes that have been created.       * processes that have been created.
921       */       */
922    
923  error:  error:
924      if (pipeIn != NULL) {      if (pipeIn != NULL) {
925          TclpCloseFile(pipeIn);          TclpCloseFile(pipeIn);
926      }      }
927      if ((curOutFile != NULL) && (curOutFile != outputFile)) {      if ((curOutFile != NULL) && (curOutFile != outputFile)) {
928          TclpCloseFile(curOutFile);          TclpCloseFile(curOutFile);
929      }      }
930      if ((curInFile != NULL) && (curInFile != inputFile)) {      if ((curInFile != NULL) && (curInFile != inputFile)) {
931          TclpCloseFile(curInFile);          TclpCloseFile(curInFile);
932      }      }
933      if ((inPipePtr != NULL) && (*inPipePtr != NULL)) {      if ((inPipePtr != NULL) && (*inPipePtr != NULL)) {
934          TclpCloseFile(*inPipePtr);          TclpCloseFile(*inPipePtr);
935          *inPipePtr = NULL;          *inPipePtr = NULL;
936      }      }
937      if ((outPipePtr != NULL) && (*outPipePtr != NULL)) {      if ((outPipePtr != NULL) && (*outPipePtr != NULL)) {
938          TclpCloseFile(*outPipePtr);          TclpCloseFile(*outPipePtr);
939          *outPipePtr = NULL;          *outPipePtr = NULL;
940      }      }
941      if ((errFilePtr != NULL) && (*errFilePtr != NULL)) {      if ((errFilePtr != NULL) && (*errFilePtr != NULL)) {
942          TclpCloseFile(*errFilePtr);          TclpCloseFile(*errFilePtr);
943          *errFilePtr = NULL;          *errFilePtr = NULL;
944      }      }
945      if (pidPtr != NULL) {      if (pidPtr != NULL) {
946          for (i = 0; i < numPids; i++) {          for (i = 0; i < numPids; i++) {
947              if (pidPtr[i] != (Tcl_Pid) -1) {              if (pidPtr[i] != (Tcl_Pid) -1) {
948                  Tcl_DetachPids(1, &pidPtr[i]);                  Tcl_DetachPids(1, &pidPtr[i]);
949              }              }
950          }          }
951          ckfree((char *) pidPtr);          ckfree((char *) pidPtr);
952      }      }
953      numPids = -1;      numPids = -1;
954      goto cleanup;      goto cleanup;
955  }  }
956    
957  /*  /*
958   *----------------------------------------------------------------------   *----------------------------------------------------------------------
959   *   *
960   * Tcl_OpenCommandChannel --   * Tcl_OpenCommandChannel --
961   *   *
962   *      Opens an I/O channel to one or more subprocesses specified   *      Opens an I/O channel to one or more subprocesses specified
963   *      by argc and argv.  The flags argument determines the   *      by argc and argv.  The flags argument determines the
964   *      disposition of the stdio handles.  If the TCL_STDIN flag is   *      disposition of the stdio handles.  If the TCL_STDIN flag is
965   *      set then the standard input for the first subprocess will   *      set then the standard input for the first subprocess will
966   *      be tied to the channel:  writing to the channel will provide   *      be tied to the channel:  writing to the channel will provide
967   *      input to the subprocess.  If TCL_STDIN is not set, then   *      input to the subprocess.  If TCL_STDIN is not set, then
968   *      standard input for the first subprocess will be the same as   *      standard input for the first subprocess will be the same as
969   *      this application's standard input.  If TCL_STDOUT is set then   *      this application's standard input.  If TCL_STDOUT is set then
970   *      standard output from the last subprocess can be read from the   *      standard output from the last subprocess can be read from the
971   *      channel;  otherwise it goes to this application's standard   *      channel;  otherwise it goes to this application's standard
972   *      output.  If TCL_STDERR is set, standard error output for all   *      output.  If TCL_STDERR is set, standard error output for all
973   *      subprocesses is returned to the channel and results in an error   *      subprocesses is returned to the channel and results in an error
974   *      when the channel is closed;  otherwise it goes to this   *      when the channel is closed;  otherwise it goes to this
975   *      application's standard error.  If TCL_ENFORCE_MODE is not set,   *      application's standard error.  If TCL_ENFORCE_MODE is not set,
976   *      then argc and argv can redirect the stdio handles to override   *      then argc and argv can redirect the stdio handles to override
977   *      TCL_STDIN, TCL_STDOUT, and TCL_STDERR;  if it is set, then it   *      TCL_STDIN, TCL_STDOUT, and TCL_STDERR;  if it is set, then it
978   *      is an error for argc and argv to override stdio channels for   *      is an error for argc and argv to override stdio channels for
979   *      which TCL_STDIN, TCL_STDOUT, and TCL_STDERR have been set.   *      which TCL_STDIN, TCL_STDOUT, and TCL_STDERR have been set.
980   *   *
981   * Results:   * Results:
982   *      A new command channel, or NULL on failure with an error   *      A new command channel, or NULL on failure with an error
983   *      message left in interp.   *      message left in interp.
984   *   *
985   * Side effects:   * Side effects:
986   *      Creates processes, opens pipes.   *      Creates processes, opens pipes.
987   *   *
988   *----------------------------------------------------------------------   *----------------------------------------------------------------------
989   */   */
990    
991  Tcl_Channel  Tcl_Channel
992  Tcl_OpenCommandChannel(interp, argc, argv, flags)  Tcl_OpenCommandChannel(interp, argc, argv, flags)
993      Tcl_Interp *interp;         /* Interpreter for error reporting. Can      Tcl_Interp *interp;         /* Interpreter for error reporting. Can
994                                   * NOT be NULL. */                                   * NOT be NULL. */
995      int argc;                   /* How many arguments. */      int argc;                   /* How many arguments. */
996      char **argv;                /* Array of arguments for command pipe. */      char **argv;                /* Array of arguments for command pipe. */
997      int flags;                  /* Or'ed combination of TCL_STDIN, TCL_STDOUT,      int flags;                  /* Or'ed combination of TCL_STDIN, TCL_STDOUT,
998                                   * TCL_STDERR, and TCL_ENFORCE_MODE. */                                   * TCL_STDERR, and TCL_ENFORCE_MODE. */
999  {  {
1000      TclFile *inPipePtr, *outPipePtr, *errFilePtr;      TclFile *inPipePtr, *outPipePtr, *errFilePtr;
1001      TclFile inPipe, outPipe, errFile;      TclFile inPipe, outPipe, errFile;
1002      int numPids;      int numPids;
1003      Tcl_Pid *pidPtr;      Tcl_Pid *pidPtr;
1004      Tcl_Channel channel;      Tcl_Channel channel;
1005    
1006      inPipe = outPipe = errFile = NULL;      inPipe = outPipe = errFile = NULL;
1007    
1008      inPipePtr = (flags & TCL_STDIN) ? &inPipe : NULL;      inPipePtr = (flags & TCL_STDIN) ? &inPipe : NULL;
1009      outPipePtr = (flags & TCL_STDOUT) ? &outPipe : NULL;      outPipePtr = (flags & TCL_STDOUT) ? &outPipe : NULL;
1010      errFilePtr = (flags & TCL_STDERR) ? &errFile : NULL;      errFilePtr = (flags & TCL_STDERR) ? &errFile : NULL;
1011            
1012      numPids = TclCreatePipeline(interp, argc, argv, &pidPtr, inPipePtr,      numPids = TclCreatePipeline(interp, argc, argv, &pidPtr, inPipePtr,
1013              outPipePtr, errFilePtr);              outPipePtr, errFilePtr);
1014    
1015      if (numPids < 0) {      if (numPids < 0) {
1016          goto error;          goto error;
1017      }      }
1018    
1019      /*      /*
1020       * Verify that the pipes that were created satisfy the       * Verify that the pipes that were created satisfy the
1021       * readable/writable constraints.       * readable/writable constraints.
1022       */       */
1023    
1024      if (flags & TCL_ENFORCE_MODE) {      if (flags & TCL_ENFORCE_MODE) {
1025          if ((flags & TCL_STDOUT) && (outPipe == NULL)) {          if ((flags & TCL_STDOUT) && (outPipe == NULL)) {
1026              Tcl_AppendResult(interp, "can't read output from command:",              Tcl_AppendResult(interp, "can't read output from command:",
1027                      " standard output was redirected", (char *) NULL);                      " standard output was redirected", (char *) NULL);
1028              goto error;              goto error;
1029          }          }
1030          if ((flags & TCL_STDIN) && (inPipe == NULL)) {          if ((flags & TCL_STDIN) && (inPipe == NULL)) {
1031              Tcl_AppendResult(interp, "can't write input to command:",              Tcl_AppendResult(interp, "can't write input to command:",
1032                      " standard input was redirected", (char *) NULL);                      " standard input was redirected", (char *) NULL);
1033              goto error;              goto error;
1034          }          }
1035      }      }
1036            
1037      channel = TclpCreateCommandChannel(outPipe, inPipe, errFile,      channel = TclpCreateCommandChannel(outPipe, inPipe, errFile,
1038              numPids, pidPtr);              numPids, pidPtr);
1039    
1040      if (channel == (Tcl_Channel) NULL) {      if (channel == (Tcl_Channel) NULL) {
1041          Tcl_AppendResult(interp, "pipe for command could not be created",          Tcl_AppendResult(interp, "pipe for command could not be created",
1042                  (char *) NULL);                  (char *) NULL);
1043          goto error;          goto error;
1044      }      }
1045      return channel;      return channel;
1046    
1047  error:  error:
1048      if (numPids > 0) {      if (numPids > 0) {
1049          Tcl_DetachPids(numPids, pidPtr);          Tcl_DetachPids(numPids, pidPtr);
1050          ckfree((char *) pidPtr);          ckfree((char *) pidPtr);
1051      }      }
1052      if (inPipe != NULL) {      if (inPipe != NULL) {
1053          TclpCloseFile(inPipe);          TclpCloseFile(inPipe);
1054      }      }
1055      if (outPipe != NULL) {      if (outPipe != NULL) {
1056          TclpCloseFile(outPipe);          TclpCloseFile(outPipe);
1057      }      }
1058      if (errFile != NULL) {      if (errFile != NULL) {
1059          TclpCloseFile(errFile);          TclpCloseFile(errFile);
1060      }      }
1061      return NULL;      return NULL;
1062  }  }
1063    
1064  /* End of tclpipe.c */  /* End of tclpipe.c */

Legend:
Removed from v.64  
changed lines
  Added in v.71

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25