aboutsummaryrefslogtreecommitdiff
path: root/jim-exec.c
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2011-09-11 14:12:01 +1000
committerSteve Bennett <steveb@workware.net.au>2011-09-12 16:58:56 +1000
commitbec5cf9f69e4a57e6993233a638dc8d652fee511 (patch)
treeb4ba64d8436dd091dfd95789ed19885722e118cf /jim-exec.c
parentbe9297ea02c2958d2e57a6fed698114ef37af91b (diff)
downloadjimtcl-bec5cf9f69e4a57e6993233a638dc8d652fee511.zip
jimtcl-bec5cf9f69e4a57e6993233a638dc8d652fee511.tar.gz
jimtcl-bec5cf9f69e4a57e6993233a638dc8d652fee511.tar.bz2
Implement full [exec] on mingw/msys
This also involves some restructuring of the existing implementation to allow for as much reuse as possible. Signed-off-by: Steve Bennett <steveb@workware.net.au>
Diffstat (limited to 'jim-exec.c')
-rw-r--r--jim-exec.c1003
1 files changed, 750 insertions, 253 deletions
diff --git a/jim-exec.c b/jim-exec.c
index 50917f2..bdbbb11 100644
--- a/jim-exec.c
+++ b/jim-exec.c
@@ -1,5 +1,4 @@
-
-/*
+/*
* (c) 2008 Steve Bennett <steveb@workware.net.au>
*
* Implements the exec command for Jim
@@ -22,30 +21,142 @@
*/
#include <string.h>
-#include <signal.h>
+#include <ctype.h>
#include "jim.h"
#include "jimautoconf.h"
-#if defined(HAVE_VFORK) && defined(HAVE_WAITPID)
+#if (!defined(HAVE_VFORK) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
+/* Poor man's implementation of exec with system()
+ * The system() call *may* do command line redirection, etc.
+ * The standard output is not available.
+ * Can't redirect filehandles.
+ */
+static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
+ int i, j;
+ int rc;
+
+ /* Create a quoted command line */
+ for (i = 1; i < argc; i++) {
+ int len;
+ const char *arg = Jim_GetString(argv[i], &len);
+
+ if (i > 1) {
+ Jim_AppendString(interp, cmdlineObj, " ", 1);
+ }
+ if (strpbrk(arg, "\\\" ") == NULL) {
+ /* No quoting required */
+ Jim_AppendString(interp, cmdlineObj, arg, len);
+ continue;
+ }
+
+ Jim_AppendString(interp, cmdlineObj, "\"", 1);
+ for (j = 0; j < len; j++) {
+ if (arg[j] == '\\' || arg[j] == '"') {
+ Jim_AppendString(interp, cmdlineObj, "\\", 1);
+ }
+ Jim_AppendString(interp, cmdlineObj, &arg[j], 1);
+ }
+ Jim_AppendString(interp, cmdlineObj, "\"", 1);
+ }
+ rc = system(Jim_String(cmdlineObj));
+
+ Jim_FreeNewObj(interp, cmdlineObj);
+
+ if (rc) {
+ Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
+ Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc));
+ Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
+ return JIM_ERR;
+ }
-#include "jim-signal.h"
+ return JIM_OK;
+}
+
+int Jim_execInit(Jim_Interp *interp)
+{
+ if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
+ return JIM_ERR;
+ Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
+ return JIM_OK;
+}
+#else
+/* Full exec implementation for unix and mingw */
-#include <unistd.h>
-#include <fcntl.h>
#include <errno.h>
-#include <sys/wait.h>
+#include <signal.h>
-#if defined(__GNUC__) && !defined(__clang__)
-#define IGNORE_RC(EXPR) ((EXPR) < 0 ? -1 : 0)
+#define XXX printf("@%s:%d\n", __FILE__, __LINE__); fflush(stdout);
+
+#if defined(__MINGW32__)
+ /* XXX: Should we use this implementation for cygwin too? */
+ #include <fcntl.h>
+
+ typedef HANDLE fdtype;
+ typedef HANDLE pidtype;
+ #define JIM_BAD_FD INVALID_HANDLE_VALUE
+ #define JIM_BAD_PID INVALID_HANDLE_VALUE
+ #define JimCloseFd CloseHandle
+
+ #define WIFEXITED(STATUS) 1
+ #define WEXITSTATUS(STATUS) (STATUS)
+ #define WIFSIGNALED(STATUS) 0
+ #define WTERMSIG(STATUS) 0
+ #define WNOHANG 1
+
+ static fdtype JimFileno(FILE *fh);
+ static pidtype JimWaitPid(pidtype pid, int *status, int nohang);
+ static fdtype JimDupFd(fdtype infd);
+ static fdtype JimOpenForRead(const char *filename);
+ static FILE *JimFdOpenForRead(fdtype fd);
+ static int JimPipe(fdtype pipefd[2]);
+ static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char *env,
+ fdtype inputId, fdtype outputId, fdtype errorId);
+ static int JimErrno(void);
#else
-#define IGNORE_RC(EXPR) EXPR
+ #include "jim-signal.h"
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <sys/wait.h>
+
+ typedef int fdtype;
+ typedef int pidtype;
+ #define JimPipe pipe
+ #define JimErrno() errno
+ #define JIM_BAD_FD -1
+ #define JIM_BAD_PID -1
+ #define JimFileno fileno
+ #define JimReadFd read
+ #define JimCloseFd close
+ #define JimWaitPid waitpid
+ #define JimDupFd dup
+ #define JimFdOpenForRead(FD) fdopen((FD), "r")
+ #define JimOpenForRead(NAME) open((NAME), O_RDONLY, 0)
#endif
-/* These two could be moved into the Tcl core */
+static const char *JimStrError(void);
+static char **JimSaveEnv(char **env);
+static void JimRestoreEnv(char **env);
+static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
+ pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr);
+static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr);
+static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId);
+static fdtype JimCreateTemp(Jim_Interp *interp, const char *contents);
+static fdtype JimOpenForWrite(const char *filename, int append);
+static int JimRewindFd(fdtype fd);
+
static void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
{
- Jim_SetResultFormatted(interp, "%s: %s", msg, strerror(errno));
+ Jim_SetResultFormatted(interp, "%s: %s", msg, JimStrError());
+}
+
+static const char *JimStrError(void)
+{
+ return strerror(JimErrno());
}
static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
@@ -60,26 +171,29 @@ static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
}
/**
- * Read from 'fd' and append the data to strObj
+ * Read from 'fd', append the data to strObj and close 'fd'.
* Returns JIM_OK if OK, or JIM_ERR on error.
*/
-static int JimAppendStreamToString(Jim_Interp *interp, int fd, Jim_Obj *strObj)
+static int JimAppendStreamToString(Jim_Interp *interp, fdtype fd, Jim_Obj *strObj)
{
- while (1) {
- char buffer[256];
- int count;
-
- count = read(fd, buffer, sizeof(buffer));
+ char buf[256];
+ FILE *fh = JimFdOpenForRead(fd);
+ if (fh == NULL) {
+ return JIM_ERR;
+ }
- if (count == 0) {
- Jim_RemoveTrailingNewline(strObj);
- return JIM_OK;
+ while (1) {
+ int retval = fread(buf, 1, sizeof(buf), fh);
+ if (retval > 0) {
+ Jim_AppendString(interp, strObj, buf, retval);
}
- if (count < 0) {
- return JIM_ERR;
+ if (retval != sizeof(buf)) {
+ break;
}
- Jim_AppendString(interp, strObj, buffer, count);
}
+ Jim_RemoveTrailingNewline(strObj);
+ fclose(fh);
+ return JIM_OK;
}
/*
@@ -110,11 +224,13 @@ static void JimTrimTrailingNewline(Jim_Interp *interp)
*/
static char **JimBuildEnv(Jim_Interp *interp)
{
-#ifdef jim_ext_tclcompat
+#if defined(jim_ext_tclcompat)
int i;
- int len;
+ int size;
+ int num;
int n;
- char **env;
+ char **envptr;
+ char *envdata;
Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);
@@ -122,32 +238,45 @@ static char **JimBuildEnv(Jim_Interp *interp)
return Jim_GetEnviron();
}
+ /* We build the array as a single block consisting of the pointers followed by
+ * the strings. This has the advantage of being easy to allocate/free and being
+ * compatible with both unix and windows
+ */
+
/* Calculate the required size */
- len = Jim_ListLength(interp, objPtr);
- if (len % 2) {
- len--;
+ num = Jim_ListLength(interp, objPtr);
+ if (num % 2) {
+ num--;
}
+ size = Jim_Length(objPtr);
+ /* We need one \0 and one equal sign for each element.
+ * A list has at least one space for each element except the first.
+ * We only need one extra char for the extra null terminator.
+ */
+ size++;
- env = Jim_Alloc(sizeof(*env) * (len / 2 + 1));
+ envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
+ envdata = (char *)&envptr[num / 2 + 1];
n = 0;
- for (i = 0; i < len; i += 2) {
- int l1, l2;
+ for (i = 0; i < num; i += 2) {
const char *s1, *s2;
Jim_Obj *elemObj;
Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE);
- s1 = Jim_GetString(elemObj, &l1);
+ s1 = Jim_String(elemObj);
Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE);
- s2 = Jim_GetString(elemObj, &l2);
+ s2 = Jim_String(elemObj);
- env[n] = Jim_Alloc(l1 + l2 + 2);
- sprintf(env[n], "%s=%s", s1, s2);
+ envptr[n] = envdata;
+ envdata += sprintf(envdata, "%s=%s", s1, s2);
+ envdata++;
n++;
}
- env[n] = NULL;
+ envptr[n] = NULL;
+ *envdata = 0;
- return env;
+ return envptr;
#else
return Jim_GetEnviron();
#endif
@@ -158,14 +287,10 @@ static char **JimBuildEnv(Jim_Interp *interp)
*
* Must pass original_environ.
*/
-static void JimFreeEnv(Jim_Interp *interp, char **env, char **original_environ)
+static void JimFreeEnv(char **env, char **original_environ)
{
#ifdef jim_ext_tclcompat
if (env != original_environ) {
- int i;
- for (i = 0; env[i]; i++) {
- Jim_Free(env[i]);
- }
Jim_Free(env);
}
#endif
@@ -177,7 +302,7 @@ static void JimFreeEnv(Jim_Interp *interp, char **env, char **original_environ)
* it gets removed below (in the same fashion that an
* extra newline in the command's output is removed).
*/
-static int JimCheckWaitStatus(Jim_Interp *interp, int pid, int waitStatus)
+static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus)
{
Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
int rc = JIM_ERR;
@@ -189,7 +314,7 @@ static int JimCheckWaitStatus(Jim_Interp *interp, int pid, int waitStatus)
}
else {
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
- Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
}
}
@@ -216,7 +341,7 @@ static int JimCheckWaitStatus(Jim_Interp *interp, int pid, int waitStatus)
#else
Jim_SetResultFormatted(interp, "child %s by signal %d", action, WTERMSIG(waitStatus));
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus)));
- Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus)));
#endif
}
@@ -231,7 +356,7 @@ static int JimCheckWaitStatus(Jim_Interp *interp, int pid, int waitStatus)
struct WaitInfo
{
- int pid; /* Process id of child. */
+ pidtype pid; /* Process id of child. */
int status; /* Status returned when child exited or suspended. */
int flags; /* Various flag bits; see below for definitions. */
};
@@ -271,18 +396,16 @@ static struct WaitInfoTable *JimAllocWaitInfoTable(void)
return table;
}
-static int Jim_CreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
- int **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr);
-static void JimDetachPids(Jim_Interp *interp, int numPids, const int *pidPtr);
-static int Jim_CleanupChildren(Jim_Interp *interp, int numPids, int *pidPtr, int errorId);
-
+/*
+ * The main [exec] command
+ */
static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
- int outputId; /* File id for output pipe. -1
+ fdtype outputId; /* File id for output pipe. -1
* means command overrode. */
- int errorId; /* File id for temporary file
+ fdtype errorId; /* File id for temporary file
* containing error output. */
- int *pidPtr;
+ pidtype *pidPtr;
int numPids, result;
/*
@@ -294,14 +417,14 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
int i;
argc--;
- numPids = Jim_CreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
+ numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
if (numPids < 0) {
return JIM_ERR;
}
/* The return value is a list of the pids */
listObj = Jim_NewListObj(interp, NULL, 0);
for (i = 0; i < numPids; i++) {
- Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, pidPtr[i]));
+ Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i]));
}
Jim_SetResult(interp, listObj);
JimDetachPids(interp, numPids, pidPtr);
@@ -313,7 +436,8 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
* Create the command's pipeline.
*/
numPids =
- Jim_CreatePipeline(interp, argc - 1, argv + 1, &pidPtr, (int *)NULL, &outputId, &errorId);
+ JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
+
if (numPids < 0) {
return JIM_ERR;
}
@@ -324,21 +448,20 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
Jim_SetResultString(interp, "", 0);
result = JIM_OK;
- if (outputId != -1) {
+ if (outputId != JIM_BAD_FD) {
result = JimAppendStreamToString(interp, outputId, Jim_GetResult(interp));
if (result < 0) {
Jim_SetResultErrno(interp, "error reading from output pipe");
}
- close(outputId);
}
- if (Jim_CleanupChildren(interp, numPids, pidPtr, errorId) != JIM_OK) {
+ if (JimCleanupChildren(interp, numPids, pidPtr, errorId) != JIM_OK) {
result = JIM_ERR;
}
return result;
}
-void Jim_ReapDetachedPids(struct WaitInfoTable *table)
+static void JimReapDetachedPids(struct WaitInfoTable *table)
{
struct WaitInfo *waitPtr;
int count;
@@ -350,8 +473,8 @@ void Jim_ReapDetachedPids(struct WaitInfoTable *table)
for (waitPtr = table->info, count = table->used; count > 0; waitPtr++, count--) {
if (waitPtr->flags & WI_DETACHED) {
int status;
- int pid = waitpid(waitPtr->pid, &status, WNOHANG);
- if (pid > 0) {
+ pidtype pid = JimWaitPid(waitPtr->pid, &status, WNOHANG);
+ if (pid != JIM_BAD_PID) {
if (waitPtr != &table->info[table->used - 1]) {
*waitPtr = table->info[table->used - 1];
}
@@ -364,11 +487,11 @@ void Jim_ReapDetachedPids(struct WaitInfoTable *table)
/**
* Does waitpid() on the given pid, and then removes the
* entry from the wait table.
- *
+ *
* Returns the pid if OK and updates *statusPtr with the status,
- * or -1 if the pid was not in the table.
+ * or JIM_BAD_PID if the pid was not in the table.
*/
-static int JimWaitPid(struct WaitInfoTable *table, int pid, int *statusPtr)
+static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr)
{
int i;
@@ -376,7 +499,7 @@ static int JimWaitPid(struct WaitInfoTable *table, int pid, int *statusPtr)
for (i = 0; i < table->used; i++) {
if (pid == table->info[i].pid) {
/* wait for it */
- waitpid(pid, statusPtr, 0);
+ JimWaitPid(pid, statusPtr, 0);
/* Remove it from the table */
if (i != table->used - 1) {
@@ -388,7 +511,7 @@ static int JimWaitPid(struct WaitInfoTable *table, int pid, int *statusPtr)
}
/* Not found */
- return -1;
+ return JIM_BAD_PID;
}
/*
@@ -409,7 +532,7 @@ static int JimWaitPid(struct WaitInfoTable *table, int pid, int *statusPtr)
*----------------------------------------------------------------------
*/
-static void JimDetachPids(Jim_Interp *interp, int numPids, const int *pidPtr)
+static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr)
{
int j;
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
@@ -429,7 +552,7 @@ static void JimDetachPids(Jim_Interp *interp, int numPids, const int *pidPtr)
/*
*----------------------------------------------------------------------
*
- * Jim_CreatePipeline --
+ * JimCreatePipeline --
*
* Given an argc/argv array, instantiate a pipeline of processes
* as described by the argv.
@@ -455,10 +578,10 @@ static void JimDetachPids(Jim_Interp *interp, int numPids, const int *pidPtr)
*----------------------------------------------------------------------
*/
static int
-Jim_CreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int **pidArrayPtr,
- int *inPipePtr, int *outPipePtr, int *errFilePtr)
+JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr,
+ fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr)
{
- int *pidPtr = NULL; /* Points to malloc-ed array holding all
+ pidtype *pidPtr = NULL; /* Points to malloc-ed array holding all
* the pids of child processes. */
int numPids = 0; /* Actual number of processes that exist
* at *pidPtr right now. */
@@ -492,43 +615,47 @@ Jim_CreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int **pid
* or NULL if output goes to stdout/pipe. */
const char *error = NULL; /* Holds name of stderr file to pipe to,
* or NULL if stderr goes to stderr/pipe. */
- int inputId = -1; /* Readable file id input to current command in
- * pipeline (could be file or pipe). -1
+ fdtype inputId = JIM_BAD_FD;
+ /* Readable file id input to current command in
+ * pipeline (could be file or pipe). JIM_BAD_FD
* means use stdin. */
- int outputId = -1; /* Writable file id for output from current
+ fdtype outputId = JIM_BAD_FD;
+ /* Writable file id for output from current
* command in pipeline (could be file or pipe).
- * -1 means use stdout. */
- int errorId = -1; /* Writable file id for all standard error
- * output from all commands in pipeline. -1
+ * JIM_BAD_FD means use stdout. */
+ fdtype errorId = JIM_BAD_FD;
+ /* Writable file id for all standard error
+ * output from all commands in pipeline. JIM_BAD_FD
* means use stderr. */
- int lastOutputId = -1; /* Write file id for output from last command
+ fdtype lastOutputId = JIM_BAD_FD;
+ /* Write file id for output from last command
* in pipeline (could be file or pipe).
* -1 means use stdout. */
- int pipeIds[2]; /* File ids for pipe that's being created. */
+ fdtype pipeIds[2]; /* File ids for pipe that's being created. */
int firstArg, lastArg; /* Indexes of first and last arguments in
* current command. */
int lastBar;
- char *execName;
- int i, pid;
- char **orig_environ;
+ int i;
+ pidtype pid;
+ char **save_environ;
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
/* Holds the args which will be used to exec */
char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
int arg_count = 0;
- Jim_ReapDetachedPids(table);
+ JimReapDetachedPids(table);
if (inPipePtr != NULL) {
- *inPipePtr = -1;
+ *inPipePtr = JIM_BAD_FD;
}
if (outPipePtr != NULL) {
- *outPipePtr = -1;
+ *outPipePtr = JIM_BAD_FD;
}
if (errFilePtr != NULL) {
- *errFilePtr = -1;
+ *errFilePtr = JIM_BAD_FD;
}
- pipeIds[0] = pipeIds[1] = -1;
+ pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
/*
* First, scan through all the arguments to figure out the structure
@@ -629,8 +756,7 @@ badargs:
}
/* Must do this before vfork(), so do it now */
- orig_environ = Jim_GetEnviron();
- Jim_SetEnviron(JimBuildEnv(interp));
+ save_environ = JimSaveEnv(JimBuildEnv(interp));
/*
* Set up the redirected input source for the pipeline, if
@@ -642,24 +768,8 @@ badargs:
* Immediate data in command. Create temporary file and
* put data into file.
*/
-
-#define TMP_STDIN_NAME "/tmp/tcl.in.XXXXXX"
- char inName[sizeof(TMP_STDIN_NAME) + 1];
- int length;
-
- strcpy(inName, TMP_STDIN_NAME);
- inputId = mkstemp(inName);
- if (inputId < 0) {
- Jim_SetResultErrno(interp, "couldn't create input file for command");
- goto error;
- }
- length = strlen(input);
- if (write(inputId, input, length) != length) {
- Jim_SetResultErrno(interp, "couldn't write file input for command");
- goto error;
- }
- if (lseek(inputId, 0L, SEEK_SET) == -1 || unlink(inName) == -1) {
- Jim_SetResultErrno(interp, "couldn't reset or remove input file for command");
+ inputId = JimCreateTemp(interp, input);
+ if (inputId == JIM_BAD_FD) {
goto error;
}
}
@@ -672,28 +782,27 @@ badargs:
if (fh == NULL) {
goto error;
}
- inputId = dup(fileno(fh));
+ inputId = JimDupFd(JimFileno(fh));
}
else {
/*
* File redirection. Just open the file.
*/
- inputId = open(input, O_RDONLY, 0);
- if (inputId < 0) {
- Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input,
- strerror(errno));
+ inputId = JimOpenForRead(input);
+ if (inputId == JIM_BAD_FD) {
+ Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, JimStrError());
goto error;
}
}
}
else if (inPipePtr != NULL) {
- if (pipe(pipeIds) != 0) {
+ if (JimPipe(pipeIds) != 0) {
Jim_SetResultErrno(interp, "couldn't create input pipe for command");
goto error;
}
inputId = pipeIds[0];
*inPipePtr = pipeIds[1];
- pipeIds[0] = pipeIds[1] = -1;
+ pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
}
/*
@@ -710,22 +819,15 @@ badargs:
goto error;
}
fflush(fh);
- lastOutputId = dup(fileno(fh));
+ lastOutputId = JimDupFd(JimFileno(fh));
}
else {
/*
* Output is to go to a file.
*/
- int mode = O_WRONLY | O_CREAT | O_TRUNC;
-
- if (outputFile == FILE_APPEND) {
- mode = O_WRONLY | O_CREAT | O_APPEND;
- }
-
- lastOutputId = open(output, mode, 0666);
- if (lastOutputId < 0) {
- Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output,
- strerror(errno));
+ lastOutputId = JimOpenForWrite(output, outputFile == FILE_APPEND);
+ if (lastOutputId == JIM_BAD_FD) {
+ Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, JimStrError());
goto error;
}
}
@@ -734,29 +836,28 @@ badargs:
/*
* Output is to go to a pipe.
*/
- if (pipe(pipeIds) != 0) {
+ if (JimPipe(pipeIds) != 0) {
Jim_SetResultErrno(interp, "couldn't create output pipe");
goto error;
}
lastOutputId = pipeIds[1];
*outPipePtr = pipeIds[0];
- pipeIds[0] = pipeIds[1] = -1;
+ pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
}
-
/* If we are redirecting stderr with 2>filename or 2>@fileId, then we ignore errFilePtr */
if (error != NULL) {
if (errorFile == FILE_HANDLE) {
if (strcmp(error, "1") == 0) {
/* Special 2>@1 */
- if (lastOutputId >= 0) {
- errorId = dup(lastOutputId);
+ if (lastOutputId != JIM_BAD_FD) {
+ errorId = JimDupFd(lastOutputId);
}
else {
/* No redirection of stdout, so just use 2>@stdout */
error = "stdout";
}
}
- if (errorId < 0) {
+ if (errorId == JIM_BAD_FD) {
Jim_Obj *fhObj = Jim_NewStringObj(interp, error, -1);
FILE *fh = Jim_AioFilehandle(interp, fhObj);
@@ -765,23 +866,17 @@ badargs:
goto error;
}
fflush(fh);
- errorId = dup(fileno(fh));
+ errorId = JimDupFd(JimFileno(fh));
}
}
else {
/*
* Output is to go to a file.
*/
- int mode = O_WRONLY | O_CREAT | O_TRUNC;
-
- if (errorFile == FILE_APPEND) {
- mode = O_WRONLY | O_CREAT | O_APPEND;
- }
-
- errorId = open(error, mode, 0666);
- if (errorId < 0) {
- Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error,
- strerror(errno));
+ errorId = JimOpenForWrite(error, errorFile == FILE_APPEND);
+ if (errorId == JIM_BAD_FD) {
+ Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError());
+ goto error;
}
}
}
@@ -794,25 +889,11 @@ badargs:
* to complete before reading stderr, and processes couldn't complete
* because stderr was backed up.
*/
-
-#define TMP_STDERR_NAME "/tmp/tcl.err.XXXXXX"
- char errName[sizeof(TMP_STDERR_NAME) + 1];
-
- strcpy(errName, TMP_STDERR_NAME);
- errorId = mkstemp(errName);
- if (errorId < 0) {
- errFileError:
- Jim_SetResultErrno(interp, "couldn't create error file for command");
- goto error;
- }
- *errFilePtr = open(errName, O_RDONLY, 0);
- if (*errFilePtr < 0) {
- goto errFileError;
- }
- if (unlink(errName) == -1) {
- Jim_SetResultErrno(interp, "couldn't remove error file for command");
+ errorId = JimCreateTemp(interp, NULL);
+ if (errorId == JIM_BAD_FD) {
goto error;
}
+ *errFilePtr = JimDupFd(errorId);
}
/*
@@ -820,15 +901,13 @@ badargs:
* group of arguments between "|" arguments.
*/
- pidPtr = (int *)Jim_Alloc(cmdCount * sizeof(*pidPtr));
+ pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
for (i = 0; i < numPids; i++) {
- pidPtr[i] = -1;
+ pidPtr[i] = JIM_BAD_PID;
}
for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
int pipe_dup_err = 0;
- int origErrorId = errorId;
- char execerr[64];
- int execerrlen;
+ fdtype origErrorId = errorId;
for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
if (arg_array[lastArg][0] == '|') {
@@ -844,16 +923,22 @@ badargs:
outputId = lastOutputId;
}
else {
- if (pipe(pipeIds) != 0) {
+ if (JimPipe(pipeIds) != 0) {
Jim_SetResultErrno(interp, "couldn't create pipe");
goto error;
}
outputId = pipeIds[1];
}
- execName = arg_array[firstArg];
/* Now fork the child */
+#ifdef __MINGW32__
+ pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ ? save_environ[0] : NULL, inputId, outputId, errorId);
+ if (pid == JIM_BAD_PID) {
+ Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
+ goto error;
+ }
+#else
/*
* Disable SIGPIPE signals: if they were allowed, this process
* might go away unexpectedly if children misbehave. This code
@@ -870,10 +955,6 @@ badargs:
errorId = outputId;
}
- /* Need to prep an error message before vfork(), just in case */
- snprintf(execerr, sizeof(execerr), "couldn't exec \"%s\"", execName);
- execerrlen = strlen(execerr);
-
/*
* Make a new process and enter it into the table if the fork
* is successful.
@@ -894,12 +975,13 @@ badargs:
close(i);
}
- execvp(execName, &arg_array[firstArg]);
+ execvp(arg_array[firstArg], &arg_array[firstArg]);
- /* we really can ignore the error here! */
- IGNORE_RC(write(2, execerr, execerrlen));
+ /* Need to prep an error message before vfork(), just in case */
+ fprintf(stderr, "couldn't exec \"%s\"", arg_array[firstArg]);
_exit(127);
}
+#endif
/* parent */
@@ -926,14 +1008,14 @@ badargs:
* this child, then set up the input for the next child.
*/
- if (inputId != -1) {
- close(inputId);
+ if (inputId != JIM_BAD_FD) {
+ JimCloseFd(inputId);
}
- if (outputId != -1) {
- close(outputId);
+ if (outputId != JIM_BAD_FD) {
+ JimCloseFd(outputId);
}
inputId = pipeIds[0];
- pipeIds[0] = pipeIds[1] = -1;
+ pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
}
*pidArrayPtr = pidPtr;
@@ -942,19 +1024,18 @@ badargs:
*/
cleanup:
- if (inputId != -1) {
- close(inputId);
+ if (inputId != JIM_BAD_FD) {
+ JimCloseFd(inputId);
}
- if (lastOutputId != -1) {
- close(lastOutputId);
+ if (lastOutputId != JIM_BAD_FD) {
+ JimCloseFd(lastOutputId);
}
- if (errorId != -1) {
- close(errorId);
+ if (errorId != JIM_BAD_FD) {
+ JimCloseFd(errorId);
}
Jim_Free(arg_array);
- JimFreeEnv(interp, Jim_GetEnviron(), orig_environ);
- Jim_SetEnviron(orig_environ);
+ JimRestoreEnv(save_environ);
return numPids;
@@ -965,27 +1046,27 @@ badargs:
*/
error:
- if ((inPipePtr != NULL) && (*inPipePtr != -1)) {
- close(*inPipePtr);
- *inPipePtr = -1;
+ if ((inPipePtr != NULL) && (*inPipePtr != JIM_BAD_FD)) {
+ JimCloseFd(*inPipePtr);
+ *inPipePtr = JIM_BAD_FD;
}
- if ((outPipePtr != NULL) && (*outPipePtr != -1)) {
- close(*outPipePtr);
- *outPipePtr = -1;
+ if ((outPipePtr != NULL) && (*outPipePtr != JIM_BAD_FD)) {
+ JimCloseFd(*outPipePtr);
+ *outPipePtr = JIM_BAD_FD;
}
- if ((errFilePtr != NULL) && (*errFilePtr != -1)) {
- close(*errFilePtr);
- *errFilePtr = -1;
+ if ((errFilePtr != NULL) && (*errFilePtr != JIM_BAD_FD)) {
+ JimCloseFd(*errFilePtr);
+ *errFilePtr = JIM_BAD_FD;
}
- if (pipeIds[0] != -1) {
- close(pipeIds[0]);
+ if (pipeIds[0] != JIM_BAD_FD) {
+ JimCloseFd(pipeIds[0]);
}
- if (pipeIds[1] != -1) {
- close(pipeIds[1]);
+ if (pipeIds[1] != JIM_BAD_FD) {
+ JimCloseFd(pipeIds[1]);
}
if (pidPtr != NULL) {
for (i = 0; i < numPids; i++) {
- if (pidPtr[i] != -1) {
+ if (pidPtr[i] != JIM_BAD_PID) {
JimDetachPids(interp, 1, &pidPtr[i]);
}
}
@@ -998,7 +1079,7 @@ badargs:
/*
*----------------------------------------------------------------------
*
- * CleanupChildren --
+ * JimCleanupChildren --
*
* This is a utility procedure used to wait for child processes
* to exit, record information about abnormal exits, and then
@@ -1017,7 +1098,7 @@ badargs:
*----------------------------------------------------------------------
*/
-static int Jim_CleanupChildren(Jim_Interp *interp, int numPids, int *pidPtr, int errorId)
+static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId)
{
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
int result = JIM_OK;
@@ -1025,7 +1106,7 @@ static int Jim_CleanupChildren(Jim_Interp *interp, int numPids, int *pidPtr, int
for (i = 0; i < numPids; i++) {
int waitStatus = 0;
- if (JimWaitPid(table, pidPtr[i], &waitStatus) > 0) {
+ if (JimWaitForProcess(table, pidPtr[i], &waitStatus) != JIM_BAD_PID) {
if (JimCheckWaitStatus(interp, pidPtr[i], waitStatus) != JIM_OK) {
result = JIM_ERR;
}
@@ -1038,12 +1119,11 @@ static int Jim_CleanupChildren(Jim_Interp *interp, int numPids, int *pidPtr, int
* then add the file's contents to the result
* string.
*/
- if (errorId >= 0) {
+ if (errorId != JIM_BAD_FD) {
+ JimRewindFd(errorId);
if (JimAppendStreamToString(interp, errorId, Jim_GetResult(interp)) != JIM_OK) {
- Jim_SetResultErrno(interp, "error reading from stderr output file");
result = JIM_ERR;
}
- close(errorId);
}
JimTrimTrailingNewline(interp);
@@ -1055,67 +1135,484 @@ int Jim_execInit(Jim_Interp *interp)
{
if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
return JIM_ERR;
-
Jim_CreateCommand(interp, "exec", Jim_ExecCmd, JimAllocWaitInfoTable(), JimFreeWaitInfoTable);
return JIM_OK;
}
-#else
-/* e.g. Windows. Poor mans implementation of exec with system()
- * The system() call *may* do command line redirection, etc.
- * The standard output is not available.
- * Can't redirect filehandles.
- */
-static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+
+#if defined(__MINGW32__)
+/* Windows-specific (mingw) implementation */
+
+static SECURITY_ATTRIBUTES *JimStdSecAttrs(void)
{
- Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
- int i, j;
- int rc;
+ static SECURITY_ATTRIBUTES secAtts;
- /* Create a quoted command line */
- for (i = 1; i < argc; i++) {
- int len;
- const char *arg = Jim_GetString(argv[i], &len);
+ secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
+ secAtts.lpSecurityDescriptor = NULL;
+ secAtts.bInheritHandle = TRUE;
+ return &secAtts;
+}
- if (i > 1) {
- Jim_AppendString(interp, cmdlineObj, " ", 1);
+static int JimErrno(void)
+{
+ switch (GetLastError()) {
+ case ERROR_FILE_NOT_FOUND: return ENOENT;
+ case ERROR_PATH_NOT_FOUND: return ENOENT;
+ case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
+ case ERROR_ACCESS_DENIED: return EACCES;
+ case ERROR_INVALID_HANDLE: return EBADF;
+ case ERROR_BAD_ENVIRONMENT: return E2BIG;
+ case ERROR_BAD_FORMAT: return ENOEXEC;
+ case ERROR_INVALID_ACCESS: return EACCES;
+ case ERROR_INVALID_DRIVE: return ENOENT;
+ case ERROR_CURRENT_DIRECTORY: return EACCES;
+ case ERROR_NOT_SAME_DEVICE: return EXDEV;
+ case ERROR_NO_MORE_FILES: return ENOENT;
+ case ERROR_WRITE_PROTECT: return EROFS;
+ case ERROR_BAD_UNIT: return ENXIO;
+ case ERROR_NOT_READY: return EBUSY;
+ case ERROR_BAD_COMMAND: return EIO;
+ case ERROR_CRC: return EIO;
+ case ERROR_BAD_LENGTH: return EIO;
+ case ERROR_SEEK: return EIO;
+ case ERROR_WRITE_FAULT: return EIO;
+ case ERROR_READ_FAULT: return EIO;
+ case ERROR_GEN_FAILURE: return EIO;
+ case ERROR_SHARING_VIOLATION: return EACCES;
+ case ERROR_LOCK_VIOLATION: return EACCES;
+ case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
+ case ERROR_HANDLE_DISK_FULL: return ENOSPC;
+ case ERROR_NOT_SUPPORTED: return ENODEV;
+ case ERROR_REM_NOT_LIST: return EBUSY;
+ case ERROR_DUP_NAME: return EEXIST;
+ case ERROR_BAD_NETPATH: return ENOENT;
+ case ERROR_NETWORK_BUSY: return EBUSY;
+ case ERROR_DEV_NOT_EXIST: return ENODEV;
+ case ERROR_TOO_MANY_CMDS: return EAGAIN;
+ case ERROR_ADAP_HDW_ERR: return EIO;
+ case ERROR_BAD_NET_RESP: return EIO;
+ case ERROR_UNEXP_NET_ERR: return EIO;
+ case ERROR_NETNAME_DELETED: return ENOENT;
+ case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
+ case ERROR_BAD_DEV_TYPE: return ENODEV;
+ case ERROR_BAD_NET_NAME: return ENOENT;
+ case ERROR_TOO_MANY_NAMES: return ENFILE;
+ case ERROR_TOO_MANY_SESS: return EIO;
+ case ERROR_SHARING_PAUSED: return EAGAIN;
+ case ERROR_REDIR_PAUSED: return EAGAIN;
+ case ERROR_FILE_EXISTS: return EEXIST;
+ case ERROR_CANNOT_MAKE: return ENOSPC;
+ case ERROR_OUT_OF_STRUCTURES: return ENFILE;
+ case ERROR_ALREADY_ASSIGNED: return EEXIST;
+ case ERROR_INVALID_PASSWORD: return EPERM;
+ case ERROR_NET_WRITE_FAULT: return EIO;
+ case ERROR_NO_PROC_SLOTS: return EAGAIN;
+ case ERROR_DISK_CHANGE: return EXDEV;
+ case ERROR_BROKEN_PIPE: return EPIPE;
+ case ERROR_OPEN_FAILED: return ENOENT;
+ case ERROR_DISK_FULL: return ENOSPC;
+ case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
+ case ERROR_INVALID_TARGET_HANDLE: return EBADF;
+ case ERROR_INVALID_NAME: return ENOENT;
+ case ERROR_PROC_NOT_FOUND: return ESRCH;
+ case ERROR_WAIT_NO_CHILDREN: return ECHILD;
+ case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
+ case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
+ case ERROR_SEEK_ON_DEVICE: return ESPIPE;
+ case ERROR_BUSY_DRIVE: return EAGAIN;
+ case ERROR_DIR_NOT_EMPTY: return EEXIST;
+ case ERROR_NOT_LOCKED: return EACCES;
+ case ERROR_BAD_PATHNAME: return ENOENT;
+ case ERROR_LOCK_FAILED: return EACCES;
+ case ERROR_ALREADY_EXISTS: return EEXIST;
+ case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
+ case ERROR_BAD_PIPE: return EPIPE;
+ case ERROR_PIPE_BUSY: return EAGAIN;
+ case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
+ case ERROR_DIRECTORY: return ENOTDIR;
+ }
+ return EINVAL;
+}
+
+static int JimPipe(fdtype pipefd[2])
+{
+ if (CreatePipe(&pipefd[0], &pipefd[1], NULL, 0)) {
+ return 0;
+ }
+ return -1;
+}
+
+static fdtype JimDupFd(fdtype infd)
+{
+ fdtype dupfd;
+ pidtype pid = GetCurrentProcess();
+
+ if (DuplicateHandle(pid, infd, pid, &dupfd, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
+ return dupfd;
+ }
+ return JIM_BAD_FD;
+}
+
+static int JimRewindFd(fdtype fd)
+{
+ return SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ? -1 : 0;
+}
+
+#if 0
+static int JimReadFd(fdtype fd, char *buffer, size_t len)
+{
+ DWORD num;
+
+ if (ReadFile(fd, buffer, len, &num, NULL)) {
+ return num;
+ }
+ if (GetLastError() == ERROR_HANDLE_EOF || GetLastError() == ERROR_BROKEN_PIPE) {
+ return 0;
+ }
+ return -1;
+}
+#endif
+
+static FILE *JimFdOpenForRead(fdtype fd)
+{
+ return _fdopen(_open_osfhandle((int)fd, _O_RDONLY | _O_TEXT), "r");
+}
+
+static fdtype JimFileno(FILE *fh)
+{
+ return (fdtype)_get_osfhandle(_fileno(fh));
+}
+
+static fdtype JimOpenForRead(const char *filename)
+{
+ return CreateFile(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ JimStdSecAttrs(), OPEN_EXISTING, 0, NULL);
+}
+
+static fdtype JimOpenForWrite(const char *filename, int append)
+{
+ return CreateFile(filename, append ? FILE_APPEND_DATA : GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ JimStdSecAttrs(), append ? OPEN_ALWAYS : CREATE_ALWAYS, 0, (HANDLE) NULL);
+}
+
+static FILE *JimFdOpenForWrite(fdtype fd)
+{
+ return _fdopen(_open_osfhandle((int)fd, _O_TEXT), "w");
+}
+
+static pidtype JimWaitPid(pidtype pid, int *status, int nohang)
+{
+ DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE);
+ if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
+ /* WAIT_TIMEOUT can only happend with WNOHANG */
+ return JIM_BAD_PID;
+ }
+ GetExitCodeProcess(pid, &ret);
+ *status = ret;
+ CloseHandle(pid);
+ return pid;
+}
+
+static HANDLE JimCreateTemp(Jim_Interp *interp, const char *contents)
+{
+ char name[MAX_PATH];
+ HANDLE handle;
+
+ if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, "JIM", 0, name)) {
+ return JIM_BAD_FD;
+ }
+
+ handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, JimStdSecAttrs(),
+ CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
+ NULL);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ goto error;
+ }
+
+ if (contents != NULL) {
+ /* Use fdopen() to get automatic text-mode translation */
+ FILE *fh = JimFdOpenForWrite(JimDupFd(handle));
+ if (fh == NULL) {
+ goto error;
}
- if (strpbrk(arg, "\\\" ") == NULL) {
- /* No quoting required */
- Jim_AppendString(interp, cmdlineObj, arg, len);
+
+ if (fwrite(contents, strlen(contents), 1, fh) != 1) {
+ fclose(fh);
+ goto error;
+ }
+ fseek(fh, 0, SEEK_SET);
+ fclose(fh);
+ }
+ return handle;
+
+ error:
+ Jim_SetResultErrno(interp, "failed to create temp file");
+ CloseHandle(handle);
+ DeleteFile(name);
+ return JIM_BAD_FD;
+}
+
+static int
+JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
+{
+ int i;
+ static char extensions[][5] = {".exe", "", ".bat"};
+
+ for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
+ lstrcpyn(fullPath, originalName, MAX_PATH - 5);
+ lstrcat(fullPath, extensions[i]);
+
+ if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) {
continue;
}
+ if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
+ continue;
+ }
+ return 0;
+ }
- Jim_AppendString(interp, cmdlineObj, "\"", 1);
- for (j = 0; j < len; j++) {
- if (arg[j] == '\\' || arg[j] == '"') {
- Jim_AppendString(interp, cmdlineObj, "\\", 1);
+ return -1;
+}
+
+static char **JimSaveEnv(char **env)
+{
+ return env;
+}
+
+static void JimRestoreEnv(char **env)
+{
+ JimFreeEnv(env, NULL);
+}
+
+static Jim_Obj *
+JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
+{
+ char *start, *special;
+ int quote, i;
+
+ Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0);
+
+ for (i = 0; argv[i]; i++) {
+ if (i > 0) {
+ Jim_AppendString(interp, strObj, " ", 1);
+ }
+
+ if (argv[i][0] == '\0') {
+ quote = 1;
+ }
+ else {
+ quote = 0;
+ for (start = argv[i]; *start != '\0'; start++) {
+ if (isspace(UCHAR(*start))) {
+ quote = 1;
+ break;
+ }
}
- Jim_AppendString(interp, cmdlineObj, &arg[j], 1);
}
- Jim_AppendString(interp, cmdlineObj, "\"", 1);
+ if (quote) {
+ Jim_AppendString(interp, strObj, "\"" , 1);
+ }
+
+ start = argv[i];
+ for (special = argv[i]; ; ) {
+ if ((*special == '\\') && (special[1] == '\\' ||
+ special[1] == '"' || (quote && special[1] == '\0'))) {
+ Jim_AppendString(interp, strObj, start, special - start);
+ start = special;
+ while (1) {
+ special++;
+ if (*special == '"' || (quote && *special == '\0')) {
+ /*
+ * N backslashes followed a quote -> insert
+ * N * 2 + 1 backslashes then a quote.
+ */
+
+ Jim_AppendString(interp, strObj, start, special - start);
+ break;
+ }
+ if (*special != '\\') {
+ break;
+ }
+ }
+ Jim_AppendString(interp, strObj, start, special - start);
+ start = special;
+ }
+ if (*special == '"') {
+ if (special == start) {
+ Jim_AppendString(interp, strObj, "\"", 1);
+ }
+ else {
+ Jim_AppendString(interp, strObj, start, special - start);
+ }
+ Jim_AppendString(interp, strObj, "\\\"", 2);
+ start = special + 1;
+ }
+ if (*special == '\0') {
+ break;
+ }
+ special++;
+ }
+ Jim_AppendString(interp, strObj, start, special - start);
+ if (quote) {
+ Jim_AppendString(interp, strObj, "\"", 1);
+ }
}
- rc = system(Jim_String(cmdlineObj));
+ return strObj;
+}
- Jim_FreeNewObj(interp, cmdlineObj);
+static pidtype
+JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, fdtype inputId, fdtype outputId, fdtype errorId)
+{
+ STARTUPINFO startInfo;
+ PROCESS_INFORMATION procInfo;
+ HANDLE hProcess, h;
+ char execPath[MAX_PATH];
+ char *originalName;
+ pidtype pid = JIM_BAD_PID;
+ Jim_Obj *cmdLineObj;
+
+ if (JimWinFindExecutable(argv[0], execPath) < 0) {
+ return JIM_BAD_PID;
+ }
+ originalName = argv[0];
+ argv[0] = execPath;
- if (rc) {
- Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
- Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
- Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0));
- Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc));
- Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
- return JIM_ERR;
+ hProcess = GetCurrentProcess();
+ cmdLineObj = JimWinBuildCommandLine(interp, argv);
+
+ /*
+ * STARTF_USESTDHANDLES must be used to pass handles to child process.
+ * Using SetStdHandle() and/or dup2() only works when a console mode
+ * parent process is spawning an attached console mode child process.
+ */
+
+ ZeroMemory(&startInfo, sizeof(startInfo));
+ startInfo.cb = sizeof(startInfo);
+ startInfo.dwFlags = STARTF_USESTDHANDLES;
+ startInfo.hStdInput = INVALID_HANDLE_VALUE;
+ startInfo.hStdOutput= INVALID_HANDLE_VALUE;
+ startInfo.hStdError = INVALID_HANDLE_VALUE;
+
+ /*
+ * Duplicate all the handles which will be passed off as stdin, stdout
+ * and stderr of the child process. The duplicate handles are set to
+ * be inheritable, so the child process can use them.
+ */
+ if (inputId == JIM_BAD_FD) {
+ if (CreatePipe(&startInfo.hStdInput, &h, JimStdSecAttrs(), 0) != FALSE) {
+ CloseHandle(h);
+ }
+ } else {
+ DuplicateHandle(hProcess, inputId, hProcess, &startInfo.hStdInput,
+ 0, TRUE, DUPLICATE_SAME_ACCESS);
+ }
+ if (startInfo.hStdInput == JIM_BAD_FD) {
+ goto end;
}
- return JIM_OK;
+ if (outputId == JIM_BAD_FD) {
+ startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0,
+ JimStdSecAttrs(), OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ } else {
+ DuplicateHandle(hProcess, outputId, hProcess, &startInfo.hStdOutput,
+ 0, TRUE, DUPLICATE_SAME_ACCESS);
+ }
+ if (startInfo.hStdOutput == JIM_BAD_FD) {
+ goto end;
+ }
+
+ if (errorId == JIM_BAD_FD) {
+ /*
+ * If handle was not set, errors should be sent to an infinitely
+ * deep sink.
+ */
+
+ startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0,
+ JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ } else {
+ DuplicateHandle(hProcess, errorId, hProcess, &startInfo.hStdError,
+ 0, TRUE, DUPLICATE_SAME_ACCESS);
+ }
+ if (startInfo.hStdError == JIM_BAD_FD) {
+ goto end;
+ }
+
+ if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
+ 0, env, NULL, &startInfo, &procInfo)) {
+ goto end;
+ }
+
+ /*
+ * "When an application spawns a process repeatedly, a new thread
+ * instance will be created for each process but the previous
+ * instances may not be cleaned up. This results in a significant
+ * virtual memory loss each time the process is spawned. If there
+ * is a WaitForInputIdle() call between CreateProcess() and
+ * CloseHandle(), the problem does not occur." PSS ID Number: Q124121
+ */
+
+ WaitForInputIdle(procInfo.hProcess, 5000);
+ CloseHandle(procInfo.hThread);
+
+ pid = procInfo.hProcess;
+
+ end:
+ Jim_FreeNewObj(interp, cmdLineObj);
+ if (startInfo.hStdInput != JIM_BAD_FD) {
+ CloseHandle(startInfo.hStdInput);
+ }
+ if (startInfo.hStdOutput != JIM_BAD_FD) {
+ CloseHandle(startInfo.hStdOutput);
+ }
+ if (startInfo.hStdError != JIM_BAD_FD) {
+ CloseHandle(startInfo.hStdError);
+ }
+ return pid;
+}
+#else
+/* Unix-specific implementation */
+static int JimOpenForWrite(const char *filename, int append)
+{
+ return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
}
-int Jim_execInit(Jim_Interp *interp)
+static int JimRewindFd(int fd)
{
- if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
- return JIM_ERR;
+ return lseek(fd, 0L, SEEK_SET);
+}
- Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
- return JIM_OK;
+static int JimCreateTemp(Jim_Interp *interp, const char *contents)
+{
+ char inName[] = "/tmp/tcl.tmp.XXXXXX";
+
+ int fd = mkstemp(inName);
+ if (fd == JIM_BAD_FD) {
+ Jim_SetResultErrno(interp, "couldn't create temp file");
+ return -1;
+ }
+ if (contents) {
+ int length = strlen(contents);
+ if (unlink(inName) == -1 || write(fd, contents, length) != length) {
+ Jim_SetResultErrno(interp, "couldn't write temp file");
+ close(fd);
+ return -1;
+ }
+ lseek(fd, 0L, SEEK_SET);
+ }
+ return fd;
+}
+
+static char **JimSaveEnv(char **env)
+{
+ char **saveenv = Jim_GetEnviron();
+ Jim_SetEnviron(env);
+ return saveenv;
}
+
+static void JimRestoreEnv(char **env)
+{
+ JimFreeEnv(Jim_GetEnviron(), env);
+ Jim_SetEnviron(env);
+}
+#endif
#endif