2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $XConsortium: tclIOUtil.c /main/3 1996/10/03 17:17:59 drk $ */
27 * This file contains a collection of utility procedures that
28 * are shared by the platform specific IO drivers.
30 * Parts of this file are based on code contributed by Karl
31 * Lehenbauer, Mark Diekhans and Peter da Silva.
33 * Copyright (c) 1991-1994 The Regents of the University of California.
34 * Copyright (c) 1994-1996 Sun Microsystems, Inc.
36 * See the file "license.terms" for information on usage and redistribution
37 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
39 * SCCS: @(#) tclIOUtil.c 1.122 96/04/02 18:46:40
46 * A linked list of the following structures is used to keep track
47 * of child processes that have been detached but haven't exited
48 * yet, so we can make sure that they're properly "reaped" (officially
49 * waited for) and don't lie around as zombies cluttering the
53 typedef struct Detached {
54 pid_t pid; /* Id of process that's been detached
55 * but isn't known to have exited. */
56 struct Detached *nextPtr; /* Next in list of all detached
60 static Detached *detList = NULL; /* List of all detached proceses. */
63 * Declarations for local procedures defined in this file:
66 static Tcl_File FileForRedirect _ANSI_ARGS_((Tcl_Interp *interp,
67 char *spec, int atOk, char *arg, int flags,
68 char *nextArg, int *skipPtr, int *closePtr));
71 *----------------------------------------------------------------------
75 * This procedure does much of the work of parsing redirection
76 * operators. It handles "@" if specified and allowed, and a file
77 * name, and opens the file if necessary.
80 * The return value is the descriptor number for the file. If an
81 * error occurs then NULL is returned and an error message is left
82 * in interp->result. Several arguments are side-effected; see
83 * the argument list below for details.
88 *----------------------------------------------------------------------
92 FileForRedirect(interp, spec, atOk, arg, flags, nextArg, skipPtr, closePtr)
93 Tcl_Interp *interp; /* Intepreter to use for error
95 register char *spec; /* Points to character just after
96 * redirection character. */
97 int atOk; /* Non-zero means '@' notation is
98 * OK, zero means it isn't. */
99 char *arg; /* Pointer to entire argument
100 * containing spec: used for error
102 int flags; /* Flags to use for opening file. */
103 char *nextArg; /* Next argument in argc/argv
104 * array, if needed for file name.
106 int *skipPtr; /* This value is incremented if
107 * nextArg is used for redirection
109 int *closePtr; /* This value is set to 1 if the file
110 * that's returned must be closed, 0
111 * if it was specified with "@" so
112 * it must be left open. */
114 int writing = (flags & O_WRONLY);
118 if (atOk && (*spec == '@')) {
127 chan = Tcl_GetChannel(interp, spec, NULL);
128 if (chan == (Tcl_Channel) NULL) {
132 file = Tcl_GetChannelFile(chan, writing ? TCL_WRITABLE : TCL_READABLE);
134 Tcl_AppendResult(interp,
136 Tcl_GetChannelName(chan),
137 "\" wasn't opened for ",
138 writing ? "writing" : "reading", (char *) NULL);
144 * Be sure to flush output to the file, so that anything
145 * written by the child appears after stuff we've already
162 name = Tcl_TranslateFileName(interp, spec, &buffer);
164 file = TclOpenFile(name, flags);
168 Tcl_DStringFree(&buffer);
170 Tcl_AppendResult(interp, "couldn't ",
171 (writing) ? "write" : "read", " file \"", spec, "\": ",
172 Tcl_PosixError(interp), (char *) NULL);
180 Tcl_AppendResult(interp, "can't specify \"", arg,
181 "\" as last word in command", (char *) NULL);
186 *----------------------------------------------------------------------
191 * Computes a POSIX mode mask for opening a file, from a given string,
192 * and also sets a flag to indicate whether the caller should seek to
193 * EOF after opening the file.
196 * On success, returns mode to pass to "open". If an error occurs, the
197 * returns -1 and if interp is not NULL, sets interp->result to an
201 * Sets the integer referenced by seekFlagPtr to 1 to tell the caller
202 * to seek to EOF after opening the file.
205 * This code is based on a prototype implementation contributed
208 *----------------------------------------------------------------------
212 TclGetOpenMode(interp, string, seekFlagPtr)
213 Tcl_Interp *interp; /* Interpreter to use for error
214 * reporting - may be NULL. */
215 char *string; /* Mode string, e.g. "r+" or
217 int *seekFlagPtr; /* Set this to 1 if the caller
218 * should seek to EOF during the
219 * opening of the file. */
221 int mode, modeArgc, c, i, gotRW;
222 char **modeArgv, *flag;
223 #define RW_MODES (O_RDONLY|O_WRONLY|O_RDWR)
226 * Check for the simpler fopen-like access modes (e.g. "r"). They
227 * are distinguished from the POSIX access modes by the presence
228 * of a lower-case first letter.
233 if (islower(UCHAR(string[0]))) {
239 mode = O_WRONLY|O_CREAT|O_TRUNC;
242 mode = O_WRONLY|O_CREAT;
247 if (interp != (Tcl_Interp *) NULL) {
248 Tcl_AppendResult(interp,
249 "illegal access mode \"", string, "\"",
254 if (string[1] == '+') {
255 mode &= ~(O_RDONLY|O_WRONLY);
257 if (string[2] != 0) {
260 } else if (string[1] != 0) {
267 * The access modes are specified using a list of POSIX modes
270 * IMPORTANT NOTE: We rely on Tcl_SplitList working correctly when
271 * a NULL interpreter is passed in.
274 if (Tcl_SplitList(interp, string, &modeArgc, &modeArgv) != TCL_OK) {
275 if (interp != (Tcl_Interp *) NULL) {
276 Tcl_AddErrorInfo(interp,
277 "\n while processing open access modes \"");
278 Tcl_AddErrorInfo(interp, string);
279 Tcl_AddErrorInfo(interp, "\"");
285 for (i = 0; i < modeArgc; i++) {
288 if ((c == 'R') && (strcmp(flag, "RDONLY") == 0)) {
289 mode = (mode & ~RW_MODES) | O_RDONLY;
291 } else if ((c == 'W') && (strcmp(flag, "WRONLY") == 0)) {
292 mode = (mode & ~RW_MODES) | O_WRONLY;
294 } else if ((c == 'R') && (strcmp(flag, "RDWR") == 0)) {
295 mode = (mode & ~RW_MODES) | O_RDWR;
297 } else if ((c == 'A') && (strcmp(flag, "APPEND") == 0)) {
300 } else if ((c == 'C') && (strcmp(flag, "CREAT") == 0)) {
302 } else if ((c == 'E') && (strcmp(flag, "EXCL") == 0)) {
304 } else if ((c == 'N') && (strcmp(flag, "NOCTTY") == 0)) {
308 if (interp != (Tcl_Interp *) NULL) {
309 Tcl_AppendResult(interp, "access mode \"", flag,
310 "\" not supported by this system", (char *) NULL);
312 ckfree((char *) modeArgv);
315 } else if ((c == 'N') && (strcmp(flag, "NONBLOCK") == 0)) {
316 #if defined(O_NDELAY) || defined(O_NONBLOCK)
323 if (interp != (Tcl_Interp *) NULL) {
324 Tcl_AppendResult(interp, "access mode \"", flag,
325 "\" not supported by this system", (char *) NULL);
327 ckfree((char *) modeArgv);
330 } else if ((c == 'T') && (strcmp(flag, "TRUNC") == 0)) {
333 if (interp != (Tcl_Interp *) NULL) {
334 Tcl_AppendResult(interp, "invalid access mode \"", flag,
335 "\": must be RDONLY, WRONLY, RDWR, APPEND, CREAT",
336 " EXCL, NOCTTY, NONBLOCK, or TRUNC", (char *) NULL);
338 ckfree((char *) modeArgv);
342 ckfree((char *) modeArgv);
344 if (interp != (Tcl_Interp *) NULL) {
345 Tcl_AppendResult(interp, "access mode must include either",
346 " RDONLY, WRONLY, or RDWR", (char *) NULL);
354 *----------------------------------------------------------------------
358 * Read in a file and process the entire file as one gigantic
362 * A standard Tcl result, which is either the result of executing
363 * the file or an error indicating why the file couldn't be read.
366 * Depends on the commands in the file.
368 *----------------------------------------------------------------------
372 Tcl_EvalFile(interp, fileName)
373 Tcl_Interp *interp; /* Interpreter in which to process file. */
374 char *fileName; /* Name of file to process. Tilde-substitution
375 * will be performed on this name. */
379 char *cmdBuffer = (char *) NULL;
380 char *oldScriptFile = (char *) NULL;
381 Interp *iPtr = (Interp *) interp;
383 char *nativeName = (char *) NULL;
384 Tcl_Channel chan = (Tcl_Channel) NULL;
386 Tcl_ResetResult(interp);
387 oldScriptFile = iPtr->scriptFile;
388 iPtr->scriptFile = fileName;
389 Tcl_DStringInit(&buffer);
390 nativeName = Tcl_TranslateFileName(interp, fileName, &buffer);
391 if (nativeName == NULL) {
396 * If Tcl_TranslateFileName didn't already copy the file name, do it
397 * here. This way we don't depend on fileName staying constant
398 * throughout the execution of the script (e.g., what if it happens
399 * to point to a Tcl variable that the script could change?).
402 if (nativeName != Tcl_DStringValue(&buffer)) {
403 Tcl_DStringSetLength(&buffer, 0);
404 Tcl_DStringAppend(&buffer, nativeName, -1);
405 nativeName = Tcl_DStringValue(&buffer);
407 if (stat(nativeName, &statBuf) == -1) {
409 Tcl_AppendResult(interp, "couldn't read file \"", fileName,
410 "\": ", Tcl_PosixError(interp), (char *) NULL);
413 chan = Tcl_OpenFileChannel(interp, nativeName, "r", 0644);
414 if (chan == (Tcl_Channel) NULL) {
415 Tcl_ResetResult(interp);
416 Tcl_AppendResult(interp, "couldn't read file \"", fileName,
417 "\": ", Tcl_PosixError(interp), (char *) NULL);
420 cmdBuffer = (char *) ckalloc((unsigned) statBuf.st_size+1);
421 result = Tcl_Read(chan, cmdBuffer, statBuf.st_size);
423 Tcl_Close(interp, chan);
424 Tcl_AppendResult(interp, "couldn't read file \"", fileName,
425 "\": ", Tcl_PosixError(interp), (char *) NULL);
428 cmdBuffer[result] = 0;
429 if (Tcl_Close(interp, chan) != TCL_OK) {
433 result = Tcl_Eval(interp, cmdBuffer);
434 if (result == TCL_RETURN) {
435 result = TclUpdateReturnInfo(iPtr);
436 } else if (result == TCL_ERROR) {
440 * Record information telling where the error occurred.
443 sprintf(msg, "\n (file \"%.150s\" line %d)", fileName,
445 Tcl_AddErrorInfo(interp, msg);
447 iPtr->scriptFile = oldScriptFile;
449 Tcl_DStringFree(&buffer);
453 if (cmdBuffer != (char *) NULL) {
456 iPtr->scriptFile = oldScriptFile;
457 Tcl_DStringFree(&buffer);
462 *----------------------------------------------------------------------
466 * This procedure is called to indicate that one or more child
467 * processes have been placed in background and will never be
468 * waited for; they should eventually be reaped by
469 * Tcl_ReapDetachedProcs.
477 *----------------------------------------------------------------------
481 Tcl_DetachPids(numPids, pidPtr)
482 int numPids; /* Number of pids to detach: gives size
483 * of array pointed to by pidPtr. */
484 pid_t *pidPtr; /* Array of pids to detach. */
486 register Detached *detPtr;
489 for (i = 0; i < numPids; i++) {
490 detPtr = (Detached *) ckalloc(sizeof(Detached));
491 detPtr->pid = pidPtr[i];
492 detPtr->nextPtr = detList;
498 *----------------------------------------------------------------------
500 * Tcl_ReapDetachedProcs --
502 * This procedure checks to see if any detached processes have
503 * exited and, if so, it "reaps" them by officially waiting on
504 * them. It should be called "occasionally" to make sure that
505 * all detached processes are eventually reaped.
511 * Processes are waited on, so that they can be reaped by the
514 *----------------------------------------------------------------------
518 Tcl_ReapDetachedProcs()
520 register Detached *detPtr;
521 Detached *nextPtr, *prevPtr;
525 for (detPtr = detList, prevPtr = NULL; detPtr != NULL; ) {
526 pid = Tcl_WaitPid(detPtr->pid, &status, WNOHANG);
527 if ((pid == 0) || ((pid == (pid_t)-1) && (errno != ECHILD))) {
529 detPtr = detPtr->nextPtr;
532 nextPtr = detPtr->nextPtr;
533 if (prevPtr == NULL) {
534 detList = detPtr->nextPtr;
536 prevPtr->nextPtr = detPtr->nextPtr;
538 ckfree((char *) detPtr);
544 *----------------------------------------------------------------------
546 * TclCleanupChildren --
548 * This is a utility procedure used to wait for child processes
549 * to exit, record information about abnormal exits, and then
550 * collect any stderr output generated by them.
553 * The return value is a standard Tcl result. If anything at
554 * weird happened with the child processes, TCL_ERROR is returned
555 * and a message is left in interp->result.
558 * If the last character of interp->result is a newline, then it
559 * is removed unless keepNewline is non-zero. File errorId gets
560 * closed, and pidPtr is freed back to the storage allocator.
562 *----------------------------------------------------------------------
566 TclCleanupChildren(interp, numPids, pidPtr, errorChan)
567 Tcl_Interp *interp; /* Used for error messages. */
568 int numPids; /* Number of entries in pidPtr array. */
569 pid_t *pidPtr; /* Array of process ids of children. */
570 Tcl_Channel errorChan; /* Channel for file containing stderr output
571 * from pipeline. NULL means there isn't any
576 int i, abnormalExit, anyErrorInfo;
577 WAIT_STATUS_TYPE waitStatus;
581 for (i = 0; i < numPids; i++) {
582 pid = Tcl_WaitPid(pidPtr[i], (int *) &waitStatus, 0);
583 if (pid == (pid_t)-1) {
585 if (interp != (Tcl_Interp *) NULL) {
586 msg = Tcl_PosixError(interp);
587 if (errno == ECHILD) {
589 * This changeup in message suggested by Mark Diekhans
590 * to remind people that ECHILD errors can occur on
591 * some systems if SIGCHLD isn't in its default state.
595 "child process lost (is SIGCHLD ignored or trapped?)";
597 Tcl_AppendResult(interp, "error waiting for process to exit: ",
604 * Create error messages for unusual process exits. An
605 * extra newline gets appended to each error message, but
606 * it gets removed below (in the same fashion that an
607 * extra newline in the command's output is removed).
610 if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) {
611 char msg1[20], msg2[20];
614 sprintf(msg1, "%ld", (long)pid);
615 if (WIFEXITED(waitStatus)) {
616 if (interp != (Tcl_Interp *) NULL) {
617 sprintf(msg2, "%d", WEXITSTATUS(waitStatus));
618 Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2,
622 } else if (WIFSIGNALED(waitStatus)) {
623 if (interp != (Tcl_Interp *) NULL) {
626 p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus)));
627 Tcl_SetErrorCode(interp, "CHILDKILLED", msg1,
628 Tcl_SignalId((int) (WTERMSIG(waitStatus))), p,
630 Tcl_AppendResult(interp, "child killed: ", p, "\n",
633 } else if (WIFSTOPPED(waitStatus)) {
634 if (interp != (Tcl_Interp *) NULL) {
637 p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus)));
638 Tcl_SetErrorCode(interp, "CHILDSUSP", msg1,
639 Tcl_SignalId((int) (WSTOPSIG(waitStatus))),
641 Tcl_AppendResult(interp, "child suspended: ", p, "\n",
645 if (interp != (Tcl_Interp *) NULL) {
646 Tcl_AppendResult(interp,
647 "child wait status didn't make sense\n",
655 * Read the standard error file. If there's anything there,
656 * then return an error and add the file's contents to the result
661 if (errorChan != NULL) {
664 * Make sure we start at the beginning of the file.
667 Tcl_Seek(errorChan, 0L, SEEK_SET);
669 if (interp != (Tcl_Interp *) NULL) {
671 #define BUFFER_SIZE 1000
672 char buffer[BUFFER_SIZE+1];
675 count = Tcl_Read(errorChan, buffer, BUFFER_SIZE);
681 Tcl_AppendResult(interp,
682 "error reading stderr output file: ",
683 Tcl_PosixError(interp), (char *) NULL);
684 break; /* out of the "while (1)" loop. */
687 Tcl_AppendResult(interp, buffer, (char *) NULL);
692 Tcl_Close(NULL, errorChan);
696 * If a child exited abnormally but didn't output any error information
697 * at all, generate an error message here.
700 if (abnormalExit && !anyErrorInfo && (interp != (Tcl_Interp *) NULL)) {
701 Tcl_AppendResult(interp, "child process exited abnormally",
709 *----------------------------------------------------------------------
711 * TclCreatePipeline --
713 * Given an argc/argv array, instantiate a pipeline of processes
714 * as described by the argv.
717 * The return value is a count of the number of new processes
718 * created, or -1 if an error occurred while creating the pipeline.
719 * *pidArrayPtr is filled in with the address of a dynamically
720 * allocated array giving the ids of all of the processes. It
721 * is up to the caller to free this array when it isn't needed
722 * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in
723 * with the file id for the input pipe for the pipeline (if any):
724 * the caller must eventually close this file. If outPipePtr
725 * isn't NULL, then *outPipePtr is filled in with the file id
726 * for the output pipe from the pipeline: the caller must close
727 * this file. If errFilePtr isn't NULL, then *errFilePtr is filled
728 * with a file id that may be used to read error output after the
729 * pipeline completes.
732 * Processes and pipes are created.
734 *----------------------------------------------------------------------
738 TclCreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,
739 outPipePtr, errFilePtr)
740 Tcl_Interp *interp; /* Interpreter to use for error reporting. */
741 int argc; /* Number of entries in argv. */
742 char **argv; /* Array of strings describing commands in
743 * pipeline plus I/O redirection with <,
744 * <<, >, etc. Argv[argc] must be NULL. */
745 pid_t **pidArrayPtr; /* Word at *pidArrayPtr gets filled in with
746 * address of array of pids for processes
747 * in pipeline (first pid is first process
749 Tcl_File *inPipePtr; /* If non-NULL, input to the pipeline comes
750 * from a pipe (unless overridden by
751 * redirection in the command). The file
752 * id with which to write to this pipe is
753 * stored at *inPipePtr. NULL means command
754 * specified its own input source. */
755 Tcl_File *outPipePtr; /* If non-NULL, output to the pipeline goes
756 * to a pipe, unless overriden by redirection
757 * in the command. The file id with which to
758 * read frome this pipe is stored at
759 * *outPipePtr. NULL means command specified
760 * its own output sink. */
761 Tcl_File *errFilePtr; /* If non-NULL, all stderr output from the
762 * pipeline will go to a temporary file
763 * created here, and a descriptor to read
764 * the file will be left at *errFilePtr.
765 * The file will be removed already, so
766 * closing this descriptor will be the end
767 * of the file. If this is NULL, then
768 * all stderr output goes to our stderr.
769 * If the pipeline specifies redirection
770 * then the file will still be created
771 * but it will never get any data. */
773 #if defined( MAC_TCL )
774 Tcl_AppendResult(interp,
775 "command pipelines not supported on Macintosh OS", NULL);
778 pid_t *pidPtr = NULL; /* Points to malloc-ed array holding all
779 * the pids of child processes. */
780 int numPids = 0; /* Actual number of processes that exist
781 * at *pidPtr right now. */
782 int cmdCount; /* Count of number of distinct commands
783 * found in argc/argv. */
784 char *input = NULL; /* If non-null, then this points to a
785 * string containing input data (specified
786 * via <<) to be piped to the first process
787 * in the pipeline. */
788 Tcl_File inputFile = NULL;
789 /* If != NULL, gives file to use as input for
790 * first process in pipeline (specified via <
792 int closeInput = 0; /* If non-zero, then must close inputId
793 * when cleaning up (zero means the file needs
794 * to stay open for some other reason). */
795 Tcl_File outputFile = NULL;
796 /* Writable file for output from last command
797 * in pipeline (could be file or pipe). NULL
798 * means use stdout. */
799 int closeOutput = 0; /* Non-zero means must close outputId when
800 * cleaning up (similar to closeInput). */
801 Tcl_File errorFile = NULL;
802 /* Writable file for error output from all
803 * commands in pipeline. NULL means use
805 int closeError = 0; /* Non-zero means must close errorId when
807 int skip; /* Number of arguments to skip (because they
808 * specify redirection). */
812 int hasPipes = TclHasPipes();
813 char finalOut[L_tmpnam];
814 char intIn[L_tmpnam];
819 if (inPipePtr != NULL) {
822 if (outPipePtr != NULL) {
825 if (errFilePtr != NULL) {
830 * First, scan through all the arguments to figure out the structure
831 * of the pipeline. Process all of the input and output redirection
832 * arguments and remove them from the argument list in the pipeline.
833 * Count the number of distinct processes (it's the number of "|"
834 * arguments plus one) but don't remove the "|" arguments.
839 for (i = 0; i < argc; i++) {
840 if ((argv[i][0] == '|') && (((argv[i][1] == 0))
841 || ((argv[i][1] == '&') && (argv[i][2] == 0)))) {
842 if ((i == (lastBar+1)) || (i == (argc-1))) {
843 interp->result = "illegal use of | or |& in command";
849 } else if (argv[i][0] == '<') {
850 if ((inputFile != NULL) && closeInput) {
851 TclCloseFile(inputFile);
855 if (argv[i][1] == '<') {
860 Tcl_AppendResult(interp, "can't specify \"", argv[i],
861 "\" as last word in command", (char *) NULL);
868 inputFile = FileForRedirect(interp, argv[i]+1, 1, argv[i],
869 O_RDONLY, argv[i+1], &skip, &closeInput);
870 if (inputFile == NULL) {
874 /* When Win32s dies out, this code can be removed */
877 Tcl_AppendResult(interp, "redirection with '@'",
878 " notation is not supported on this system",
882 strcpy(intIn, skip == 1 ? argv[i]+1 : argv[i+1]);
885 } else if (argv[i][0] == '>') {
886 int append, useForStdErr, useForStdOut, mustClose, atOk, flags;
890 append = useForStdErr = 0;
892 if (argv[i][1] == '>') {
896 flags = O_WRONLY|O_CREAT;
899 flags = O_WRONLY|O_CREAT|O_TRUNC;
905 file = FileForRedirect(interp, p, atOk, argv[i], flags, argv[i+1],
911 /* When Win32s dies out, this code can be removed */
914 Tcl_AppendResult(interp, "redirection with '@'",
915 " notation is not supported on this system",
919 strcpy(finalOut, skip == 1 ? p : argv[i+1]);
922 if (hasPipes && append) {
923 TclSeekFile(file, 0L, 2);
927 * Got the file descriptor. Now use it for standard output,
928 * standard error, or both, depending on the redirection.
932 if ((outputFile != NULL) && closeOutput) {
933 TclCloseFile(outputFile);
936 closeOutput = mustClose;
939 if ((errorFile != NULL) && closeError) {
940 TclCloseFile(errorFile);
943 closeError = (useForStdOut) ? 0 : mustClose;
945 } else if ((argv[i][0] == '2') && (argv[i][1] == '>')) {
946 int append, atOk, flags;
948 if ((errorFile != NULL) && closeError) {
949 TclCloseFile(errorFile);
957 flags = O_WRONLY|O_CREAT;
961 flags = O_WRONLY|O_CREAT|O_TRUNC;
963 errorFile = FileForRedirect(interp, p, atOk, argv[i], flags,
964 argv[i+1], &skip, &closeError);
965 if (errorFile == NULL) {
968 if (hasPipes && append) {
969 TclSeekFile(errorFile, 0L, 2);
974 for (j = i+skip; j < argc; j++) {
975 argv[j-skip] = argv[j];
978 i -= 1; /* Process next arg from same position. */
981 interp->result = "didn't specify command to execute";
985 if ((hasPipes && inputFile == NULL) || (!hasPipes && intIn[0] == '\0')) {
989 * The input for the first process is immediate data coming from
990 * Tcl. Create a temporary file for it and put the data into the
994 inputFile = TclCreateTempFile(input);
996 if (inputFile == NULL) {
997 Tcl_AppendResult(interp,
998 "couldn't create input file for command: ",
999 Tcl_PosixError(interp), (char *) NULL);
1002 } else if (inPipePtr != NULL) {
1003 Tcl_File inPipe, outPipe;
1005 * The input for the first process in the pipeline is to
1006 * come from a pipe that can be written from this end.
1009 if (!hasPipes || TclCreatePipe(&inPipe, &outPipe) == 0) {
1010 Tcl_AppendResult(interp,
1011 "couldn't create input pipe for command: ",
1012 Tcl_PosixError(interp), (char *) NULL);
1017 *inPipePtr = outPipe;
1022 * Set up a pipe to receive output from the pipeline, if no other
1023 * output sink has been specified.
1026 if ((outputFile == NULL) && (outPipePtr != NULL)) {
1030 Tcl_File inPipe, outPipe;
1031 if (TclCreatePipe(&inPipe, &outPipe) == 0) {
1032 Tcl_AppendResult(interp,
1033 "couldn't create output pipe for command: ",
1034 Tcl_PosixError(interp), (char *) NULL);
1037 outputFile = outPipe;
1039 *outPipePtr = inPipe;
1044 * Set up the standard error output sink for the pipeline, if
1045 * requested. Use a temporary file which is opened, then deleted.
1046 * Could potentially just use pipe, but if it filled up it could
1047 * cause the pipeline to deadlock: we'd be waiting for processes
1048 * to complete before reading stderr, and processes couldn't complete
1049 * because stderr was backed up.
1052 if (errFilePtr && !errorFile) {
1053 *errFilePtr = TclCreateTempFile(NULL);
1054 if (*errFilePtr == NULL) {
1055 Tcl_AppendResult(interp,
1056 "couldn't create error file for command: ",
1057 Tcl_PosixError(interp), (char *) NULL);
1060 errorFile = *errFilePtr;
1065 * Scan through the argc array, forking off a process for each
1066 * group of arguments between "|" arguments.
1069 pidPtr = (pid_t *) ckalloc((unsigned) (cmdCount * sizeof(pid_t)));
1070 Tcl_ReapDetachedProcs();
1072 if (TclSpawnPipeline(interp, pidPtr, &numPids, argc, argv,
1073 inputFile, outputFile, errorFile, intIn, finalOut) == 0) {
1076 *pidArrayPtr = pidPtr;
1079 * All done. Cleanup open files lying around and then return.
1083 if ((inputFile != NULL) && closeInput) {
1084 TclCloseFile(inputFile);
1086 if ((outputFile != NULL) && closeOutput) {
1087 TclCloseFile(outputFile);
1089 if ((errorFile != NULL) && closeError) {
1090 TclCloseFile(errorFile);
1095 * An error occurred. There could have been extra files open, such
1096 * as pipes between children. Clean them all up. Detach any child
1097 * processes that have been created.
1101 if ((inPipePtr != NULL) && (*inPipePtr != NULL)) {
1102 TclCloseFile(*inPipePtr);
1105 if ((outPipePtr != NULL) && (*outPipePtr != NULL)) {
1106 TclCloseFile(*outPipePtr);
1109 if ((errFilePtr != NULL) && (*errFilePtr != NULL)) {
1110 TclCloseFile(*errFilePtr);
1113 if (pidPtr != NULL) {
1114 for (i = 0; i < numPids; i++) {
1115 if (pidPtr[i] != (pid_t)-1) {
1116 Tcl_DetachPids(1, &pidPtr[i]);
1119 ckfree((char *) pidPtr);
1123 #endif /* !MAC_TCL */
1127 *----------------------------------------------------------------------
1131 * Gets the current value of the Tcl error code variable. This is
1132 * currently the global variable "errno" but could in the future
1133 * change to something else.
1136 * The value of the Tcl error code variable.
1139 * None. Note that the value of the Tcl error code variable is
1140 * UNDEFINED if a call to Tcl_SetErrno did not precede this call.
1142 *----------------------------------------------------------------------
1152 *----------------------------------------------------------------------
1156 * Sets the Tcl error code variable to the supplied value.
1162 * Modifies the value of the Tcl error code variable.
1164 *----------------------------------------------------------------------
1169 int err; /* The new value. */
1175 *----------------------------------------------------------------------
1179 * This procedure is typically called after UNIX kernel calls
1180 * return errors. It stores machine-readable information about
1181 * the error in $errorCode returns an information string for
1185 * The return value is a human-readable string describing the
1189 * The global variable $errorCode is reset.
1191 *----------------------------------------------------------------------
1195 Tcl_PosixError(interp)
1196 Tcl_Interp *interp; /* Interpreter whose $errorCode variable
1197 * is to be changed. */
1201 msg = Tcl_ErrnoMsg(errno);
1203 Tcl_SetErrorCode(interp, "POSIX", id, msg, (char *) NULL);
1208 *----------------------------------------------------------------------
1210 * Tcl_OpenCommandChannel --
1212 * Opens an I/O channel to one or more subprocesses specified
1213 * by argc and argv. The flags argument determines the
1214 * disposition of the stdio handles. If the TCL_STDIN flag is
1215 * set then the standard input for the first subprocess will
1216 * be tied to the channel: writing to the channel will provide
1217 * input to the subprocess. If TCL_STDIN is not set, then
1218 * standard input for the first subprocess will be the same as
1219 * this application's standard input. If TCL_STDOUT is set then
1220 * standard output from the last subprocess can be read from the
1221 * channel; otherwise it goes to this application's standard
1222 * output. If TCL_STDERR is set, standard error output for all
1223 * subprocesses is returned to the channel and results in an error
1224 * when the channel is closed; otherwise it goes to this
1225 * application's standard error. If TCL_ENFORCE_MODE is not set,
1226 * then argc and argv can redirect the stdio handles to override
1227 * TCL_STDIN, TCL_STDOUT, and TCL_STDERR; if it is set, then it
1228 * is an error for argc and argv to override stdio channels for
1229 * which TCL_STDIN, TCL_STDOUT, and TCL_STDERR have been set.
1232 * A new command channel, or NULL on failure with an error
1233 * message left in interp.
1236 * Creates processes, opens pipes.
1238 *----------------------------------------------------------------------
1242 Tcl_OpenCommandChannel(interp, argc, argv, flags)
1243 Tcl_Interp *interp; /* Interpreter for error reporting. Can
1245 int argc; /* How many arguments. */
1246 char **argv; /* Array of arguments for command pipe. */
1247 int flags; /* Or'ed combination of TCL_STDIN, TCL_STDOUT,
1248 * TCL_STDERR, and TCL_ENFORCE_MODE. */
1250 Tcl_File *inPipePtr, *outPipePtr, *errFilePtr;
1251 Tcl_File inPipe, outPipe, errFile;
1254 Tcl_Channel channel;
1256 inPipe = outPipe = errFile = NULL;
1258 inPipePtr = (flags & TCL_STDIN) ? &inPipe : NULL;
1259 outPipePtr = (flags & TCL_STDOUT) ? &outPipe : NULL;
1260 errFilePtr = (flags & TCL_STDERR) ? &errFile : NULL;
1262 numPids = TclCreatePipeline(interp, argc, argv, &pidPtr, inPipePtr,
1263 outPipePtr, errFilePtr);
1270 * Verify that the pipes that were created satisfy the
1271 * readable/writable constraints.
1274 if (flags & TCL_ENFORCE_MODE) {
1275 if ((flags & TCL_STDOUT) && (outPipe == NULL)) {
1276 Tcl_AppendResult(interp, "can't read output from command:",
1277 " standard output was redirected", (char *) NULL);
1280 if ((flags & TCL_STDIN) && (inPipe == NULL)) {
1281 Tcl_AppendResult(interp, "can't write input to command:",
1282 " standard input was redirected", (char *) NULL);
1287 channel = TclCreateCommandChannel(outPipe, inPipe, errFile,
1290 if (channel == (Tcl_Channel) NULL) {
1291 Tcl_AppendResult(interp, "pipe for command could not be created",
1299 Tcl_DetachPids(numPids, pidPtr);
1300 ckfree((char *) pidPtr);
1302 if (inPipe != NULL) {
1303 TclClosePipeFile(inPipe);
1305 if (outPipe != NULL) {
1306 TclClosePipeFile(outPipe);
1308 if (errFile != NULL) {
1309 TclClosePipeFile(errFile);