2 * (c) 2008 Steve Bennett <steveb@workware.net.au>
4 * Implements the exec command for Jim
6 * Based on code originally from Tcl 6.7 by John Ousterhout.
9 * The Tcl_Fork and Tcl_WaitPids procedures are based on code
10 * contributed by Karl Lehenbauer, Mark Diekhans and Peter
13 * Copyright 1987-1991 Regents of the University of California
14 * Permission to use, copy, modify, and distribute this
15 * software and its documentation for any purpose and without
16 * fee is hereby granted, provided that the above copyright
17 * notice appear in all copies. The University of California
18 * makes no representations about the suitability of this
19 * software for any purpose. It is provided "as is" without
20 * express or implied warranty.
27 #include "jimautoconf.h"
30 #if (!defined(HAVE_VFORK) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
31 /* Poor man's implementation of exec with system()
32 * The system() call *may* do command line redirection, etc.
33 * The standard output is not available.
34 * Can't redirect filehandles.
36 static int Jim_ExecCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
38 Jim_Obj
*cmdlineObj
= Jim_NewEmptyStringObj(interp
);
42 /* Create a quoted command line */
43 for (i
= 1; i
< argc
; i
++) {
45 const char *arg
= Jim_GetString(argv
[i
], &len
);
48 Jim_AppendString(interp
, cmdlineObj
, " ", 1);
50 if (strpbrk(arg
, "\\\" ") == NULL
) {
51 /* No quoting required */
52 Jim_AppendString(interp
, cmdlineObj
, arg
, len
);
56 Jim_AppendString(interp
, cmdlineObj
, "\"", 1);
57 for (j
= 0; j
< len
; j
++) {
58 if (arg
[j
] == '\\' || arg
[j
] == '"') {
59 Jim_AppendString(interp
, cmdlineObj
, "\\", 1);
61 Jim_AppendString(interp
, cmdlineObj
, &arg
[j
], 1);
63 Jim_AppendString(interp
, cmdlineObj
, "\"", 1);
65 rc
= system(Jim_String(cmdlineObj
));
67 Jim_FreeNewObj(interp
, cmdlineObj
);
70 Jim_Obj
*errorCode
= Jim_NewListObj(interp
, NULL
, 0);
71 Jim_ListAppendElement(interp
, errorCode
, Jim_NewStringObj(interp
, "CHILDSTATUS", -1));
72 Jim_ListAppendElement(interp
, errorCode
, Jim_NewIntObj(interp
, 0));
73 Jim_ListAppendElement(interp
, errorCode
, Jim_NewIntObj(interp
, rc
));
74 Jim_SetGlobalVariableStr(interp
, "errorCode", errorCode
);
81 int Jim_execInit(Jim_Interp
*interp
)
83 if (Jim_PackageProvide(interp
, "exec", "1.0", JIM_ERRMSG
))
86 Jim_CreateCommand(interp
, "exec", Jim_ExecCmd
, NULL
, NULL
);
90 /* Full exec implementation for unix and mingw */
95 #if defined(__MINGW32__)
96 /* XXX: Should we use this implementation for cygwin too? msvc? */
100 #define WIN32_LEAN_AND_MEAN
104 typedef HANDLE fdtype
;
105 typedef HANDLE pidtype
;
106 #define JIM_BAD_FD INVALID_HANDLE_VALUE
107 #define JIM_BAD_PID INVALID_HANDLE_VALUE
108 #define JimCloseFd CloseHandle
110 #define WIFEXITED(STATUS) 1
111 #define WEXITSTATUS(STATUS) (STATUS)
112 #define WIFSIGNALED(STATUS) 0
113 #define WTERMSIG(STATUS) 0
116 static fdtype
JimFileno(FILE *fh
);
117 static pidtype
JimWaitPid(pidtype pid
, int *status
, int nohang
);
118 static fdtype
JimDupFd(fdtype infd
);
119 static fdtype
JimOpenForRead(const char *filename
);
120 static FILE *JimFdOpenForRead(fdtype fd
);
121 static int JimPipe(fdtype pipefd
[2]);
122 static pidtype
JimStartWinProcess(Jim_Interp
*interp
, char **argv
, char *env
,
123 fdtype inputId
, fdtype outputId
, fdtype errorId
);
124 static int JimErrno(void);
126 #include "jim-signal.h"
129 #include <sys/wait.h>
130 #include <sys/stat.h>
135 #define JimErrno() errno
136 #define JIM_BAD_FD -1
137 #define JIM_BAD_PID -1
138 #define JimFileno fileno
139 #define JimReadFd read
140 #define JimCloseFd close
141 #define JimWaitPid waitpid
143 #define JimFdOpenForRead(FD) fdopen((FD), "r")
144 #define JimOpenForRead(NAME) open((NAME), O_RDONLY, 0)
147 #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
151 static const char *JimStrError(void);
152 static char **JimSaveEnv(char **env
);
153 static void JimRestoreEnv(char **env
);
154 static int JimCreatePipeline(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
,
155 pidtype
**pidArrayPtr
, fdtype
*inPipePtr
, fdtype
*outPipePtr
, fdtype
*errFilePtr
);
156 static void JimDetachPids(Jim_Interp
*interp
, int numPids
, const pidtype
*pidPtr
);
157 static int JimCleanupChildren(Jim_Interp
*interp
, int numPids
, pidtype
*pidPtr
, Jim_Obj
*errStrObj
);
158 static fdtype
JimCreateTemp(Jim_Interp
*interp
, const char *contents
, int len
);
159 static fdtype
JimOpenForWrite(const char *filename
, int append
);
160 static int JimRewindFd(fdtype fd
);
162 static void Jim_SetResultErrno(Jim_Interp
*interp
, const char *msg
)
164 Jim_SetResultFormatted(interp
, "%s: %s", msg
, JimStrError());
167 static const char *JimStrError(void)
169 return strerror(JimErrno());
173 * If the last character of 'objPtr' is a newline, then remove
174 * the newline character.
176 static void Jim_RemoveTrailingNewline(Jim_Obj
*objPtr
)
179 const char *s
= Jim_GetString(objPtr
, &len
);
181 if (len
> 0 && s
[len
- 1] == '\n') {
183 objPtr
->bytes
[objPtr
->length
] = '\0';
188 * Read from 'fd', append the data to strObj and close 'fd'.
189 * Returns 1 if data was added, 0 if not, or -1 on error.
191 static int JimAppendStreamToString(Jim_Interp
*interp
, fdtype fd
, Jim_Obj
*strObj
)
194 FILE *fh
= JimFdOpenForRead(fd
);
202 int retval
= fread(buf
, 1, sizeof(buf
), fh
);
205 Jim_AppendString(interp
, strObj
, buf
, retval
);
207 if (retval
!= sizeof(buf
)) {
216 * Builds the environment array from $::env
218 * If $::env is not set, simply returns environ.
220 * Otherwise allocates the environ array from the contents of $::env
222 * If the exec fails, memory can be freed via JimFreeEnv()
224 static char **JimBuildEnv(Jim_Interp
*interp
)
233 Jim_Obj
*objPtr
= Jim_GetGlobalVariableStr(interp
, "env", JIM_NONE
);
236 return Jim_GetEnviron();
239 /* We build the array as a single block consisting of the pointers followed by
240 * the strings. This has the advantage of being easy to allocate/free and being
241 * compatible with both unix and windows
244 /* Calculate the required size */
245 num
= Jim_ListLength(interp
, objPtr
);
247 /* Silently drop the last element if not a valid dictionary */
250 /* We need one \0 and one equal sign for each element.
251 * A list has at least one space for each element except the first.
252 * We need one extra char for the extra null terminator and one for the equal sign.
254 size
= Jim_Length(objPtr
) + 2;
256 envptr
= Jim_Alloc(sizeof(*envptr
) * (num
/ 2 + 1) + size
);
257 envdata
= (char *)&envptr
[num
/ 2 + 1];
260 for (i
= 0; i
< num
; i
+= 2) {
264 Jim_ListIndex(interp
, objPtr
, i
, &elemObj
, JIM_NONE
);
265 s1
= Jim_String(elemObj
);
266 Jim_ListIndex(interp
, objPtr
, i
+ 1, &elemObj
, JIM_NONE
);
267 s2
= Jim_String(elemObj
);
270 envdata
+= sprintf(envdata
, "%s=%s", s1
, s2
);
281 * Frees the environment allocated by JimBuildEnv()
283 * Must pass original_environ.
285 static void JimFreeEnv(char **env
, char **original_environ
)
287 if (env
!= original_environ
) {
292 #ifndef jim_ext_signal
293 /* Implement trivial Jim_SignalId() and Jim_SignalName(), just good enough for JimCheckWaitStatus() */
294 const char *Jim_SignalId(int sig
)
297 snprintf(buf
, sizeof(buf
), "%d", sig
);
301 const char *Jim_SignalName(int sig
)
303 return Jim_SignalId(sig
);
308 * Create and store an appropriate value for the global variable $::errorCode
309 * Based on pid and waitStatus.
311 * Returns JIM_OK for a normal exit with code 0, otherwise returns JIM_ERR.
313 * Note that $::errorCode is left unchanged for a normal exit.
314 * Details of any abnormal exit is appended to the errStrObj, unless it is NULL.
316 static int JimCheckWaitStatus(Jim_Interp
*interp
, pidtype pid
, int waitStatus
, Jim_Obj
*errStrObj
)
320 if (WIFEXITED(waitStatus
) && WEXITSTATUS(waitStatus
) == 0) {
323 errorCode
= Jim_NewListObj(interp
, NULL
, 0);
325 if (WIFEXITED(waitStatus
)) {
326 Jim_ListAppendElement(interp
, errorCode
, Jim_NewStringObj(interp
, "CHILDSTATUS", -1));
327 Jim_ListAppendElement(interp
, errorCode
, Jim_NewIntObj(interp
, (long)pid
));
328 Jim_ListAppendElement(interp
, errorCode
, Jim_NewIntObj(interp
, WEXITSTATUS(waitStatus
)));
334 if (WIFSIGNALED(waitStatus
)) {
335 type
= "CHILDKILLED";
340 action
= "suspended";
343 Jim_ListAppendElement(interp
, errorCode
, Jim_NewStringObj(interp
, type
, -1));
346 /* Append the message to 'errStrObj' with a newline.
347 * The last newline will be stripped later
349 Jim_AppendStrings(interp
, errStrObj
, "child ", action
, " by signal ", Jim_SignalId(WTERMSIG(waitStatus
)), "\n", NULL
);
352 Jim_ListAppendElement(interp
, errorCode
, Jim_NewIntObj(interp
, (long)pid
));
353 Jim_ListAppendElement(interp
, errorCode
, Jim_NewStringObj(interp
, Jim_SignalId(WTERMSIG(waitStatus
)), -1));
354 Jim_ListAppendElement(interp
, errorCode
, Jim_NewStringObj(interp
, Jim_SignalName(WTERMSIG(waitStatus
)), -1));
356 Jim_SetGlobalVariableStr(interp
, "errorCode", errorCode
);
362 * Data structures of the following type are used by JimFork and
363 * JimWaitPids to keep track of child processes.
368 pidtype pid
; /* Process id of child. */
369 int status
; /* Status returned when child exited or suspended. */
370 int flags
; /* Various flag bits; see below for definitions. */
373 struct WaitInfoTable
{
374 struct WaitInfo
*info
; /* Table of outstanding processes */
375 int size
; /* Size of the allocated table */
376 int used
; /* Number of entries in use */
380 * Flag bits in WaitInfo structures:
382 * WI_DETACHED - Non-zero means no-one cares about the
383 * process anymore. Ignore it until it
384 * exits, then forget about it.
387 #define WI_DETACHED 2
389 #define WAIT_TABLE_GROW_BY 4
391 static void JimFreeWaitInfoTable(struct Jim_Interp
*interp
, void *privData
)
393 struct WaitInfoTable
*table
= privData
;
395 Jim_Free(table
->info
);
399 static struct WaitInfoTable
*JimAllocWaitInfoTable(void)
401 struct WaitInfoTable
*table
= Jim_Alloc(sizeof(*table
));
403 table
->size
= table
->used
= 0;
409 * The main [exec] command
411 static int Jim_ExecCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
413 fdtype outputId
; /* File id for output pipe. -1 means command overrode. */
414 fdtype errorId
; /* File id for temporary file containing error output. */
417 int child_siginfo
= 1;
418 Jim_Obj
*childErrObj
;
422 * See if the command is to be run in the background; if so, create
423 * the command, detach it, and return.
425 if (argc
> 1 && Jim_CompareStringImmediate(interp
, argv
[argc
- 1], "&")) {
430 numPids
= JimCreatePipeline(interp
, argc
- 1, argv
+ 1, &pidPtr
, NULL
, NULL
, NULL
);
434 /* The return value is a list of the pids */
435 listObj
= Jim_NewListObj(interp
, NULL
, 0);
436 for (i
= 0; i
< numPids
; i
++) {
437 Jim_ListAppendElement(interp
, listObj
, Jim_NewIntObj(interp
, (long)pidPtr
[i
]));
439 Jim_SetResult(interp
, listObj
);
440 JimDetachPids(interp
, numPids
, pidPtr
);
446 * Create the command's pipeline.
449 JimCreatePipeline(interp
, argc
- 1, argv
+ 1, &pidPtr
, NULL
, &outputId
, &errorId
);
457 errStrObj
= Jim_NewStringObj(interp
, "", 0);
459 /* Read from the output pipe until EOF */
460 if (outputId
!= JIM_BAD_FD
) {
461 if (JimAppendStreamToString(interp
, outputId
, errStrObj
) < 0) {
463 Jim_SetResultErrno(interp
, "error reading from output pipe");
467 /* Now wait for children to finish. Any abnormal results are appended to childErrObj */
468 childErrObj
= Jim_NewStringObj(interp
, "", 0);
469 Jim_IncrRefCount(childErrObj
);
471 if (JimCleanupChildren(interp
, numPids
, pidPtr
, childErrObj
) != JIM_OK
) {
476 * Read the child's error output (if any) and put it into the result.
478 * Note that unlike Tcl, the presence of stderr output does not cause
479 * exec to return an error.
481 if (errorId
!= JIM_BAD_FD
) {
483 JimRewindFd(errorId
);
484 ret
= JimAppendStreamToString(interp
, errorId
, errStrObj
);
486 Jim_SetResultErrno(interp
, "error reading from error pipe");
490 /* Got some error output, so discard the abnormal info string */
496 /* Append the child siginfo to the result */
497 Jim_AppendObj(interp
, errStrObj
, childErrObj
);
499 Jim_DecrRefCount(interp
, childErrObj
);
501 /* Finally remove any trailing newline from the result */
502 Jim_RemoveTrailingNewline(errStrObj
);
504 /* Set this as the result */
505 Jim_SetResult(interp
, errStrObj
);
510 static void JimReapDetachedPids(struct WaitInfoTable
*table
)
512 struct WaitInfo
*waitPtr
;
520 waitPtr
= table
->info
;
522 for (count
= table
->used
; count
> 0; waitPtr
++, count
--) {
523 if (waitPtr
->flags
& WI_DETACHED
) {
525 pidtype pid
= JimWaitPid(waitPtr
->pid
, &status
, WNOHANG
);
526 if (pid
== waitPtr
->pid
) {
527 /* Process has exited, so remove it from the table */
532 if (waitPtr
!= &table
->info
[dest
]) {
533 table
->info
[dest
] = *waitPtr
;
540 * Does waitpid() on the given pid, and then removes the
541 * entry from the wait table.
543 * Returns the pid if OK and updates *statusPtr with the status,
544 * or JIM_BAD_PID if the pid was not in the table.
546 static pidtype
JimWaitForProcess(struct WaitInfoTable
*table
, pidtype pid
, int *statusPtr
)
550 /* Find it in the table */
551 for (i
= 0; i
< table
->used
; i
++) {
552 if (pid
== table
->info
[i
].pid
) {
554 JimWaitPid(pid
, statusPtr
, 0);
556 /* Remove it from the table */
557 if (i
!= table
->used
- 1) {
558 table
->info
[i
] = table
->info
[table
->used
- 1];
570 * Indicates that one or more child processes have been placed in
571 * background and are no longer cared about.
572 * These children can be cleaned up with JimReapDetachedPids().
574 static void JimDetachPids(Jim_Interp
*interp
, int numPids
, const pidtype
*pidPtr
)
577 struct WaitInfoTable
*table
= Jim_CmdPrivData(interp
);
579 for (j
= 0; j
< numPids
; j
++) {
580 /* Find it in the table */
582 for (i
= 0; i
< table
->used
; i
++) {
583 if (pidPtr
[j
] == table
->info
[i
].pid
) {
584 table
->info
[i
].flags
|= WI_DETACHED
;
591 static FILE *JimGetAioFilehandle(Jim_Interp
*interp
, const char *name
)
596 fhObj
= Jim_NewStringObj(interp
, name
, -1);
597 Jim_IncrRefCount(fhObj
);
598 fh
= Jim_AioFilehandle(interp
, fhObj
);
599 Jim_DecrRefCount(interp
, fhObj
);
605 *----------------------------------------------------------------------
607 * JimCreatePipeline --
609 * Given an argc/argv array, instantiate a pipeline of processes
610 * as described by the argv.
613 * The return value is a count of the number of new processes
614 * created, or -1 if an error occurred while creating the pipeline.
615 * *pidArrayPtr is filled in with the address of a dynamically
616 * allocated array giving the ids of all of the processes. It
617 * is up to the caller to free this array when it isn't needed
618 * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in
619 * with the file id for the input pipe for the pipeline (if any):
620 * the caller must eventually close this file. If outPipePtr
621 * isn't NULL, then *outPipePtr is filled in with the file id
622 * for the output pipe from the pipeline: the caller must close
623 * this file. If errFilePtr isn't NULL, then *errFilePtr is filled
624 * with a file id that may be used to read error output after the
625 * pipeline completes.
628 * Processes and pipes are created.
630 *----------------------------------------------------------------------
633 JimCreatePipeline(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
, pidtype
**pidArrayPtr
,
634 fdtype
*inPipePtr
, fdtype
*outPipePtr
, fdtype
*errFilePtr
)
636 pidtype
*pidPtr
= NULL
; /* Points to malloc-ed array holding all
637 * the pids of child processes. */
638 int numPids
= 0; /* Actual number of processes that exist
639 * at *pidPtr right now. */
640 int cmdCount
; /* Count of number of distinct commands
641 * found in argc/argv. */
642 const char *input
= NULL
; /* Describes input for pipeline, depending
643 * on "inputFile". NULL means take input
644 * from stdin/pipe. */
645 int input_len
= 0; /* Length of input, if relevant */
647 #define FILE_NAME 0 /* input/output: filename */
648 #define FILE_APPEND 1 /* output only: filename, append */
649 #define FILE_HANDLE 2 /* input/output: filehandle */
650 #define FILE_TEXT 3 /* input only: input is actual text */
652 int inputFile
= FILE_NAME
; /* 1 means input is name of input file.
653 * 2 means input is filehandle name.
654 * 0 means input holds actual
655 * text to be input to command. */
657 int outputFile
= FILE_NAME
; /* 0 means output is the name of output file.
658 * 1 means output is the name of output file, and append.
659 * 2 means output is filehandle name.
660 * All this is ignored if output is NULL
662 int errorFile
= FILE_NAME
; /* 0 means error is the name of error file.
663 * 1 means error is the name of error file, and append.
664 * 2 means error is filehandle name.
665 * All this is ignored if error is NULL
667 const char *output
= NULL
; /* Holds name of output file to pipe to,
668 * or NULL if output goes to stdout/pipe. */
669 const char *error
= NULL
; /* Holds name of stderr file to pipe to,
670 * or NULL if stderr goes to stderr/pipe. */
671 fdtype inputId
= JIM_BAD_FD
;
672 /* Readable file id input to current command in
673 * pipeline (could be file or pipe). JIM_BAD_FD
674 * means use stdin. */
675 fdtype outputId
= JIM_BAD_FD
;
676 /* Writable file id for output from current
677 * command in pipeline (could be file or pipe).
678 * JIM_BAD_FD means use stdout. */
679 fdtype errorId
= JIM_BAD_FD
;
680 /* Writable file id for all standard error
681 * output from all commands in pipeline. JIM_BAD_FD
682 * means use stderr. */
683 fdtype lastOutputId
= JIM_BAD_FD
;
684 /* Write file id for output from last command
685 * in pipeline (could be file or pipe).
686 * -1 means use stdout. */
687 fdtype pipeIds
[2]; /* File ids for pipe that's being created. */
688 int firstArg
, lastArg
; /* Indexes of first and last arguments in
689 * current command. */
694 struct WaitInfoTable
*table
= Jim_CmdPrivData(interp
);
696 /* Holds the args which will be used to exec */
697 char **arg_array
= Jim_Alloc(sizeof(*arg_array
) * (argc
+ 1));
700 JimReapDetachedPids(table
);
702 if (inPipePtr
!= NULL
) {
703 *inPipePtr
= JIM_BAD_FD
;
705 if (outPipePtr
!= NULL
) {
706 *outPipePtr
= JIM_BAD_FD
;
708 if (errFilePtr
!= NULL
) {
709 *errFilePtr
= JIM_BAD_FD
;
711 pipeIds
[0] = pipeIds
[1] = JIM_BAD_FD
;
714 * First, scan through all the arguments to figure out the structure
715 * of the pipeline. Count the number of distinct processes (it's the
716 * number of "|" arguments). If there are "<", "<<", or ">" arguments
717 * then make note of input and output redirection and remove these
718 * arguments and the arguments that follow them.
722 for (i
= 0; i
< argc
; i
++) {
723 const char *arg
= Jim_String(argv
[i
]);
726 inputFile
= FILE_NAME
;
729 inputFile
= FILE_TEXT
;
730 input_len
= Jim_Length(argv
[i
]) - 2;
733 else if (*input
== '@') {
734 inputFile
= FILE_HANDLE
;
738 if (!*input
&& ++i
< argc
) {
739 input
= Jim_GetString(argv
[i
], &input_len
);
742 else if (arg
[0] == '>') {
745 outputFile
= FILE_NAME
;
748 if (*output
== '>') {
749 outputFile
= FILE_APPEND
;
752 if (*output
== '&') {
753 /* Redirect stderr too */
757 if (*output
== '@') {
758 outputFile
= FILE_HANDLE
;
761 if (!*output
&& ++i
< argc
) {
762 output
= Jim_String(argv
[i
]);
765 errorFile
= outputFile
;
769 else if (arg
[0] == '2' && arg
[1] == '>') {
771 errorFile
= FILE_NAME
;
774 errorFile
= FILE_HANDLE
;
777 else if (*error
== '>') {
778 errorFile
= FILE_APPEND
;
781 if (!*error
&& ++i
< argc
) {
782 error
= Jim_String(argv
[i
]);
786 if (strcmp(arg
, "|") == 0 || strcmp(arg
, "|&") == 0) {
787 if (i
== lastBar
+ 1 || i
== argc
- 1) {
788 Jim_SetResultString(interp
, "illegal use of | or |& in command", -1);
794 /* Either |, |& or a "normal" arg, so store it in the arg array */
795 arg_array
[arg_count
++] = (char *)arg
;
800 Jim_SetResultFormatted(interp
, "can't specify \"%s\" as last word in command", arg
);
805 if (arg_count
== 0) {
806 Jim_SetResultString(interp
, "didn't specify command to execute", -1);
812 /* Must do this before vfork(), so do it now */
813 save_environ
= JimSaveEnv(JimBuildEnv(interp
));
816 * Set up the redirected input source for the pipeline, if
820 if (inputFile
== FILE_TEXT
) {
822 * Immediate data in command. Create temporary file and
823 * put data into file.
825 inputId
= JimCreateTemp(interp
, input
, input_len
);
826 if (inputId
== JIM_BAD_FD
) {
830 else if (inputFile
== FILE_HANDLE
) {
831 /* Should be a file descriptor */
832 FILE *fh
= JimGetAioFilehandle(interp
, input
);
837 inputId
= JimDupFd(JimFileno(fh
));
841 * File redirection. Just open the file.
843 inputId
= JimOpenForRead(input
);
844 if (inputId
== JIM_BAD_FD
) {
845 Jim_SetResultFormatted(interp
, "couldn't read file \"%s\": %s", input
, JimStrError());
850 else if (inPipePtr
!= NULL
) {
851 if (JimPipe(pipeIds
) != 0) {
852 Jim_SetResultErrno(interp
, "couldn't create input pipe for command");
855 inputId
= pipeIds
[0];
856 *inPipePtr
= pipeIds
[1];
857 pipeIds
[0] = pipeIds
[1] = JIM_BAD_FD
;
861 * Set up the redirected output sink for the pipeline from one
862 * of two places, if requested.
864 if (output
!= NULL
) {
865 if (outputFile
== FILE_HANDLE
) {
866 FILE *fh
= JimGetAioFilehandle(interp
, output
);
871 lastOutputId
= JimDupFd(JimFileno(fh
));
875 * Output is to go to a file.
877 lastOutputId
= JimOpenForWrite(output
, outputFile
== FILE_APPEND
);
878 if (lastOutputId
== JIM_BAD_FD
) {
879 Jim_SetResultFormatted(interp
, "couldn't write file \"%s\": %s", output
, JimStrError());
884 else if (outPipePtr
!= NULL
) {
886 * Output is to go to a pipe.
888 if (JimPipe(pipeIds
) != 0) {
889 Jim_SetResultErrno(interp
, "couldn't create output pipe");
892 lastOutputId
= pipeIds
[1];
893 *outPipePtr
= pipeIds
[0];
894 pipeIds
[0] = pipeIds
[1] = JIM_BAD_FD
;
896 /* If we are redirecting stderr with 2>filename or 2>@fileId, then we ignore errFilePtr */
898 if (errorFile
== FILE_HANDLE
) {
899 if (strcmp(error
, "1") == 0) {
901 if (lastOutputId
!= JIM_BAD_FD
) {
902 errorId
= JimDupFd(lastOutputId
);
905 /* No redirection of stdout, so just use 2>@stdout */
909 if (errorId
== JIM_BAD_FD
) {
910 FILE *fh
= JimGetAioFilehandle(interp
, error
);
915 errorId
= JimDupFd(JimFileno(fh
));
920 * Output is to go to a file.
922 errorId
= JimOpenForWrite(error
, errorFile
== FILE_APPEND
);
923 if (errorId
== JIM_BAD_FD
) {
924 Jim_SetResultFormatted(interp
, "couldn't write file \"%s\": %s", error
, JimStrError());
929 else if (errFilePtr
!= NULL
) {
931 * Set up the standard error output sink for the pipeline, if
932 * requested. Use a temporary file which is opened, then deleted.
933 * Could potentially just use pipe, but if it filled up it could
934 * cause the pipeline to deadlock: we'd be waiting for processes
935 * to complete before reading stderr, and processes couldn't complete
936 * because stderr was backed up.
938 errorId
= JimCreateTemp(interp
, NULL
, 0);
939 if (errorId
== JIM_BAD_FD
) {
942 *errFilePtr
= JimDupFd(errorId
);
946 * Scan through the argc array, forking off a process for each
947 * group of arguments between "|" arguments.
950 pidPtr
= Jim_Alloc(cmdCount
* sizeof(*pidPtr
));
951 for (i
= 0; i
< numPids
; i
++) {
952 pidPtr
[i
] = JIM_BAD_PID
;
954 for (firstArg
= 0; firstArg
< arg_count
; numPids
++, firstArg
= lastArg
+ 1) {
955 int pipe_dup_err
= 0;
956 fdtype origErrorId
= errorId
;
958 for (lastArg
= firstArg
; lastArg
< arg_count
; lastArg
++) {
959 if (arg_array
[lastArg
][0] == '|') {
960 if (arg_array
[lastArg
][1] == '&') {
966 /* Replace | with NULL for execv() */
967 arg_array
[lastArg
] = NULL
;
968 if (lastArg
== arg_count
) {
969 outputId
= lastOutputId
;
972 if (JimPipe(pipeIds
) != 0) {
973 Jim_SetResultErrno(interp
, "couldn't create pipe");
976 outputId
= pipeIds
[1];
979 /* Need to do this befor vfork() */
984 /* Now fork the child */
987 pid
= JimStartWinProcess(interp
, &arg_array
[firstArg
], save_environ
? save_environ
[0] : NULL
, inputId
, outputId
, errorId
);
988 if (pid
== JIM_BAD_PID
) {
989 Jim_SetResultFormatted(interp
, "couldn't exec \"%s\"", arg_array
[firstArg
]);
994 * Make a new process and enter it into the table if the fork
999 Jim_SetResultErrno(interp
, "couldn't fork child process");
1005 if (inputId
!= -1) dup2(inputId
, 0);
1006 if (outputId
!= -1) dup2(outputId
, 1);
1007 if (errorId
!= -1) dup2(errorId
, 2);
1009 for (i
= 3; (i
<= outputId
) || (i
<= inputId
) || (i
<= errorId
); i
++) {
1013 /* Restore SIGPIPE behaviour */
1014 (void)signal(SIGPIPE
, SIG_DFL
);
1016 execvpe(arg_array
[firstArg
], &arg_array
[firstArg
], Jim_GetEnviron());
1018 /* Need to prep an error message before vfork(), just in case */
1019 fprintf(stderr
, "couldn't exec \"%s\"\n", arg_array
[firstArg
]);
1027 * Enlarge the wait table if there isn't enough space for a new
1030 if (table
->used
== table
->size
) {
1031 table
->size
+= WAIT_TABLE_GROW_BY
;
1032 table
->info
= Jim_Realloc(table
->info
, table
->size
* sizeof(*table
->info
));
1035 table
->info
[table
->used
].pid
= pid
;
1036 table
->info
[table
->used
].flags
= 0;
1039 pidPtr
[numPids
] = pid
;
1041 /* Restore in case of pipe_dup_err */
1042 errorId
= origErrorId
;
1045 * Close off our copies of file descriptors that were set up for
1046 * this child, then set up the input for the next child.
1049 if (inputId
!= JIM_BAD_FD
) {
1050 JimCloseFd(inputId
);
1052 if (outputId
!= JIM_BAD_FD
) {
1053 JimCloseFd(outputId
);
1055 inputId
= pipeIds
[0];
1056 pipeIds
[0] = pipeIds
[1] = JIM_BAD_FD
;
1058 *pidArrayPtr
= pidPtr
;
1061 * All done. Cleanup open files lying around and then return.
1065 if (inputId
!= JIM_BAD_FD
) {
1066 JimCloseFd(inputId
);
1068 if (lastOutputId
!= JIM_BAD_FD
) {
1069 JimCloseFd(lastOutputId
);
1071 if (errorId
!= JIM_BAD_FD
) {
1072 JimCloseFd(errorId
);
1074 Jim_Free(arg_array
);
1076 JimRestoreEnv(save_environ
);
1081 * An error occurred. There could have been extra files open, such
1082 * as pipes between children. Clean them all up. Detach any child
1083 * processes that have been created.
1087 if ((inPipePtr
!= NULL
) && (*inPipePtr
!= JIM_BAD_FD
)) {
1088 JimCloseFd(*inPipePtr
);
1089 *inPipePtr
= JIM_BAD_FD
;
1091 if ((outPipePtr
!= NULL
) && (*outPipePtr
!= JIM_BAD_FD
)) {
1092 JimCloseFd(*outPipePtr
);
1093 *outPipePtr
= JIM_BAD_FD
;
1095 if ((errFilePtr
!= NULL
) && (*errFilePtr
!= JIM_BAD_FD
)) {
1096 JimCloseFd(*errFilePtr
);
1097 *errFilePtr
= JIM_BAD_FD
;
1099 if (pipeIds
[0] != JIM_BAD_FD
) {
1100 JimCloseFd(pipeIds
[0]);
1102 if (pipeIds
[1] != JIM_BAD_FD
) {
1103 JimCloseFd(pipeIds
[1]);
1105 if (pidPtr
!= NULL
) {
1106 for (i
= 0; i
< numPids
; i
++) {
1107 if (pidPtr
[i
] != JIM_BAD_PID
) {
1108 JimDetachPids(interp
, 1, &pidPtr
[i
]);
1118 *----------------------------------------------------------------------
1120 * JimCleanupChildren --
1122 * This is a utility procedure used to wait for child processes
1123 * to exit, record information about abnormal exits.
1126 * The return value is a standard Tcl result. If anything at
1127 * weird happened with the child processes, JIM_ERR is returned
1128 * and a structured message is left in $::errorCode.
1129 * If errStrObj is not NULL, abnormal exit details are appended to this object.
1134 *----------------------------------------------------------------------
1137 static int JimCleanupChildren(Jim_Interp
*interp
, int numPids
, pidtype
*pidPtr
, Jim_Obj
*errStrObj
)
1139 struct WaitInfoTable
*table
= Jim_CmdPrivData(interp
);
1140 int result
= JIM_OK
;
1143 /* Now check the return status of each child */
1144 for (i
= 0; i
< numPids
; i
++) {
1146 if (JimWaitForProcess(table
, pidPtr
[i
], &waitStatus
) != JIM_BAD_PID
) {
1147 if (JimCheckWaitStatus(interp
, pidPtr
[i
], waitStatus
, errStrObj
) != JIM_OK
) {
1157 int Jim_execInit(Jim_Interp
*interp
)
1159 if (Jim_PackageProvide(interp
, "exec", "1.0", JIM_ERRMSG
))
1164 * Disable SIGPIPE signals: if they were allowed, this process
1165 * might go away unexpectedly if children misbehave. This code
1166 * can potentially interfere with other application code that
1167 * expects to handle SIGPIPEs.
1169 * By doing this in the init function, applications can override
1170 * this later. Note that child processes have SIGPIPE restored
1171 * to the default after vfork().
1173 (void)signal(SIGPIPE
, SIG_IGN
);
1176 Jim_CreateCommand(interp
, "exec", Jim_ExecCmd
, JimAllocWaitInfoTable(), JimFreeWaitInfoTable
);
1180 #if defined(__MINGW32__)
1181 /* Windows-specific (mingw) implementation */
1183 static SECURITY_ATTRIBUTES
*JimStdSecAttrs(void)
1185 static SECURITY_ATTRIBUTES secAtts
;
1187 secAtts
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
1188 secAtts
.lpSecurityDescriptor
= NULL
;
1189 secAtts
.bInheritHandle
= TRUE
;
1193 static int JimErrno(void)
1195 switch (GetLastError()) {
1196 case ERROR_FILE_NOT_FOUND
: return ENOENT
;
1197 case ERROR_PATH_NOT_FOUND
: return ENOENT
;
1198 case ERROR_TOO_MANY_OPEN_FILES
: return EMFILE
;
1199 case ERROR_ACCESS_DENIED
: return EACCES
;
1200 case ERROR_INVALID_HANDLE
: return EBADF
;
1201 case ERROR_BAD_ENVIRONMENT
: return E2BIG
;
1202 case ERROR_BAD_FORMAT
: return ENOEXEC
;
1203 case ERROR_INVALID_ACCESS
: return EACCES
;
1204 case ERROR_INVALID_DRIVE
: return ENOENT
;
1205 case ERROR_CURRENT_DIRECTORY
: return EACCES
;
1206 case ERROR_NOT_SAME_DEVICE
: return EXDEV
;
1207 case ERROR_NO_MORE_FILES
: return ENOENT
;
1208 case ERROR_WRITE_PROTECT
: return EROFS
;
1209 case ERROR_BAD_UNIT
: return ENXIO
;
1210 case ERROR_NOT_READY
: return EBUSY
;
1211 case ERROR_BAD_COMMAND
: return EIO
;
1212 case ERROR_CRC
: return EIO
;
1213 case ERROR_BAD_LENGTH
: return EIO
;
1214 case ERROR_SEEK
: return EIO
;
1215 case ERROR_WRITE_FAULT
: return EIO
;
1216 case ERROR_READ_FAULT
: return EIO
;
1217 case ERROR_GEN_FAILURE
: return EIO
;
1218 case ERROR_SHARING_VIOLATION
: return EACCES
;
1219 case ERROR_LOCK_VIOLATION
: return EACCES
;
1220 case ERROR_SHARING_BUFFER_EXCEEDED
: return ENFILE
;
1221 case ERROR_HANDLE_DISK_FULL
: return ENOSPC
;
1222 case ERROR_NOT_SUPPORTED
: return ENODEV
;
1223 case ERROR_REM_NOT_LIST
: return EBUSY
;
1224 case ERROR_DUP_NAME
: return EEXIST
;
1225 case ERROR_BAD_NETPATH
: return ENOENT
;
1226 case ERROR_NETWORK_BUSY
: return EBUSY
;
1227 case ERROR_DEV_NOT_EXIST
: return ENODEV
;
1228 case ERROR_TOO_MANY_CMDS
: return EAGAIN
;
1229 case ERROR_ADAP_HDW_ERR
: return EIO
;
1230 case ERROR_BAD_NET_RESP
: return EIO
;
1231 case ERROR_UNEXP_NET_ERR
: return EIO
;
1232 case ERROR_NETNAME_DELETED
: return ENOENT
;
1233 case ERROR_NETWORK_ACCESS_DENIED
: return EACCES
;
1234 case ERROR_BAD_DEV_TYPE
: return ENODEV
;
1235 case ERROR_BAD_NET_NAME
: return ENOENT
;
1236 case ERROR_TOO_MANY_NAMES
: return ENFILE
;
1237 case ERROR_TOO_MANY_SESS
: return EIO
;
1238 case ERROR_SHARING_PAUSED
: return EAGAIN
;
1239 case ERROR_REDIR_PAUSED
: return EAGAIN
;
1240 case ERROR_FILE_EXISTS
: return EEXIST
;
1241 case ERROR_CANNOT_MAKE
: return ENOSPC
;
1242 case ERROR_OUT_OF_STRUCTURES
: return ENFILE
;
1243 case ERROR_ALREADY_ASSIGNED
: return EEXIST
;
1244 case ERROR_INVALID_PASSWORD
: return EPERM
;
1245 case ERROR_NET_WRITE_FAULT
: return EIO
;
1246 case ERROR_NO_PROC_SLOTS
: return EAGAIN
;
1247 case ERROR_DISK_CHANGE
: return EXDEV
;
1248 case ERROR_BROKEN_PIPE
: return EPIPE
;
1249 case ERROR_OPEN_FAILED
: return ENOENT
;
1250 case ERROR_DISK_FULL
: return ENOSPC
;
1251 case ERROR_NO_MORE_SEARCH_HANDLES
: return EMFILE
;
1252 case ERROR_INVALID_TARGET_HANDLE
: return EBADF
;
1253 case ERROR_INVALID_NAME
: return ENOENT
;
1254 case ERROR_PROC_NOT_FOUND
: return ESRCH
;
1255 case ERROR_WAIT_NO_CHILDREN
: return ECHILD
;
1256 case ERROR_CHILD_NOT_COMPLETE
: return ECHILD
;
1257 case ERROR_DIRECT_ACCESS_HANDLE
: return EBADF
;
1258 case ERROR_SEEK_ON_DEVICE
: return ESPIPE
;
1259 case ERROR_BUSY_DRIVE
: return EAGAIN
;
1260 case ERROR_DIR_NOT_EMPTY
: return EEXIST
;
1261 case ERROR_NOT_LOCKED
: return EACCES
;
1262 case ERROR_BAD_PATHNAME
: return ENOENT
;
1263 case ERROR_LOCK_FAILED
: return EACCES
;
1264 case ERROR_ALREADY_EXISTS
: return EEXIST
;
1265 case ERROR_FILENAME_EXCED_RANGE
: return ENAMETOOLONG
;
1266 case ERROR_BAD_PIPE
: return EPIPE
;
1267 case ERROR_PIPE_BUSY
: return EAGAIN
;
1268 case ERROR_PIPE_NOT_CONNECTED
: return EPIPE
;
1269 case ERROR_DIRECTORY
: return ENOTDIR
;
1274 static int JimPipe(fdtype pipefd
[2])
1276 if (CreatePipe(&pipefd
[0], &pipefd
[1], NULL
, 0)) {
1282 static fdtype
JimDupFd(fdtype infd
)
1285 pidtype pid
= GetCurrentProcess();
1287 if (DuplicateHandle(pid
, infd
, pid
, &dupfd
, 0, TRUE
, DUPLICATE_SAME_ACCESS
)) {
1293 static int JimRewindFd(fdtype fd
)
1295 return SetFilePointer(fd
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
? -1 : 0;
1299 static int JimReadFd(fdtype fd
, char *buffer
, size_t len
)
1303 if (ReadFile(fd
, buffer
, len
, &num
, NULL
)) {
1306 if (GetLastError() == ERROR_HANDLE_EOF
|| GetLastError() == ERROR_BROKEN_PIPE
) {
1313 static FILE *JimFdOpenForRead(fdtype fd
)
1315 return _fdopen(_open_osfhandle((int)fd
, _O_RDONLY
| _O_TEXT
), "r");
1318 static fdtype
JimFileno(FILE *fh
)
1320 return (fdtype
)_get_osfhandle(_fileno(fh
));
1323 static fdtype
JimOpenForRead(const char *filename
)
1325 return CreateFile(filename
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1326 JimStdSecAttrs(), OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1329 static fdtype
JimOpenForWrite(const char *filename
, int append
)
1331 fdtype fd
= CreateFile(filename
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1332 JimStdSecAttrs(), append
? OPEN_ALWAYS
: CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, (HANDLE
) NULL
);
1333 if (append
&& fd
!= JIM_BAD_FD
) {
1334 SetFilePointer(fd
, 0, NULL
, FILE_END
);
1339 static FILE *JimFdOpenForWrite(fdtype fd
)
1341 return _fdopen(_open_osfhandle((int)fd
, _O_TEXT
), "w");
1344 static pidtype
JimWaitPid(pidtype pid
, int *status
, int nohang
)
1346 DWORD ret
= WaitForSingleObject(pid
, nohang
? 0 : INFINITE
);
1347 if (ret
== WAIT_TIMEOUT
|| ret
== WAIT_FAILED
) {
1348 /* WAIT_TIMEOUT can only happend with WNOHANG */
1351 GetExitCodeProcess(pid
, &ret
);
1357 static HANDLE
JimCreateTemp(Jim_Interp
*interp
, const char *contents
, int len
)
1359 char name
[MAX_PATH
];
1362 if (!GetTempPath(MAX_PATH
, name
) || !GetTempFileName(name
, "JIM", 0, name
)) {
1366 handle
= CreateFile(name
, GENERIC_READ
| GENERIC_WRITE
, 0, JimStdSecAttrs(),
1367 CREATE_ALWAYS
, FILE_ATTRIBUTE_TEMPORARY
| FILE_FLAG_DELETE_ON_CLOSE
,
1370 if (handle
== INVALID_HANDLE_VALUE
) {
1374 if (contents
!= NULL
) {
1375 /* Use fdopen() to get automatic text-mode translation */
1376 FILE *fh
= JimFdOpenForWrite(JimDupFd(handle
));
1381 if (fwrite(contents
, len
, 1, fh
) != 1) {
1385 fseek(fh
, 0, SEEK_SET
);
1391 Jim_SetResultErrno(interp
, "failed to create temp file");
1392 CloseHandle(handle
);
1398 JimWinFindExecutable(const char *originalName
, char fullPath
[MAX_PATH
])
1401 static char extensions
[][5] = {".exe", "", ".bat"};
1403 for (i
= 0; i
< (int) (sizeof(extensions
) / sizeof(extensions
[0])); i
++) {
1404 snprintf(fullPath
, MAX_PATH
, "%s%s", originalName
, extensions
[i
]);
1406 if (SearchPath(NULL
, fullPath
, NULL
, MAX_PATH
, fullPath
, NULL
) == 0) {
1409 if (GetFileAttributes(fullPath
) & FILE_ATTRIBUTE_DIRECTORY
) {
1418 static char **JimSaveEnv(char **env
)
1423 static void JimRestoreEnv(char **env
)
1425 JimFreeEnv(env
, Jim_GetEnviron());
1429 JimWinBuildCommandLine(Jim_Interp
*interp
, char **argv
)
1431 char *start
, *special
;
1434 Jim_Obj
*strObj
= Jim_NewStringObj(interp
, "", 0);
1436 for (i
= 0; argv
[i
]; i
++) {
1438 Jim_AppendString(interp
, strObj
, " ", 1);
1441 if (argv
[i
][0] == '\0') {
1446 for (start
= argv
[i
]; *start
!= '\0'; start
++) {
1447 if (isspace(UCHAR(*start
))) {
1454 Jim_AppendString(interp
, strObj
, "\"" , 1);
1458 for (special
= argv
[i
]; ; ) {
1459 if ((*special
== '\\') && (special
[1] == '\\' ||
1460 special
[1] == '"' || (quote
&& special
[1] == '\0'))) {
1461 Jim_AppendString(interp
, strObj
, start
, special
- start
);
1465 if (*special
== '"' || (quote
&& *special
== '\0')) {
1467 * N backslashes followed a quote -> insert
1468 * N * 2 + 1 backslashes then a quote.
1471 Jim_AppendString(interp
, strObj
, start
, special
- start
);
1474 if (*special
!= '\\') {
1478 Jim_AppendString(interp
, strObj
, start
, special
- start
);
1481 if (*special
== '"') {
1482 if (special
== start
) {
1483 Jim_AppendString(interp
, strObj
, "\"", 1);
1486 Jim_AppendString(interp
, strObj
, start
, special
- start
);
1488 Jim_AppendString(interp
, strObj
, "\\\"", 2);
1489 start
= special
+ 1;
1491 if (*special
== '\0') {
1496 Jim_AppendString(interp
, strObj
, start
, special
- start
);
1498 Jim_AppendString(interp
, strObj
, "\"", 1);
1505 JimStartWinProcess(Jim_Interp
*interp
, char **argv
, char *env
, fdtype inputId
, fdtype outputId
, fdtype errorId
)
1507 STARTUPINFO startInfo
;
1508 PROCESS_INFORMATION procInfo
;
1510 char execPath
[MAX_PATH
];
1511 pidtype pid
= JIM_BAD_PID
;
1512 Jim_Obj
*cmdLineObj
;
1514 if (JimWinFindExecutable(argv
[0], execPath
) < 0) {
1519 hProcess
= GetCurrentProcess();
1520 cmdLineObj
= JimWinBuildCommandLine(interp
, argv
);
1523 * STARTF_USESTDHANDLES must be used to pass handles to child process.
1524 * Using SetStdHandle() and/or dup2() only works when a console mode
1525 * parent process is spawning an attached console mode child process.
1528 ZeroMemory(&startInfo
, sizeof(startInfo
));
1529 startInfo
.cb
= sizeof(startInfo
);
1530 startInfo
.dwFlags
= STARTF_USESTDHANDLES
;
1531 startInfo
.hStdInput
= INVALID_HANDLE_VALUE
;
1532 startInfo
.hStdOutput
= INVALID_HANDLE_VALUE
;
1533 startInfo
.hStdError
= INVALID_HANDLE_VALUE
;
1536 * Duplicate all the handles which will be passed off as stdin, stdout
1537 * and stderr of the child process. The duplicate handles are set to
1538 * be inheritable, so the child process can use them.
1540 if (inputId
== JIM_BAD_FD
) {
1541 if (CreatePipe(&startInfo
.hStdInput
, &h
, JimStdSecAttrs(), 0) != FALSE
) {
1545 DuplicateHandle(hProcess
, inputId
, hProcess
, &startInfo
.hStdInput
,
1546 0, TRUE
, DUPLICATE_SAME_ACCESS
);
1548 if (startInfo
.hStdInput
== JIM_BAD_FD
) {
1552 if (outputId
== JIM_BAD_FD
) {
1553 startInfo
.hStdOutput
= CreateFile("NUL:", GENERIC_WRITE
, 0,
1554 JimStdSecAttrs(), OPEN_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1556 DuplicateHandle(hProcess
, outputId
, hProcess
, &startInfo
.hStdOutput
,
1557 0, TRUE
, DUPLICATE_SAME_ACCESS
);
1559 if (startInfo
.hStdOutput
== JIM_BAD_FD
) {
1563 if (errorId
== JIM_BAD_FD
) {
1565 * If handle was not set, errors should be sent to an infinitely
1569 startInfo
.hStdError
= CreateFile("NUL:", GENERIC_WRITE
, 0,
1570 JimStdSecAttrs(), OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1572 DuplicateHandle(hProcess
, errorId
, hProcess
, &startInfo
.hStdError
,
1573 0, TRUE
, DUPLICATE_SAME_ACCESS
);
1575 if (startInfo
.hStdError
== JIM_BAD_FD
) {
1579 if (!CreateProcess(NULL
, (char *)Jim_String(cmdLineObj
), NULL
, NULL
, TRUE
,
1580 0, env
, NULL
, &startInfo
, &procInfo
)) {
1585 * "When an application spawns a process repeatedly, a new thread
1586 * instance will be created for each process but the previous
1587 * instances may not be cleaned up. This results in a significant
1588 * virtual memory loss each time the process is spawned. If there
1589 * is a WaitForInputIdle() call between CreateProcess() and
1590 * CloseHandle(), the problem does not occur." PSS ID Number: Q124121
1593 WaitForInputIdle(procInfo
.hProcess
, 5000);
1594 CloseHandle(procInfo
.hThread
);
1596 pid
= procInfo
.hProcess
;
1599 Jim_FreeNewObj(interp
, cmdLineObj
);
1600 if (startInfo
.hStdInput
!= JIM_BAD_FD
) {
1601 CloseHandle(startInfo
.hStdInput
);
1603 if (startInfo
.hStdOutput
!= JIM_BAD_FD
) {
1604 CloseHandle(startInfo
.hStdOutput
);
1606 if (startInfo
.hStdError
!= JIM_BAD_FD
) {
1607 CloseHandle(startInfo
.hStdError
);
1612 /* Unix-specific implementation */
1613 static int JimOpenForWrite(const char *filename
, int append
)
1615 return open(filename
, O_WRONLY
| O_CREAT
| (append
? O_APPEND
: O_TRUNC
), 0666);
1618 static int JimRewindFd(int fd
)
1620 return lseek(fd
, 0L, SEEK_SET
);
1623 static int JimCreateTemp(Jim_Interp
*interp
, const char *contents
, int len
)
1625 int fd
= Jim_MakeTempFile(interp
, NULL
);
1627 if (fd
!= JIM_BAD_FD
) {
1628 unlink(Jim_String(Jim_GetResult(interp
)));
1630 if (write(fd
, contents
, len
) != len
) {
1631 Jim_SetResultErrno(interp
, "couldn't write temp file");
1635 lseek(fd
, 0L, SEEK_SET
);
1641 static char **JimSaveEnv(char **env
)
1643 char **saveenv
= Jim_GetEnviron();
1644 Jim_SetEnviron(env
);
1648 static void JimRestoreEnv(char **env
)
1650 JimFreeEnv(Jim_GetEnviron(), env
);
1651 Jim_SetEnviron(env
);