usbmodeswitch: Updated to v.1.2.6 from shibby's branch.
[tomato.git] / release / src / router / usbmodeswitch / jim / jim-exec.c
blob90881564a448395af24ed4977c0b5c18456e7c33
1 /*
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.
7 * From that code:
9 * The Tcl_Fork and Tcl_WaitPids procedures are based on code
10 * contributed by Karl Lehenbauer, Mark Diekhans and Peter
11 * da Silva.
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.
23 #include <string.h>
24 #include <ctype.h>
26 #include "jim.h"
27 #include "jimautoconf.h"
29 #if (!defined(HAVE_VFORK) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
30 /* Poor man's implementation of exec with system()
31 * The system() call *may* do command line redirection, etc.
32 * The standard output is not available.
33 * Can't redirect filehandles.
35 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
37 Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
38 int i, j;
39 int rc;
41 /* Create a quoted command line */
42 for (i = 1; i < argc; i++) {
43 int len;
44 const char *arg = Jim_GetString(argv[i], &len);
46 if (i > 1) {
47 Jim_AppendString(interp, cmdlineObj, " ", 1);
49 if (strpbrk(arg, "\\\" ") == NULL) {
50 /* No quoting required */
51 Jim_AppendString(interp, cmdlineObj, arg, len);
52 continue;
55 Jim_AppendString(interp, cmdlineObj, "\"", 1);
56 for (j = 0; j < len; j++) {
57 if (arg[j] == '\\' || arg[j] == '"') {
58 Jim_AppendString(interp, cmdlineObj, "\\", 1);
60 Jim_AppendString(interp, cmdlineObj, &arg[j], 1);
62 Jim_AppendString(interp, cmdlineObj, "\"", 1);
64 rc = system(Jim_String(cmdlineObj));
66 Jim_FreeNewObj(interp, cmdlineObj);
68 if (rc) {
69 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
70 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
71 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0));
72 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc));
73 Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
74 return JIM_ERR;
77 return JIM_OK;
80 int Jim_execInit(Jim_Interp *interp)
82 if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
83 return JIM_ERR;
84 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
85 return JIM_OK;
87 #else
88 /* Full exec implementation for unix and mingw */
90 #include <errno.h>
91 #include <signal.h>
93 #define XXX printf("@%s:%d\n", __FILE__, __LINE__); fflush(stdout);
95 #if defined(__MINGW32__)
96 /* XXX: Should we use this implementation for cygwin too? */
97 #include <fcntl.h>
99 typedef HANDLE fdtype;
100 typedef HANDLE pidtype;
101 #define JIM_BAD_FD INVALID_HANDLE_VALUE
102 #define JIM_BAD_PID INVALID_HANDLE_VALUE
103 #define JimCloseFd CloseHandle
105 #define WIFEXITED(STATUS) 1
106 #define WEXITSTATUS(STATUS) (STATUS)
107 #define WIFSIGNALED(STATUS) 0
108 #define WTERMSIG(STATUS) 0
109 #define WNOHANG 1
111 static fdtype JimFileno(FILE *fh);
112 static pidtype JimWaitPid(pidtype pid, int *status, int nohang);
113 static fdtype JimDupFd(fdtype infd);
114 static fdtype JimOpenForRead(const char *filename);
115 static FILE *JimFdOpenForRead(fdtype fd);
116 static int JimPipe(fdtype pipefd[2]);
117 static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char *env,
118 fdtype inputId, fdtype outputId, fdtype errorId);
119 static int JimErrno(void);
120 #else
121 #include "jim-signal.h"
122 #include <unistd.h>
123 #include <fcntl.h>
124 #include <sys/wait.h>
126 typedef int fdtype;
127 typedef int pidtype;
128 #define JimPipe pipe
129 #define JimErrno() errno
130 #define JIM_BAD_FD -1
131 #define JIM_BAD_PID -1
132 #define JimFileno fileno
133 #define JimReadFd read
134 #define JimCloseFd close
135 #define JimWaitPid waitpid
136 #define JimDupFd dup
137 #define JimFdOpenForRead(FD) fdopen((FD), "r")
138 #define JimOpenForRead(NAME) open((NAME), O_RDONLY, 0)
139 #endif
141 static const char *JimStrError(void);
142 static char **JimSaveEnv(char **env);
143 static void JimRestoreEnv(char **env);
144 static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
145 pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr);
146 static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr);
147 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId);
148 static fdtype JimCreateTemp(Jim_Interp *interp, const char *contents);
149 static fdtype JimOpenForWrite(const char *filename, int append);
150 static int JimRewindFd(fdtype fd);
152 static void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
154 Jim_SetResultFormatted(interp, "%s: %s", msg, JimStrError());
157 static const char *JimStrError(void)
159 return strerror(JimErrno());
162 static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
164 int len;
165 const char *s = Jim_GetString(objPtr, &len);
167 if (len > 0 && s[len - 1] == '\n') {
168 objPtr->length--;
169 objPtr->bytes[objPtr->length] = '\0';
174 * Read from 'fd', append the data to strObj and close 'fd'.
175 * Returns JIM_OK if OK, or JIM_ERR on error.
177 static int JimAppendStreamToString(Jim_Interp *interp, fdtype fd, Jim_Obj *strObj)
179 char buf[256];
180 FILE *fh = JimFdOpenForRead(fd);
181 if (fh == NULL) {
182 return JIM_ERR;
185 while (1) {
186 int retval = fread(buf, 1, sizeof(buf), fh);
187 if (retval > 0) {
188 Jim_AppendString(interp, strObj, buf, retval);
190 if (retval != sizeof(buf)) {
191 break;
194 Jim_RemoveTrailingNewline(strObj);
195 fclose(fh);
196 return JIM_OK;
200 * If the last character of the result is a newline, then remove
201 * the newline character (the newline would just confuse things).
203 * Note: Ideally we could do this by just reducing the length of stringrep
204 * by 1, but there is no API for this :-(
206 static void JimTrimTrailingNewline(Jim_Interp *interp)
208 int len;
209 const char *p = Jim_GetString(Jim_GetResult(interp), &len);
211 if (len > 0 && p[len - 1] == '\n') {
212 Jim_SetResultString(interp, p, len - 1);
217 * Builds the environment array from $::env
219 * If $::env is not set, simply returns environ.
221 * Otherwise allocates the environ array from the contents of $::env
223 * If the exec fails, memory can be freed via JimFreeEnv()
225 static char **JimBuildEnv(Jim_Interp *interp)
227 #if defined(jim_ext_tclcompat)
228 int i;
229 int size;
230 int num;
231 int n;
232 char **envptr;
233 char *envdata;
235 Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);
237 if (!objPtr) {
238 return Jim_GetEnviron();
241 /* We build the array as a single block consisting of the pointers followed by
242 * the strings. This has the advantage of being easy to allocate/free and being
243 * compatible with both unix and windows
246 /* Calculate the required size */
247 num = Jim_ListLength(interp, objPtr);
248 if (num % 2) {
249 num--;
251 size = Jim_Length(objPtr);
252 /* We need one \0 and one equal sign for each element.
253 * A list has at least one space for each element except the first.
254 * We only need one extra char for the extra null terminator.
256 size++;
258 envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
259 envdata = (char *)&envptr[num / 2 + 1];
261 n = 0;
262 for (i = 0; i < num; i += 2) {
263 const char *s1, *s2;
264 Jim_Obj *elemObj;
266 Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE);
267 s1 = Jim_String(elemObj);
268 Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE);
269 s2 = Jim_String(elemObj);
271 envptr[n] = envdata;
272 envdata += sprintf(envdata, "%s=%s", s1, s2);
273 envdata++;
274 n++;
276 envptr[n] = NULL;
277 *envdata = 0;
279 return envptr;
280 #else
281 return Jim_GetEnviron();
282 #endif
286 * Frees the environment allocated by JimBuildEnv()
288 * Must pass original_environ.
290 static void JimFreeEnv(char **env, char **original_environ)
292 #ifdef jim_ext_tclcompat
293 if (env != original_environ) {
294 Jim_Free(env);
296 #endif
300 * Create error messages for unusual process exits. An
301 * extra newline gets appended to each error message, but
302 * it gets removed below (in the same fashion that an
303 * extra newline in the command's output is removed).
305 static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus)
307 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
308 int rc = JIM_ERR;
310 if (WIFEXITED(waitStatus)) {
311 if (WEXITSTATUS(waitStatus) == 0) {
312 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1));
313 rc = JIM_OK;
315 else {
316 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
317 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
318 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
321 else {
322 const char *type;
323 const char *action;
325 if (WIFSIGNALED(waitStatus)) {
326 type = "CHILDKILLED";
327 action = "killed";
329 else {
330 type = "CHILDSUSP";
331 action = "suspended";
334 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
336 #ifdef jim_ext_signal
337 Jim_SetResultFormatted(interp, "child %s by signal %s", action, Jim_SignalId(WTERMSIG(waitStatus)));
338 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalId(WTERMSIG(waitStatus)), -1));
339 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
340 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalName(WTERMSIG(waitStatus)), -1));
341 #else
342 Jim_SetResultFormatted(interp, "child %s by signal %d", action, WTERMSIG(waitStatus));
343 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus)));
344 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
345 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus)));
346 #endif
348 Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
349 return rc;
353 * Data structures of the following type are used by JimFork and
354 * JimWaitPids to keep track of child processes.
357 struct WaitInfo
359 pidtype pid; /* Process id of child. */
360 int status; /* Status returned when child exited or suspended. */
361 int flags; /* Various flag bits; see below for definitions. */
364 struct WaitInfoTable {
365 struct WaitInfo *info;
366 int size;
367 int used;
371 * Flag bits in WaitInfo structures:
373 * WI_DETACHED - Non-zero means no-one cares about the
374 * process anymore. Ignore it until it
375 * exits, then forget about it.
378 #define WI_DETACHED 2
380 #define WAIT_TABLE_GROW_BY 4
382 static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
384 struct WaitInfoTable *table = privData;
386 Jim_Free(table->info);
387 Jim_Free(table);
390 static struct WaitInfoTable *JimAllocWaitInfoTable(void)
392 struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
393 table->info = NULL;
394 table->size = table->used = 0;
396 return table;
400 * The main [exec] command
402 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
404 fdtype outputId; /* File id for output pipe. -1
405 * means command overrode. */
406 fdtype errorId; /* File id for temporary file
407 * containing error output. */
408 pidtype *pidPtr;
409 int numPids, result;
412 * See if the command is to be run in background; if so, create
413 * the command, detach it, and return.
415 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
416 Jim_Obj *listObj;
417 int i;
419 argc--;
420 numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
421 if (numPids < 0) {
422 return JIM_ERR;
424 /* The return value is a list of the pids */
425 listObj = Jim_NewListObj(interp, NULL, 0);
426 for (i = 0; i < numPids; i++) {
427 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i]));
429 Jim_SetResult(interp, listObj);
430 JimDetachPids(interp, numPids, pidPtr);
431 Jim_Free(pidPtr);
432 return JIM_OK;
436 * Create the command's pipeline.
438 numPids =
439 JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
441 if (numPids < 0) {
442 return JIM_ERR;
446 * Read the child's output (if any) and put it into the result.
448 Jim_SetResultString(interp, "", 0);
450 result = JIM_OK;
451 if (outputId != JIM_BAD_FD) {
452 result = JimAppendStreamToString(interp, outputId, Jim_GetResult(interp));
453 if (result < 0) {
454 Jim_SetResultErrno(interp, "error reading from output pipe");
458 if (JimCleanupChildren(interp, numPids, pidPtr, errorId) != JIM_OK) {
459 result = JIM_ERR;
461 return result;
464 static void JimReapDetachedPids(struct WaitInfoTable *table)
466 struct WaitInfo *waitPtr;
467 int count;
469 if (!table) {
470 return;
473 for (waitPtr = table->info, count = table->used; count > 0; waitPtr++, count--) {
474 if (waitPtr->flags & WI_DETACHED) {
475 int status;
476 pidtype pid = JimWaitPid(waitPtr->pid, &status, WNOHANG);
477 if (pid != JIM_BAD_PID) {
478 if (waitPtr != &table->info[table->used - 1]) {
479 *waitPtr = table->info[table->used - 1];
481 table->used--;
488 * Does waitpid() on the given pid, and then removes the
489 * entry from the wait table.
491 * Returns the pid if OK and updates *statusPtr with the status,
492 * or JIM_BAD_PID if the pid was not in the table.
494 static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr)
496 int i;
498 /* Find it in the table */
499 for (i = 0; i < table->used; i++) {
500 if (pid == table->info[i].pid) {
501 /* wait for it */
502 JimWaitPid(pid, statusPtr, 0);
504 /* Remove it from the table */
505 if (i != table->used - 1) {
506 table->info[i] = table->info[table->used - 1];
508 table->used--;
509 return pid;
513 /* Not found */
514 return JIM_BAD_PID;
518 *----------------------------------------------------------------------
520 * JimDetachPids --
522 * This procedure is called to indicate that one or more child
523 * processes have been placed in background and are no longer
524 * cared about. These children can be cleaned up with JimReapDetachedPids().
526 * Results:
527 * None.
529 * Side effects:
530 * None.
532 *----------------------------------------------------------------------
535 static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr)
537 int j;
538 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
540 for (j = 0; j < numPids; j++) {
541 /* Find it in the table */
542 int i;
543 for (i = 0; i < table->used; i++) {
544 if (pidPtr[j] == table->info[i].pid) {
545 table->info[i].flags |= WI_DETACHED;
546 break;
553 *----------------------------------------------------------------------
555 * JimCreatePipeline --
557 * Given an argc/argv array, instantiate a pipeline of processes
558 * as described by the argv.
560 * Results:
561 * The return value is a count of the number of new processes
562 * created, or -1 if an error occurred while creating the pipeline.
563 * *pidArrayPtr is filled in with the address of a dynamically
564 * allocated array giving the ids of all of the processes. It
565 * is up to the caller to free this array when it isn't needed
566 * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in
567 * with the file id for the input pipe for the pipeline (if any):
568 * the caller must eventually close this file. If outPipePtr
569 * isn't NULL, then *outPipePtr is filled in with the file id
570 * for the output pipe from the pipeline: the caller must close
571 * this file. If errFilePtr isn't NULL, then *errFilePtr is filled
572 * with a file id that may be used to read error output after the
573 * pipeline completes.
575 * Side effects:
576 * Processes and pipes are created.
578 *----------------------------------------------------------------------
580 static int
581 JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr,
582 fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr)
584 pidtype *pidPtr = NULL; /* Points to malloc-ed array holding all
585 * the pids of child processes. */
586 int numPids = 0; /* Actual number of processes that exist
587 * at *pidPtr right now. */
588 int cmdCount; /* Count of number of distinct commands
589 * found in argc/argv. */
590 const char *input = NULL; /* Describes input for pipeline, depending
591 * on "inputFile". NULL means take input
592 * from stdin/pipe. */
594 #define FILE_NAME 0 /* input/output: filename */
595 #define FILE_APPEND 1 /* output only: filename, append */
596 #define FILE_HANDLE 2 /* input/output: filehandle */
597 #define FILE_TEXT 3 /* input only: input is actual text */
599 int inputFile = FILE_NAME; /* 1 means input is name of input file.
600 * 2 means input is filehandle name.
601 * 0 means input holds actual
602 * text to be input to command. */
604 int outputFile = FILE_NAME; /* 0 means output is the name of output file.
605 * 1 means output is the name of output file, and append.
606 * 2 means output is filehandle name.
607 * All this is ignored if output is NULL
609 int errorFile = FILE_NAME; /* 0 means error is the name of error file.
610 * 1 means error is the name of error file, and append.
611 * 2 means error is filehandle name.
612 * All this is ignored if error is NULL
614 const char *output = NULL; /* Holds name of output file to pipe to,
615 * or NULL if output goes to stdout/pipe. */
616 const char *error = NULL; /* Holds name of stderr file to pipe to,
617 * or NULL if stderr goes to stderr/pipe. */
618 fdtype inputId = JIM_BAD_FD;
619 /* Readable file id input to current command in
620 * pipeline (could be file or pipe). JIM_BAD_FD
621 * means use stdin. */
622 fdtype outputId = JIM_BAD_FD;
623 /* Writable file id for output from current
624 * command in pipeline (could be file or pipe).
625 * JIM_BAD_FD means use stdout. */
626 fdtype errorId = JIM_BAD_FD;
627 /* Writable file id for all standard error
628 * output from all commands in pipeline. JIM_BAD_FD
629 * means use stderr. */
630 fdtype lastOutputId = JIM_BAD_FD;
631 /* Write file id for output from last command
632 * in pipeline (could be file or pipe).
633 * -1 means use stdout. */
634 fdtype pipeIds[2]; /* File ids for pipe that's being created. */
635 int firstArg, lastArg; /* Indexes of first and last arguments in
636 * current command. */
637 int lastBar;
638 int i;
639 pidtype pid;
640 char **save_environ;
641 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
643 /* Holds the args which will be used to exec */
644 char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
645 int arg_count = 0;
647 JimReapDetachedPids(table);
649 if (inPipePtr != NULL) {
650 *inPipePtr = JIM_BAD_FD;
652 if (outPipePtr != NULL) {
653 *outPipePtr = JIM_BAD_FD;
655 if (errFilePtr != NULL) {
656 *errFilePtr = JIM_BAD_FD;
658 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
661 * First, scan through all the arguments to figure out the structure
662 * of the pipeline. Count the number of distinct processes (it's the
663 * number of "|" arguments). If there are "<", "<<", or ">" arguments
664 * then make note of input and output redirection and remove these
665 * arguments and the arguments that follow them.
667 cmdCount = 1;
668 lastBar = -1;
669 for (i = 0; i < argc; i++) {
670 const char *arg = Jim_String(argv[i]);
672 if (arg[0] == '<') {
673 inputFile = FILE_NAME;
674 input = arg + 1;
675 if (*input == '<') {
676 inputFile = FILE_TEXT;
677 input++;
679 else if (*input == '@') {
680 inputFile = FILE_HANDLE;
681 input++;
684 if (!*input && ++i < argc) {
685 input = Jim_String(argv[i]);
688 else if (arg[0] == '>') {
689 int dup_error = 0;
691 outputFile = FILE_NAME;
693 output = arg + 1;
694 if (*output == '>') {
695 outputFile = FILE_APPEND;
696 output++;
698 if (*output == '&') {
699 /* Redirect stderr too */
700 output++;
701 dup_error = 1;
703 if (*output == '@') {
704 outputFile = FILE_HANDLE;
705 output++;
707 if (!*output && ++i < argc) {
708 output = Jim_String(argv[i]);
710 if (dup_error) {
711 errorFile = outputFile;
712 error = output;
715 else if (arg[0] == '2' && arg[1] == '>') {
716 error = arg + 2;
717 errorFile = FILE_NAME;
719 if (*error == '@') {
720 errorFile = FILE_HANDLE;
721 error++;
723 else if (*error == '>') {
724 errorFile = FILE_APPEND;
725 error++;
727 if (!*error && ++i < argc) {
728 error = Jim_String(argv[i]);
731 else {
732 if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
733 if (i == lastBar + 1 || i == argc - 1) {
734 Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
735 goto badargs;
737 lastBar = i;
738 cmdCount++;
740 /* Either |, |& or a "normal" arg, so store it in the arg array */
741 arg_array[arg_count++] = (char *)arg;
742 continue;
745 if (i >= argc) {
746 Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
747 goto badargs;
751 if (arg_count == 0) {
752 Jim_SetResultString(interp, "didn't specify command to execute", -1);
753 badargs:
754 Jim_Free(arg_array);
755 return -1;
758 /* Must do this before vfork(), so do it now */
759 save_environ = JimSaveEnv(JimBuildEnv(interp));
762 * Set up the redirected input source for the pipeline, if
763 * so requested.
765 if (input != NULL) {
766 if (inputFile == FILE_TEXT) {
768 * Immediate data in command. Create temporary file and
769 * put data into file.
771 inputId = JimCreateTemp(interp, input);
772 if (inputId == JIM_BAD_FD) {
773 goto error;
776 else if (inputFile == FILE_HANDLE) {
777 /* Should be a file descriptor */
778 Jim_Obj *fhObj = Jim_NewStringObj(interp, input, -1);
779 FILE *fh = Jim_AioFilehandle(interp, fhObj);
781 Jim_FreeNewObj(interp, fhObj);
782 if (fh == NULL) {
783 goto error;
785 inputId = JimDupFd(JimFileno(fh));
787 else {
789 * File redirection. Just open the file.
791 inputId = JimOpenForRead(input);
792 if (inputId == JIM_BAD_FD) {
793 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, JimStrError());
794 goto error;
798 else if (inPipePtr != NULL) {
799 if (JimPipe(pipeIds) != 0) {
800 Jim_SetResultErrno(interp, "couldn't create input pipe for command");
801 goto error;
803 inputId = pipeIds[0];
804 *inPipePtr = pipeIds[1];
805 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
809 * Set up the redirected output sink for the pipeline from one
810 * of two places, if requested.
812 if (output != NULL) {
813 if (outputFile == FILE_HANDLE) {
814 Jim_Obj *fhObj = Jim_NewStringObj(interp, output, -1);
815 FILE *fh = Jim_AioFilehandle(interp, fhObj);
817 Jim_FreeNewObj(interp, fhObj);
818 if (fh == NULL) {
819 goto error;
821 fflush(fh);
822 lastOutputId = JimDupFd(JimFileno(fh));
824 else {
826 * Output is to go to a file.
828 lastOutputId = JimOpenForWrite(output, outputFile == FILE_APPEND);
829 if (lastOutputId == JIM_BAD_FD) {
830 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, JimStrError());
831 goto error;
835 else if (outPipePtr != NULL) {
837 * Output is to go to a pipe.
839 if (JimPipe(pipeIds) != 0) {
840 Jim_SetResultErrno(interp, "couldn't create output pipe");
841 goto error;
843 lastOutputId = pipeIds[1];
844 *outPipePtr = pipeIds[0];
845 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
847 /* If we are redirecting stderr with 2>filename or 2>@fileId, then we ignore errFilePtr */
848 if (error != NULL) {
849 if (errorFile == FILE_HANDLE) {
850 if (strcmp(error, "1") == 0) {
851 /* Special 2>@1 */
852 if (lastOutputId != JIM_BAD_FD) {
853 errorId = JimDupFd(lastOutputId);
855 else {
856 /* No redirection of stdout, so just use 2>@stdout */
857 error = "stdout";
860 if (errorId == JIM_BAD_FD) {
861 Jim_Obj *fhObj = Jim_NewStringObj(interp, error, -1);
862 FILE *fh = Jim_AioFilehandle(interp, fhObj);
864 Jim_FreeNewObj(interp, fhObj);
865 if (fh == NULL) {
866 goto error;
868 fflush(fh);
869 errorId = JimDupFd(JimFileno(fh));
872 else {
874 * Output is to go to a file.
876 errorId = JimOpenForWrite(error, errorFile == FILE_APPEND);
877 if (errorId == JIM_BAD_FD) {
878 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError());
879 goto error;
883 else if (errFilePtr != NULL) {
885 * Set up the standard error output sink for the pipeline, if
886 * requested. Use a temporary file which is opened, then deleted.
887 * Could potentially just use pipe, but if it filled up it could
888 * cause the pipeline to deadlock: we'd be waiting for processes
889 * to complete before reading stderr, and processes couldn't complete
890 * because stderr was backed up.
892 errorId = JimCreateTemp(interp, NULL);
893 if (errorId == JIM_BAD_FD) {
894 goto error;
896 *errFilePtr = JimDupFd(errorId);
900 * Scan through the argc array, forking off a process for each
901 * group of arguments between "|" arguments.
904 pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
905 for (i = 0; i < numPids; i++) {
906 pidPtr[i] = JIM_BAD_PID;
908 for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
909 int pipe_dup_err = 0;
910 fdtype origErrorId = errorId;
912 for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
913 if (arg_array[lastArg][0] == '|') {
914 if (arg_array[lastArg][1] == '&') {
915 pipe_dup_err = 1;
917 break;
920 /* Replace | with NULL for execv() */
921 arg_array[lastArg] = NULL;
922 if (lastArg == arg_count) {
923 outputId = lastOutputId;
925 else {
926 if (JimPipe(pipeIds) != 0) {
927 Jim_SetResultErrno(interp, "couldn't create pipe");
928 goto error;
930 outputId = pipeIds[1];
933 /* Now fork the child */
935 #ifdef __MINGW32__
936 pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ ? save_environ[0] : NULL, inputId, outputId, errorId);
937 if (pid == JIM_BAD_PID) {
938 Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
939 goto error;
941 #else
943 * Disable SIGPIPE signals: if they were allowed, this process
944 * might go away unexpectedly if children misbehave. This code
945 * can potentially interfere with other application code that
946 * expects to handle SIGPIPEs; what's really needed is an
947 * arbiter for signals to allow them to be "shared".
949 if (table->info == NULL) {
950 (void)signal(SIGPIPE, SIG_IGN);
953 /* Need to do this befor vfork() */
954 if (pipe_dup_err) {
955 errorId = outputId;
959 * Make a new process and enter it into the table if the fork
960 * is successful.
962 pid = vfork();
963 if (pid < 0) {
964 Jim_SetResultErrno(interp, "couldn't fork child process");
965 goto error;
967 if (pid == 0) {
968 /* Child */
970 if (inputId != -1) dup2(inputId, 0);
971 if (outputId != -1) dup2(outputId, 1);
972 if (errorId != -1) dup2(errorId, 2);
974 for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) {
975 close(i);
978 execvp(arg_array[firstArg], &arg_array[firstArg]);
980 /* Need to prep an error message before vfork(), just in case */
981 fprintf(stderr, "couldn't exec \"%s\"", arg_array[firstArg]);
982 _exit(127);
984 #endif
986 /* parent */
989 * Enlarge the wait table if there isn't enough space for a new
990 * entry.
992 if (table->used == table->size) {
993 table->size += WAIT_TABLE_GROW_BY;
994 table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
997 table->info[table->used].pid = pid;
998 table->info[table->used].flags = 0;
999 table->used++;
1001 pidPtr[numPids] = pid;
1003 /* Restore in case of pipe_dup_err */
1004 errorId = origErrorId;
1007 * Close off our copies of file descriptors that were set up for
1008 * this child, then set up the input for the next child.
1011 if (inputId != JIM_BAD_FD) {
1012 JimCloseFd(inputId);
1014 if (outputId != JIM_BAD_FD) {
1015 JimCloseFd(outputId);
1017 inputId = pipeIds[0];
1018 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
1020 *pidArrayPtr = pidPtr;
1023 * All done. Cleanup open files lying around and then return.
1026 cleanup:
1027 if (inputId != JIM_BAD_FD) {
1028 JimCloseFd(inputId);
1030 if (lastOutputId != JIM_BAD_FD) {
1031 JimCloseFd(lastOutputId);
1033 if (errorId != JIM_BAD_FD) {
1034 JimCloseFd(errorId);
1036 Jim_Free(arg_array);
1038 JimRestoreEnv(save_environ);
1040 return numPids;
1043 * An error occurred. There could have been extra files open, such
1044 * as pipes between children. Clean them all up. Detach any child
1045 * processes that have been created.
1048 error:
1049 if ((inPipePtr != NULL) && (*inPipePtr != JIM_BAD_FD)) {
1050 JimCloseFd(*inPipePtr);
1051 *inPipePtr = JIM_BAD_FD;
1053 if ((outPipePtr != NULL) && (*outPipePtr != JIM_BAD_FD)) {
1054 JimCloseFd(*outPipePtr);
1055 *outPipePtr = JIM_BAD_FD;
1057 if ((errFilePtr != NULL) && (*errFilePtr != JIM_BAD_FD)) {
1058 JimCloseFd(*errFilePtr);
1059 *errFilePtr = JIM_BAD_FD;
1061 if (pipeIds[0] != JIM_BAD_FD) {
1062 JimCloseFd(pipeIds[0]);
1064 if (pipeIds[1] != JIM_BAD_FD) {
1065 JimCloseFd(pipeIds[1]);
1067 if (pidPtr != NULL) {
1068 for (i = 0; i < numPids; i++) {
1069 if (pidPtr[i] != JIM_BAD_PID) {
1070 JimDetachPids(interp, 1, &pidPtr[i]);
1073 Jim_Free(pidPtr);
1075 numPids = -1;
1076 goto cleanup;
1080 *----------------------------------------------------------------------
1082 * JimCleanupChildren --
1084 * This is a utility procedure used to wait for child processes
1085 * to exit, record information about abnormal exits, and then
1086 * collect any stderr output generated by them.
1088 * Results:
1089 * The return value is a standard Tcl result. If anything at
1090 * weird happened with the child processes, JIM_ERROR is returned
1091 * and a message is left in interp->result.
1093 * Side effects:
1094 * If the last character of interp->result is a newline, then it
1095 * is removed. File errorId gets closed, and pidPtr is freed
1096 * back to the storage allocator.
1098 *----------------------------------------------------------------------
1101 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId)
1103 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
1104 int result = JIM_OK;
1105 int i;
1107 for (i = 0; i < numPids; i++) {
1108 int waitStatus = 0;
1109 if (JimWaitForProcess(table, pidPtr[i], &waitStatus) != JIM_BAD_PID) {
1110 if (JimCheckWaitStatus(interp, pidPtr[i], waitStatus) != JIM_OK) {
1111 result = JIM_ERR;
1115 Jim_Free(pidPtr);
1118 * Read the standard error file. If there's anything there,
1119 * then add the file's contents to the result
1120 * string.
1122 if (errorId != JIM_BAD_FD) {
1123 JimRewindFd(errorId);
1124 if (JimAppendStreamToString(interp, errorId, Jim_GetResult(interp)) != JIM_OK) {
1125 result = JIM_ERR;
1129 JimTrimTrailingNewline(interp);
1131 return result;
1134 int Jim_execInit(Jim_Interp *interp)
1136 if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
1137 return JIM_ERR;
1138 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, JimAllocWaitInfoTable(), JimFreeWaitInfoTable);
1139 return JIM_OK;
1142 #if defined(__MINGW32__)
1143 /* Windows-specific (mingw) implementation */
1145 static SECURITY_ATTRIBUTES *JimStdSecAttrs(void)
1147 static SECURITY_ATTRIBUTES secAtts;
1149 secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
1150 secAtts.lpSecurityDescriptor = NULL;
1151 secAtts.bInheritHandle = TRUE;
1152 return &secAtts;
1155 static int JimErrno(void)
1157 switch (GetLastError()) {
1158 case ERROR_FILE_NOT_FOUND: return ENOENT;
1159 case ERROR_PATH_NOT_FOUND: return ENOENT;
1160 case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
1161 case ERROR_ACCESS_DENIED: return EACCES;
1162 case ERROR_INVALID_HANDLE: return EBADF;
1163 case ERROR_BAD_ENVIRONMENT: return E2BIG;
1164 case ERROR_BAD_FORMAT: return ENOEXEC;
1165 case ERROR_INVALID_ACCESS: return EACCES;
1166 case ERROR_INVALID_DRIVE: return ENOENT;
1167 case ERROR_CURRENT_DIRECTORY: return EACCES;
1168 case ERROR_NOT_SAME_DEVICE: return EXDEV;
1169 case ERROR_NO_MORE_FILES: return ENOENT;
1170 case ERROR_WRITE_PROTECT: return EROFS;
1171 case ERROR_BAD_UNIT: return ENXIO;
1172 case ERROR_NOT_READY: return EBUSY;
1173 case ERROR_BAD_COMMAND: return EIO;
1174 case ERROR_CRC: return EIO;
1175 case ERROR_BAD_LENGTH: return EIO;
1176 case ERROR_SEEK: return EIO;
1177 case ERROR_WRITE_FAULT: return EIO;
1178 case ERROR_READ_FAULT: return EIO;
1179 case ERROR_GEN_FAILURE: return EIO;
1180 case ERROR_SHARING_VIOLATION: return EACCES;
1181 case ERROR_LOCK_VIOLATION: return EACCES;
1182 case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
1183 case ERROR_HANDLE_DISK_FULL: return ENOSPC;
1184 case ERROR_NOT_SUPPORTED: return ENODEV;
1185 case ERROR_REM_NOT_LIST: return EBUSY;
1186 case ERROR_DUP_NAME: return EEXIST;
1187 case ERROR_BAD_NETPATH: return ENOENT;
1188 case ERROR_NETWORK_BUSY: return EBUSY;
1189 case ERROR_DEV_NOT_EXIST: return ENODEV;
1190 case ERROR_TOO_MANY_CMDS: return EAGAIN;
1191 case ERROR_ADAP_HDW_ERR: return EIO;
1192 case ERROR_BAD_NET_RESP: return EIO;
1193 case ERROR_UNEXP_NET_ERR: return EIO;
1194 case ERROR_NETNAME_DELETED: return ENOENT;
1195 case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
1196 case ERROR_BAD_DEV_TYPE: return ENODEV;
1197 case ERROR_BAD_NET_NAME: return ENOENT;
1198 case ERROR_TOO_MANY_NAMES: return ENFILE;
1199 case ERROR_TOO_MANY_SESS: return EIO;
1200 case ERROR_SHARING_PAUSED: return EAGAIN;
1201 case ERROR_REDIR_PAUSED: return EAGAIN;
1202 case ERROR_FILE_EXISTS: return EEXIST;
1203 case ERROR_CANNOT_MAKE: return ENOSPC;
1204 case ERROR_OUT_OF_STRUCTURES: return ENFILE;
1205 case ERROR_ALREADY_ASSIGNED: return EEXIST;
1206 case ERROR_INVALID_PASSWORD: return EPERM;
1207 case ERROR_NET_WRITE_FAULT: return EIO;
1208 case ERROR_NO_PROC_SLOTS: return EAGAIN;
1209 case ERROR_DISK_CHANGE: return EXDEV;
1210 case ERROR_BROKEN_PIPE: return EPIPE;
1211 case ERROR_OPEN_FAILED: return ENOENT;
1212 case ERROR_DISK_FULL: return ENOSPC;
1213 case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
1214 case ERROR_INVALID_TARGET_HANDLE: return EBADF;
1215 case ERROR_INVALID_NAME: return ENOENT;
1216 case ERROR_PROC_NOT_FOUND: return ESRCH;
1217 case ERROR_WAIT_NO_CHILDREN: return ECHILD;
1218 case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
1219 case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
1220 case ERROR_SEEK_ON_DEVICE: return ESPIPE;
1221 case ERROR_BUSY_DRIVE: return EAGAIN;
1222 case ERROR_DIR_NOT_EMPTY: return EEXIST;
1223 case ERROR_NOT_LOCKED: return EACCES;
1224 case ERROR_BAD_PATHNAME: return ENOENT;
1225 case ERROR_LOCK_FAILED: return EACCES;
1226 case ERROR_ALREADY_EXISTS: return EEXIST;
1227 case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
1228 case ERROR_BAD_PIPE: return EPIPE;
1229 case ERROR_PIPE_BUSY: return EAGAIN;
1230 case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
1231 case ERROR_DIRECTORY: return ENOTDIR;
1233 return EINVAL;
1236 static int JimPipe(fdtype pipefd[2])
1238 if (CreatePipe(&pipefd[0], &pipefd[1], NULL, 0)) {
1239 return 0;
1241 return -1;
1244 static fdtype JimDupFd(fdtype infd)
1246 fdtype dupfd;
1247 pidtype pid = GetCurrentProcess();
1249 if (DuplicateHandle(pid, infd, pid, &dupfd, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
1250 return dupfd;
1252 return JIM_BAD_FD;
1255 static int JimRewindFd(fdtype fd)
1257 return SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ? -1 : 0;
1260 #if 0
1261 static int JimReadFd(fdtype fd, char *buffer, size_t len)
1263 DWORD num;
1265 if (ReadFile(fd, buffer, len, &num, NULL)) {
1266 return num;
1268 if (GetLastError() == ERROR_HANDLE_EOF || GetLastError() == ERROR_BROKEN_PIPE) {
1269 return 0;
1271 return -1;
1273 #endif
1275 static FILE *JimFdOpenForRead(fdtype fd)
1277 return _fdopen(_open_osfhandle((int)fd, _O_RDONLY | _O_TEXT), "r");
1280 static fdtype JimFileno(FILE *fh)
1282 return (fdtype)_get_osfhandle(_fileno(fh));
1285 static fdtype JimOpenForRead(const char *filename)
1287 return CreateFile(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
1288 JimStdSecAttrs(), OPEN_EXISTING, 0, NULL);
1291 static fdtype JimOpenForWrite(const char *filename, int append)
1293 return CreateFile(filename, append ? FILE_APPEND_DATA : GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
1294 JimStdSecAttrs(), append ? OPEN_ALWAYS : CREATE_ALWAYS, 0, (HANDLE) NULL);
1297 static FILE *JimFdOpenForWrite(fdtype fd)
1299 return _fdopen(_open_osfhandle((int)fd, _O_TEXT), "w");
1302 static pidtype JimWaitPid(pidtype pid, int *status, int nohang)
1304 DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE);
1305 if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
1306 /* WAIT_TIMEOUT can only happend with WNOHANG */
1307 return JIM_BAD_PID;
1309 GetExitCodeProcess(pid, &ret);
1310 *status = ret;
1311 CloseHandle(pid);
1312 return pid;
1315 static HANDLE JimCreateTemp(Jim_Interp *interp, const char *contents)
1317 char name[MAX_PATH];
1318 HANDLE handle;
1320 if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, "JIM", 0, name)) {
1321 return JIM_BAD_FD;
1324 handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, JimStdSecAttrs(),
1325 CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
1326 NULL);
1328 if (handle == INVALID_HANDLE_VALUE) {
1329 goto error;
1332 if (contents != NULL) {
1333 /* Use fdopen() to get automatic text-mode translation */
1334 FILE *fh = JimFdOpenForWrite(JimDupFd(handle));
1335 if (fh == NULL) {
1336 goto error;
1339 if (fwrite(contents, strlen(contents), 1, fh) != 1) {
1340 fclose(fh);
1341 goto error;
1343 fseek(fh, 0, SEEK_SET);
1344 fclose(fh);
1346 return handle;
1348 error:
1349 Jim_SetResultErrno(interp, "failed to create temp file");
1350 CloseHandle(handle);
1351 DeleteFile(name);
1352 return JIM_BAD_FD;
1355 static int
1356 JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
1358 int i;
1359 static char extensions[][5] = {".exe", "", ".bat"};
1361 for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
1362 lstrcpyn(fullPath, originalName, MAX_PATH - 5);
1363 lstrcat(fullPath, extensions[i]);
1365 if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) {
1366 continue;
1368 if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
1369 continue;
1371 return 0;
1374 return -1;
1377 static char **JimSaveEnv(char **env)
1379 return env;
1382 static void JimRestoreEnv(char **env)
1384 JimFreeEnv(env, NULL);
1387 static Jim_Obj *
1388 JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
1390 char *start, *special;
1391 int quote, i;
1393 Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0);
1395 for (i = 0; argv[i]; i++) {
1396 if (i > 0) {
1397 Jim_AppendString(interp, strObj, " ", 1);
1400 if (argv[i][0] == '\0') {
1401 quote = 1;
1403 else {
1404 quote = 0;
1405 for (start = argv[i]; *start != '\0'; start++) {
1406 if (isspace(UCHAR(*start))) {
1407 quote = 1;
1408 break;
1412 if (quote) {
1413 Jim_AppendString(interp, strObj, "\"" , 1);
1416 start = argv[i];
1417 for (special = argv[i]; ; ) {
1418 if ((*special == '\\') && (special[1] == '\\' ||
1419 special[1] == '"' || (quote && special[1] == '\0'))) {
1420 Jim_AppendString(interp, strObj, start, special - start);
1421 start = special;
1422 while (1) {
1423 special++;
1424 if (*special == '"' || (quote && *special == '\0')) {
1426 * N backslashes followed a quote -> insert
1427 * N * 2 + 1 backslashes then a quote.
1430 Jim_AppendString(interp, strObj, start, special - start);
1431 break;
1433 if (*special != '\\') {
1434 break;
1437 Jim_AppendString(interp, strObj, start, special - start);
1438 start = special;
1440 if (*special == '"') {
1441 if (special == start) {
1442 Jim_AppendString(interp, strObj, "\"", 1);
1444 else {
1445 Jim_AppendString(interp, strObj, start, special - start);
1447 Jim_AppendString(interp, strObj, "\\\"", 2);
1448 start = special + 1;
1450 if (*special == '\0') {
1451 break;
1453 special++;
1455 Jim_AppendString(interp, strObj, start, special - start);
1456 if (quote) {
1457 Jim_AppendString(interp, strObj, "\"", 1);
1460 return strObj;
1463 static pidtype
1464 JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, fdtype inputId, fdtype outputId, fdtype errorId)
1466 STARTUPINFO startInfo;
1467 PROCESS_INFORMATION procInfo;
1468 HANDLE hProcess, h;
1469 char execPath[MAX_PATH];
1470 char *originalName;
1471 pidtype pid = JIM_BAD_PID;
1472 Jim_Obj *cmdLineObj;
1474 if (JimWinFindExecutable(argv[0], execPath) < 0) {
1475 return JIM_BAD_PID;
1477 originalName = argv[0];
1478 argv[0] = execPath;
1480 hProcess = GetCurrentProcess();
1481 cmdLineObj = JimWinBuildCommandLine(interp, argv);
1484 * STARTF_USESTDHANDLES must be used to pass handles to child process.
1485 * Using SetStdHandle() and/or dup2() only works when a console mode
1486 * parent process is spawning an attached console mode child process.
1489 ZeroMemory(&startInfo, sizeof(startInfo));
1490 startInfo.cb = sizeof(startInfo);
1491 startInfo.dwFlags = STARTF_USESTDHANDLES;
1492 startInfo.hStdInput = INVALID_HANDLE_VALUE;
1493 startInfo.hStdOutput= INVALID_HANDLE_VALUE;
1494 startInfo.hStdError = INVALID_HANDLE_VALUE;
1497 * Duplicate all the handles which will be passed off as stdin, stdout
1498 * and stderr of the child process. The duplicate handles are set to
1499 * be inheritable, so the child process can use them.
1501 if (inputId == JIM_BAD_FD) {
1502 if (CreatePipe(&startInfo.hStdInput, &h, JimStdSecAttrs(), 0) != FALSE) {
1503 CloseHandle(h);
1505 } else {
1506 DuplicateHandle(hProcess, inputId, hProcess, &startInfo.hStdInput,
1507 0, TRUE, DUPLICATE_SAME_ACCESS);
1509 if (startInfo.hStdInput == JIM_BAD_FD) {
1510 goto end;
1513 if (outputId == JIM_BAD_FD) {
1514 startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0,
1515 JimStdSecAttrs(), OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1516 } else {
1517 DuplicateHandle(hProcess, outputId, hProcess, &startInfo.hStdOutput,
1518 0, TRUE, DUPLICATE_SAME_ACCESS);
1520 if (startInfo.hStdOutput == JIM_BAD_FD) {
1521 goto end;
1524 if (errorId == JIM_BAD_FD) {
1526 * If handle was not set, errors should be sent to an infinitely
1527 * deep sink.
1530 startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0,
1531 JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1532 } else {
1533 DuplicateHandle(hProcess, errorId, hProcess, &startInfo.hStdError,
1534 0, TRUE, DUPLICATE_SAME_ACCESS);
1536 if (startInfo.hStdError == JIM_BAD_FD) {
1537 goto end;
1540 if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
1541 0, env, NULL, &startInfo, &procInfo)) {
1542 goto end;
1546 * "When an application spawns a process repeatedly, a new thread
1547 * instance will be created for each process but the previous
1548 * instances may not be cleaned up. This results in a significant
1549 * virtual memory loss each time the process is spawned. If there
1550 * is a WaitForInputIdle() call between CreateProcess() and
1551 * CloseHandle(), the problem does not occur." PSS ID Number: Q124121
1554 WaitForInputIdle(procInfo.hProcess, 5000);
1555 CloseHandle(procInfo.hThread);
1557 pid = procInfo.hProcess;
1559 end:
1560 Jim_FreeNewObj(interp, cmdLineObj);
1561 if (startInfo.hStdInput != JIM_BAD_FD) {
1562 CloseHandle(startInfo.hStdInput);
1564 if (startInfo.hStdOutput != JIM_BAD_FD) {
1565 CloseHandle(startInfo.hStdOutput);
1567 if (startInfo.hStdError != JIM_BAD_FD) {
1568 CloseHandle(startInfo.hStdError);
1570 return pid;
1572 #else
1573 /* Unix-specific implementation */
1574 static int JimOpenForWrite(const char *filename, int append)
1576 return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
1579 static int JimRewindFd(int fd)
1581 return lseek(fd, 0L, SEEK_SET);
1584 static int JimCreateTemp(Jim_Interp *interp, const char *contents)
1586 char inName[] = "/tmp/tcl.tmp.XXXXXX";
1588 int fd = mkstemp(inName);
1589 if (fd == JIM_BAD_FD) {
1590 Jim_SetResultErrno(interp, "couldn't create temp file");
1591 return -1;
1593 unlink(inName);
1594 if (contents) {
1595 int length = strlen(contents);
1596 if (write(fd, contents, length) != length) {
1597 Jim_SetResultErrno(interp, "couldn't write temp file");
1598 close(fd);
1599 return -1;
1601 lseek(fd, 0L, SEEK_SET);
1603 return fd;
1606 static char **JimSaveEnv(char **env)
1608 char **saveenv = Jim_GetEnviron();
1609 Jim_SetEnviron(env);
1610 return saveenv;
1613 static void JimRestoreEnv(char **env)
1615 JimFreeEnv(Jim_GetEnviron(), env);
1616 Jim_SetEnviron(env);
1618 #endif
1619 #endif