summaryrefslogtreecommitdiffstats
path: root/Source/kwsys
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2003-06-10 19:46:31 (GMT)
committerBrad King <brad.king@kitware.com>2003-06-10 19:46:31 (GMT)
commit96ccaed54dcf97e802c2e958185cf321dfa7f1e4 (patch)
treefdeb2ff895a72232e4f290f39126faad8ab7b68f /Source/kwsys
parent89cf5d538c53af72c6f212d86e74011277233067 (diff)
downloadCMake-96ccaed54dcf97e802c2e958185cf321dfa7f1e4.zip
CMake-96ccaed54dcf97e802c2e958185cf321dfa7f1e4.tar.gz
CMake-96ccaed54dcf97e802c2e958185cf321dfa7f1e4.tar.bz2
ENH: Added Process execution implementation.
Diffstat (limited to 'Source/kwsys')
-rw-r--r--Source/kwsys/CMakeLists.txt50
-rw-r--r--Source/kwsys/EncodeExecutable.c99
-rw-r--r--Source/kwsys/Process.h.in172
-rw-r--r--Source/kwsys/ProcessFwd9x.c145
-rw-r--r--Source/kwsys/ProcessUNIX.c924
-rw-r--r--Source/kwsys/ProcessWin32.c1234
6 files changed, 2621 insertions, 3 deletions
diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt
index 83ebe22..68bd8c0 100644
--- a/Source/kwsys/CMakeLists.txt
+++ b/Source/kwsys/CMakeLists.txt
@@ -29,9 +29,19 @@ SET(KWSYS_NO_ANSI_STRING_STREAM ${CMAKE_NO_ANSI_STRING_STREAM})
SET(KWSYS_NO_ANSI_FOR_SCOPE ${CMAKE_NO_ANSI_FOR_SCOPE})
SET(CLASSES Directory RegularExpression SystemTools)
-SET(HEADERS Configure)
+SET(H Process)
+SET(HXX Configure)
+
+IF(NOT UNIX)
+ ADD_EXECUTABLE(${KWSYS_NAMESPACE}ProcessFwd9x ProcessFwd9x.c)
+ ADD_EXECUTABLE(${KWSYS_NAMESPACE}EncodeExecutable EncodeExecutable.c)
+ SET(SRCS ProcessWin32.c ${CMAKE_CURRENT_BINARY_DIR}/${KWSYS_NAMESPACE}ProcessFwd9xEnc.c)
+ SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_BINARY_DIR}/${KWSYS_NAMESPACE}ProcessFwd9xEnc.c
+ PROPERTIES GENERATED 1)
+ELSE(NOT UNIX)
+ SET(SRCS ProcessUNIX.c)
+ENDIF(NOT UNIX)
-SET(SRCS)
SET(KWSYS_INCLUDES)
FOREACH(c ${CLASSES})
SET(SRCS ${SRCS} ${c}.cxx)
@@ -42,7 +52,15 @@ FOREACH(c ${CLASSES})
${PROJECT_BINARY_DIR}/../${KWSYS_NAMESPACE}/${c}.hxx)
ENDFOREACH(c)
-FOREACH(h ${HEADERS})
+FOREACH(h ${H})
+ CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/${h}.h.in
+ ${PROJECT_BINARY_DIR}/../${KWSYS_NAMESPACE}/${h}.h
+ @ONLY IMMEDIATE)
+ SET(KWSYS_INCLUDES ${KWSYS_INCLUDES}
+ ${PROJECT_BINARY_DIR}/../${KWSYS_NAMESPACE}/${h}.h)
+ENDFOREACH(h)
+
+FOREACH(h ${HXX})
CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/${h}.hxx.in
${PROJECT_BINARY_DIR}/../${KWSYS_NAMESPACE}/${h}.hxx
@ONLY IMMEDIATE)
@@ -93,3 +111,29 @@ IF(KWSYS_DEFAULTS)
ADD_EXECUTABLE(test1 test1.cxx)
TARGET_LINK_LIBRARIES(test1 ${KWSYS_NAMESPACE})
ENDIF(KWSYS_DEFAULTS)
+
+IF(NOT UNIX)
+ SET(BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
+ IF(EXECUTABLE_OUTPUT_PATH)
+ SET(BIN_DIR ${EXECUTABLE_OUTPUT_PATH})
+ ENDIF(EXECUTABLE_OUTPUT_PATH)
+
+ SET(MAKE_SYSTEM)
+ SET(CFG_INTDIR "/${CMAKE_CFG_INTDIR}")
+ IF(CMAKE_BUILD_TOOL MATCHES "make")
+ SET(CFG_INTDIR "")
+ ENDIF(CMAKE_BUILD_TOOL MATCHES "make")
+
+ SET(CMD ${BIN_DIR}${CFG_INTDIR}/${KWSYS_NAMESPACE}EncodeExecutable.exe)
+ SET(FWD ${BIN_DIR}${CFG_INTDIR}/${KWSYS_NAMESPACE}ProcessFwd9x.exe)
+ ADD_CUSTOM_COMMAND(
+ TARGET ${KWSYS_NAMESPACE}
+ SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/ProcessFwd9x.c
+ COMMAND ${CMD}
+ ARGS ${FWD} ${CMAKE_CURRENT_BINARY_DIR}/${KWSYS_NAMESPACE}ProcessFwd9xEnc.c
+ ${KWSYS_NAMESPACE} ProcessFwd9x
+ OUTPUTS ${CMAKE_CURRENT_BINARY_DIR}/${KWSYS_NAMESPACE}ProcessFwd9xEnc.c
+ DEPENDS ${CMD} ${FWD})
+ ADD_DEPENDENCIES(${KWSYS_NAMESPACE} ${KWSYS_NAMESPACE}ProcessFwd9x
+ ${KWSYS_NAMESPACE}EncodeExecutable)
+ENDIF(NOT UNIX)
diff --git a/Source/kwsys/EncodeExecutable.c b/Source/kwsys/EncodeExecutable.c
new file mode 100644
index 0000000..ef76eb2
--- /dev/null
+++ b/Source/kwsys/EncodeExecutable.c
@@ -0,0 +1,99 @@
+/*=========================================================================
+
+Program: KWSys - Kitware System Library
+Module: $RCSfile$
+Language: C++
+Date: $Date$
+Version: $Revision$
+
+Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
+See http://www.cmake.org/HTML/Copyright.html for details.
+
+This software is distributed WITHOUT ANY WARRANTY; without even
+the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the above copyright notices for more information.
+
+=========================================================================*/
+#include <stdio.h>
+
+int main(int argc, char* argv[])
+{
+ FILE* ifp;
+ FILE* ofp;
+ int i;
+ int n;
+ int count = 0;
+ unsigned char buffer[1024];
+
+ /* Check arguments. */
+ if(argc != 5)
+ {
+ fprintf(stderr, "Usage: %s <input> <output> <kwsys-name> <array>\n",
+ argv[0]);
+ return 1;
+ }
+
+ /* Open the input file. */
+ ifp = fopen(argv[1], "rb");
+ if(!ifp)
+ {
+ fprintf(stderr, "Cannot open input file: \"%s\"\n", argv[1]);
+ return 2;
+ }
+ ofp = fopen(argv[2], "w");
+ if(!ofp)
+ {
+ fprintf(stderr, "Cannot open output file: \"%s\"\n", argv[2]);
+ return 2;
+ }
+
+ /* Prepend header comment. */
+ fprintf(ofp, "/*\n * DO NOT EDIT\n * This file is generated by:\n");
+ fprintf(ofp, " * %s\n */\n\n", argv[0]);
+ fprintf(ofp, "#include <stdio.h>\n\n");
+
+ /* Split file up in 1024-byte chunks. */
+ while((n = fread(buffer, 1, 1024, ifp)) > 0)
+ {
+ fprintf(ofp, "static unsigned char kwsysEncodedArray%s_%d[%d] = {\n",
+ argv[4], count++, n);
+ for(i=0; i < n-1; ++i)
+ {
+ fprintf(ofp, "0x%02X", buffer[i]);
+ if(i%10 == 9)
+ {
+ fprintf(ofp, ",\n");
+ }
+ else
+ {
+ fprintf(ofp, ", ");
+ }
+ }
+ fprintf(ofp, "0x%02X};\n\n", buffer[n-1]);
+ }
+ fclose(ifp);
+
+ /* Provide a function to write the data to a file. */
+ fprintf(ofp, "extern int %sEncodedWriteArray%s(const char* fname)\n",
+ argv[3], argv[4]);
+ fprintf(ofp, "{\n");
+ fprintf(ofp, " FILE* ofp = fopen(fname, \"wb\");\n");
+ fprintf(ofp, " if(!ofp) { return 0; }\n");
+ for(i=0; i < count; ++i)
+ {
+ fprintf(ofp, " if(fwrite(kwsysEncodedArray%s_%d, 1,\n"
+ " sizeof(kwsysEncodedArray%s_%d), ofp) !=\n"
+ " sizeof(kwsysEncodedArray%s_%d))\n",
+ argv[4], i, argv[4], i, argv[4], i);
+ fprintf(ofp, " {\n");
+ fprintf(ofp, " fclose(ofp);\n");
+ fprintf(ofp, " _unlink(fname);\n");
+ fprintf(ofp, " return 0;\n");
+ fprintf(ofp, " }\n");
+ }
+ fprintf(ofp, " fclose(ofp);\n");
+ fprintf(ofp, " return 1;\n");
+ fprintf(ofp, "}\n");
+ fclose(ofp);
+ return 0;
+}
diff --git a/Source/kwsys/Process.h.in b/Source/kwsys/Process.h.in
new file mode 100644
index 0000000..11c63ff
--- /dev/null
+++ b/Source/kwsys/Process.h.in
@@ -0,0 +1,172 @@
+/*=========================================================================
+
+ Program: KWSys - Kitware System Library
+ Module: $RCSfile$
+ Language: C++
+ Date: $Date$
+ Version: $Revision$
+
+ Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
+ See http://www.cmake.org/HTML/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef @KWSYS_NAMESPACE@_Process_h
+#define @KWSYS_NAMESPACE@_Process_h
+
+#define kwsys(x) @KWSYS_NAMESPACE@##x
+#define kwsysProcess_STDOUT kwsys(Process_STDOUT)
+#define kwsysProcess_STDERR kwsys(Process_STDERR)
+#define kwsysProcess_Timeout kwsys(Process_Timeout)
+#define kwsysProcess_Starting kwsys(Process_Starting)
+#define kwsysProcess_Executing kwsys(Process_Executing)
+#define kwsysProcess_Expired kwsys(Process_Expired)
+#define kwsysProcess_Exited kwsys(Process_Exited)
+#define kwsysProcess_Killed kwsys(Process_Killed)
+#define kwsysProcess_Signalled kwsys(Process_Signalled)
+#define kwsysProcess_Error kwsys(Process_Error)
+#define kwsysProcess_State kwsys(Process_State)
+#define kwsysProcess_Pipes_e kwsys(Process_Pipes_e)
+#define kwsysProcess_State_e kwsys(Process_State_e)
+#define kwsysProcess_s kwsys(Process_s)
+#define kwsysProcess kwsys(Process)
+#define kwsysProcess_New kwsys(Process_New)
+#define kwsysProcess_Delete kwsys(Process_Delete)
+#define kwsysProcess_SetCommand kwsys(Process_SetCommand)
+#define kwsysProcess_SetTimeout kwsys(Process_SetTimeout)
+#define kwsysProcess_GetState kwsys(Process_GetState)
+#define kwsysProcess_GetExitCode kwsys(Process_GetExitCode)
+#define kwsysProcess_GetErrorString kwsys(Process_GetErrorString)
+#define kwsysProcess_Execute kwsys(Process_Execute)
+#define kwsysProcess_WaitForData kwsys(Process_WaitForData)
+#define kwsysProcess_WaitForExit kwsys(Process_WaitForExit)
+#define kwsysProcess_Kill kwsys(Process_Kill)
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+typedef enum kwsysProcess_Pipes_e
+{
+ kwsysProcess_STDOUT=1,
+ kwsysProcess_STDERR=2,
+ kwsysProcess_Timeout=255
+} kwsysProcess_Pipes;
+
+typedef enum kwsysProcess_State_e
+{
+ kwsysProcess_Starting, /* Between New and Execute; No process run yet */
+ kwsysProcess_Executing, /* Process is running */
+ kwsysProcess_Expired, /* Process timeout expired and was killed */
+ kwsysProcess_Exited, /* Process exited */
+ kwsysProcess_Killed, /* Process was killed by Kill */
+ kwsysProcess_Signalled, /* Process was terminated by a signal (crash / ctrl-C) */
+ kwsysProcess_Error /* Internal error of Process */
+} kwsysProcess_State;
+
+typedef struct kwsysProcess_s kwsysProcess;
+
+kwsysProcess* kwsysProcess_New();
+
+void kwsysProcess_Delete(kwsysProcess* cp);
+
+void kwsysProcess_SetCommand(kwsysProcess* cp, char const* const* command);
+
+void kwsysProcess_SetTimeout(kwsysProcess* cp, double timeout);
+
+/*
+ * Get the current internal state of the kwsysProcess instance
+ */
+int kwsysProcess_GetState(kwsysProcess* cp);
+
+/*
+ * Get process return code or when signalled, get the signal code
+ */
+int kwsysProcess_GetExitCode(kwsysProcess* cp);
+
+/*
+ * On kwsysProcess_Error get the error message
+ */
+const char* kwsysProcess_GetErrorString(kwsysProcess* cp);
+
+void kwsysProcess_Execute(kwsysProcess* cp);
+
+/*
+ * Block until data available on requested pipe or one of the timeouts expired,
+ * or the process exits. If the pipe is not specified, data on that pipe are
+ * ignored.
+ *
+ * pipes - a list of interested pipes - kwsysProcess_STDOUT | kwsysProcess_STDERR
+ * data - returns pointer to data if read, NULL otherwise
+ * length - length of the returned data
+ * userTimeout - timeout for the current kwsysProcess_WaitForData call
+ * the userTimeout will contain the remaining time
+ *
+ * Returns:
+ * 0 - Process exited or killed or process timeout expired with no data
+ * available, or no process running.
+ * PIPE id otherwise:
+ * kwsysProcess_STDOUT - if stdout is returned
+ * kwsysProcess_STDERR - if stderr is returned
+ * kwsysProcess_Timeout - if user timeout expired
+ */
+int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* length,
+ double* userTimeout);
+
+/*
+ * Block until the process exits or the timeout expires. If no process is
+ * running, return immediatly.
+ *
+ * Returns:
+ * 0 - When user timeout expires
+ * 1 - Otherwise
+ */
+int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout);
+
+/*
+ * Kills the process. kwsysProcess_WaitForExit should still be called
+ * after kwsysProcess_Kill.
+ */
+void kwsysProcess_Kill(kwsysProcess* cp);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+
+/* If we are building a kwsysProcess .c file, let it use these macros. */
+#if !defined(KWSYS_IN_PROCESS_C)
+# undef kwsys
+# undef kwsysProcess_STDOUT
+# undef kwsysProcess_STDERR
+# undef kwsysProcess_Timeout
+# undef kwsysProcess_Starting
+# undef kwsysProcess_Executing
+# undef kwsysProcess_Expired
+# undef kwsysProcess_Exited
+# undef kwsysProcess_Killed
+# undef kwsysProcess_Signalled
+# undef kwsysProcess_Error
+# undef kwsysProcess_State
+# undef kwsysProcess_Pipes_e
+# undef kwsysProcess_State_e
+# undef kwsysProcess_s
+# undef kwsysProcess
+# undef kwsysProcess_New
+# undef kwsysProcess_Delete
+# undef kwsysProcess_SetCommand
+# undef kwsysProcess_SetTimeout
+# undef kwsysProcess_GetState
+# undef kwsysProcess_GetExitCode
+# undef kwsysProcess_GetErrorString
+# undef kwsysProcess_Execute
+# undef kwsysProcess_WaitForData
+# undef kwsysProcess_WaitForExit
+# undef kwsysProcess_Kill
+#endif
+
+#endif
diff --git a/Source/kwsys/ProcessFwd9x.c b/Source/kwsys/ProcessFwd9x.c
new file mode 100644
index 0000000..a6a428a
--- /dev/null
+++ b/Source/kwsys/ProcessFwd9x.c
@@ -0,0 +1,145 @@
+/*=========================================================================
+
+Program: KWSys - Kitware System Library
+Module: $RCSfile$
+Language: C++
+Date: $Date$
+Version: $Revision$
+
+Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
+See http://www.cmake.org/HTML/Copyright.html for details.
+
+This software is distributed WITHOUT ANY WARRANTY; without even
+the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the above copyright notices for more information.
+
+=========================================================================*/
+
+/*
+ On Windows9x platforms, this executable is spawned between a parent
+ process and the child it is invoking to work around a bug. See the
+ Win32 implementation file for details.
+*/
+
+#include <windows.h>
+#include <stdio.h>
+
+int main()
+{
+ /* Process startup information for the real child. */
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ /* The result of waiting for the child to exit. */
+ DWORD waitResult;
+
+ /* The child's process return code. */
+ DWORD retVal;
+
+ /* The command line used to invoke this process. */
+ LPSTR commandLine = GetCommandLine();
+
+ /* Pointer that will be advanced to the beginning of the command
+ line of the real child process. */
+ LPSTR cmdLine = commandLine;
+
+ /* Handle to the error reporting pipe provided by the parent. This
+ is parsed off the command line. */
+ HANDLE errorPipe = 0;
+
+ /* Handle to the event the parent uses to tell us to kill the child.
+ This is parsed off the command line. */
+ HANDLE killEvent = 0;
+
+ /* An array of the handles on which we wait when the child is
+ running. */
+ HANDLE waitHandles[2] = {0, 0};
+
+ /* Move the pointer past the name of this executable. */
+ if(*cmdLine == '"')
+ {
+ ++cmdLine;
+ while(*cmdLine && *cmdLine != '"') { ++cmdLine; }
+ if(*cmdLine) { ++cmdLine; }
+ }
+ else
+ {
+ while(*cmdLine && *cmdLine != ' ') { ++cmdLine; }
+ }
+
+ /* Parse the error pipe handle. */
+ while(*cmdLine && *cmdLine == ' ') { ++cmdLine; }
+ sscanf(cmdLine, "%d", &errorPipe);
+
+ /* Parse the kill event handle. */
+ while(*cmdLine && *cmdLine != ' ') { ++cmdLine; }
+ while(*cmdLine && *cmdLine == ' ') { ++cmdLine; }
+ sscanf(cmdLine, "%d", &killEvent);
+
+ /* Skip to the beginning of the command line of the real child. */
+ while(*cmdLine && *cmdLine != ' ') { ++cmdLine; }
+ while(*cmdLine && *cmdLine == ' ') { ++cmdLine; }
+
+ /* Create the subprocess. */
+ ZeroMemory(&si, sizeof(si));
+ ZeroMemory(&pi, sizeof(pi));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_SHOWDEFAULT;
+ si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ if(!CreateProcess(0, cmdLine, 0, 0, TRUE, 0, 0, 0, &si, &pi))
+ {
+ /* Error creating the process. Report the error to the parent
+ process through the special error reporting pipe. */
+ LPVOID lpMsgBuf;
+ DWORD n;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+ WriteFile(errorPipe, lpMsgBuf, strlen(lpMsgBuf)+1, &n, 0);
+ LocalFree( lpMsgBuf );
+ return 1;
+ }
+ CloseHandle(pi.hThread);
+
+ /* Wait for subprocess to exit or for kill event from parent. */
+ waitHandles[0] = killEvent;
+ waitHandles[1] = pi.hProcess;
+ waitResult = WaitForMultipleObjects(2, waitHandles, 0, INFINITE);
+
+ /* Check what happened. */
+ if(waitResult == WAIT_OBJECT_0)
+ {
+ /* We were asked to kill the child. */
+ TerminateProcess(pi.hProcess, -1);
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ CloseHandle(pi.hProcess);
+ return 1;
+ }
+ else if(GetExitCodeProcess(pi.hProcess, &retVal))
+ {
+ /* The child exited and we could get the return code. */
+ CloseHandle(pi.hProcess);
+ return retVal;
+ }
+ else
+ {
+ /* The child exited and we could not get the return code. Report
+ the problem to the parent process. */
+ DWORD n;
+ const char* msg = "Failed to get process return code.";
+ WriteFile(errorPipe, msg, strlen(msg)+1, &n, 0);
+ CloseHandle(pi.hProcess);
+ return -1;
+ }
+}
diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c
new file mode 100644
index 0000000..2189315
--- /dev/null
+++ b/Source/kwsys/ProcessUNIX.c
@@ -0,0 +1,924 @@
+/*=========================================================================
+
+Program: KWSys - Kitware System Library
+Module: $RCSfile$
+Language: C++
+Date: $Date$
+Version: $Revision$
+
+Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
+See http://www.cmake.org/HTML/Copyright.html for details.
+
+This software is distributed WITHOUT ANY WARRANTY; without even
+the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the above copyright notices for more information.
+
+=========================================================================*/
+#define KWSYS_IN_PROCESS_C
+#include <Process.h>
+
+/*
+
+Implementation for UNIX
+
+On UNIX, a child process is forked to exec the program. Three
+output pipes from the child are read by the parent process using a
+select call to block until data are ready. Two of the pipes are
+stdout and stderr for the child. The third is a special error pipe
+that has two purposes. First, if the child cannot exec the program,
+the error is reported through the error pipe. Second, the error
+pipe is left open until the child exits. This is used in
+conjunction with the timeout on the select call to implement a
+timeout for program even when it closes stdout and stderr.
+*/
+
+#include <stdio.h> /* snprintf */
+#include <stdlib.h> /* malloc, free */
+#include <string.h> /* strdup, strerror, memset */
+#include <sys/time.h> /* struct timeval */
+#include <sys/types.h> /* pid_t, fd_set */
+#include <sys/wait.h> /* waitpid */
+#include <unistd.h> /* pipe, close, fork, execvp, select, _exit */
+#include <fcntl.h> /* fcntl */
+#include <errno.h> /* errno */
+#include <time.h> /* gettimeofday */
+#include <signal.h> /* sigaction */
+
+/* The number of pipes for the child's output. The standard stdout
+ and stderr pipes are the first two. One more pipe is used for the
+ child to report errors to the parent before the real process is
+ invoked. */
+#define KWSYSPE_PIPE_COUNT 3
+#define KWSYSPE_PIPE_STDOUT 0
+#define KWSYSPE_PIPE_STDERR 1
+#define KWSYSPE_PIPE_ERROR 2
+
+/* The maximum amount to read from a pipe at a time. */
+#define KWSYSPE_PIPE_BUFFER_SIZE 1024
+
+typedef struct timeval kwsysProcessTime;
+
+/*--------------------------------------------------------------------------*/
+static void kwsysProcessInitialize(kwsysProcess* cp);
+static void kwsysProcessCleanup(kwsysProcess* cp, int error);
+static void kwsysProcessCleanupDescriptor(int* pfd);
+static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
+ kwsysProcessTime* timeoutTime);
+static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime,
+ kwsysProcessTime* timeoutLength);
+static kwsysProcessTime kwsysProcessTimeGetCurrent();
+static double kwsysProcessTimeToDouble(kwsysProcessTime t);
+static kwsysProcessTime kwsysProcessTimeFromDouble(double d);
+static int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2);
+static kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, kwsysProcessTime in2);
+static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProcessTime in2);
+static void kwsysProcessChildErrorExit(kwsysProcess* cp);
+static void kwsysProcessRestoreDefaultSignalHandlers();
+
+/*--------------------------------------------------------------------------*/
+/* Structure containing data used to implement the child's execution. */
+struct kwsysProcess_s
+{
+ /* The command line to execute. */
+ char** Command;
+
+ /* Descriptors for the read ends of the child's output pipes. */
+ int PipeReadEnds[KWSYSPE_PIPE_COUNT];
+
+ /* Descriptors for the write ends of the child's output pipes. */
+ int PipeWriteEnds[KWSYSPE_PIPE_COUNT];
+
+ /* Buffer for pipe data. */
+ char PipeBuffer[KWSYSPE_PIPE_BUFFER_SIZE];
+
+ /* Process ID returned by the fork. */
+ pid_t ForkPID;
+
+ /* Flag for whether the child reported an error. */
+ int ChildError;
+
+ /* The timeout length. */
+ float Timeout;
+
+ /* Time at which the child started. Negative for no timeout. */
+ kwsysProcessTime StartTime;
+
+ /* Time at which the child will timeout. Negative for no timeout. */
+ kwsysProcessTime TimeoutTime;
+
+ /* Flag for whether the timeout expired. */
+ int TimeoutExpired;
+
+ /* The old SIGCHLD handler. */
+ struct sigaction OldSigChldAction;
+
+ /* The number of pipes left open during execution. */
+ int PipesLeft;
+
+ /* File descriptor set for call to select. */
+ fd_set PipeSet;
+
+ /* The current status of the child process. */
+ int State;
+
+ /* The exit code of the child process, if any. */
+ int ExitCode;
+
+ /* Whether the process was killed. */
+ int Killed;
+
+ /* Buffer for error message in case of failure. */
+ char ErrorMessage[KWSYSPE_PIPE_BUFFER_SIZE+1];
+ int ErrorMessageLength;
+};
+
+/*--------------------------------------------------------------------------*/
+kwsysProcess* kwsysProcess_New()
+{
+ /* Allocate a process control structure. */
+ kwsysProcess* cp = (kwsysProcess*)malloc(sizeof(kwsysProcess));
+ if(!cp)
+ {
+ return 0;
+ }
+ memset(cp, 0, sizeof(kwsysProcess));
+ cp->State = kwsysProcess_Starting;
+ return cp;
+}
+
+/*--------------------------------------------------------------------------*/
+void kwsysProcess_Delete(kwsysProcess* cp)
+{
+ /* If the process is executing, wait for it to finish. */
+ if(cp->State == kwsysProcess_Executing)
+ {
+ kwsysProcess_WaitForExit(cp, 0);
+ }
+
+ /* Free memory. */
+ kwsysProcess_SetCommand(cp, 0);
+ free(cp);
+}
+
+/*--------------------------------------------------------------------------*/
+void kwsysProcess_SetCommand(kwsysProcess* cp, char const* const* command)
+{
+ if(cp->Command)
+ {
+ char** c = cp->Command;
+ while(*c)
+ {
+ free(*c++);
+ }
+ free(cp->Command);
+ cp->Command = 0;
+ }
+ if(command)
+ {
+ char const* const* c = command;
+ int n = 0;
+ int i = 0;
+ while(*c++);
+ n = c - command - 1;
+ cp->Command = (char**)malloc((n+1)*sizeof(char*));
+ for(i=0; i < n; ++i)
+ {
+ cp->Command[i] = strdup(command[i]);
+ }
+ cp->Command[n] = 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+void kwsysProcess_SetTimeout(kwsysProcess* cp, double timeout)
+{
+ cp->Timeout = timeout;
+ if(cp->Timeout < 0)
+ {
+ cp->Timeout = 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+int kwsysProcess_GetState(kwsysProcess* cp)
+{
+ return cp->State;
+}
+
+/*--------------------------------------------------------------------------*/
+int kwsysProcess_GetExitCode(kwsysProcess* cp)
+{
+ return cp->ExitCode;
+}
+
+/*--------------------------------------------------------------------------*/
+const char* kwsysProcess_GetErrorString(kwsysProcess* cp)
+{
+ if(cp->State == kwsysProcess_Error)
+ {
+ return cp->PipeBuffer;
+ }
+ return "";
+}
+
+/*--------------------------------------------------------------------------*/
+void kwsysProcess_Execute(kwsysProcess* cp)
+{
+ int i;
+ struct sigaction newSigChldAction;
+
+ /* Do not execute a second copy simultaneously. */
+ if(cp->State == kwsysProcess_Executing)
+ {
+ return;
+ }
+
+ /* Initialize the control structure for a new process. */
+ kwsysProcessInitialize(cp);
+
+ /* We want no special handling of SIGCHLD. Repeat call until it is
+ not interrupted. */
+ newSigChldAction.sa_handler = SIG_DFL;
+ while((sigaction(SIGCHLD, &newSigChldAction, &cp->OldSigChldAction) < 0) &&
+ (errno == EINTR));
+
+ /* Create pipes for subprocess output. */
+ for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
+ {
+ int p[2];
+
+ /* Create the pipe. */
+ if(pipe(p) < 0)
+ {
+ kwsysProcessCleanup(cp, 1);
+ return;
+ }
+
+ /* Set close-on-exec flag on the pipe's ends. */
+ if((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) ||
+ (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0))
+ {
+ kwsysProcessCleanup(cp, 1);
+ return;
+ }
+
+ /* Store the pipe. */
+ cp->PipeReadEnds[i] = p[0];
+ cp->PipeWriteEnds[i] = p[1];
+ }
+
+ /* The timeout period starts now. */
+ cp->StartTime = kwsysProcessTimeGetCurrent();
+ cp->TimeoutTime.tv_sec = -1;
+ cp->TimeoutTime.tv_usec = -1;
+
+ /* Fork off a child process. */
+ cp->ForkPID = fork();
+ if(cp->ForkPID < 0)
+ {
+ kwsysProcessCleanup(cp, 1);
+ return;
+ }
+
+ /* If this is the child process, run the real process. */
+ if(cp->ForkPID == 0)
+ {
+ /* Close stdin. */
+ close(0);
+
+ /* Setup the stdout/stderr pipes. */
+ dup2(cp->PipeWriteEnds[KWSYSPE_PIPE_STDOUT], 1);
+ dup2(cp->PipeWriteEnds[KWSYSPE_PIPE_STDERR], 2);
+
+ /* Clear the close-on-exec flag for stdout, stderr, and the child
+ error report pipe. All other pipe handles will be closed when
+ exec succeeds. */
+ fcntl(1, F_SETFD, 0);
+ fcntl(2, F_SETFD, 0);
+ fcntl(cp->PipeWriteEnds[KWSYSPE_PIPE_ERROR], F_SETFD, 0);
+
+ /* Restore all default signal handlers. */
+ kwsysProcessRestoreDefaultSignalHandlers();
+
+ /* Execute the real process. If successful, this does not return. */
+ execvp(cp->Command[0], cp->Command);
+
+ /* Failure. Report error to parent and terminate. */
+ kwsysProcessChildErrorExit(cp);
+ }
+
+ /* The parent process does not need the pipe write ends. */
+ for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
+ {
+ kwsysProcessCleanupDescriptor(&cp->PipeWriteEnds[i]);
+ }
+
+ /* All the pipes are now open. */
+ cp->PipesLeft = KWSYSPE_PIPE_COUNT;
+
+ /* The process has now started. */
+ cp->State = kwsysProcess_Executing;
+}
+
+/*--------------------------------------------------------------------------*/
+int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* length,
+ double* userTimeout)
+{
+ int i;
+ int max = -1;
+ kwsysProcessTime* timeout = 0;
+ kwsysProcessTime timeoutLength;
+ kwsysProcessTime timeoutTime;
+ kwsysProcessTime userStartTime;
+ int user = 0;
+ int expired = 0;
+ int pipeId = 0;
+ int numReady = 0;
+
+ /* Record the time at which user timeout period starts. */
+ if(userTimeout)
+ {
+ userStartTime = kwsysProcessTimeGetCurrent();
+ }
+
+ /* Calculate the time at which a timeout will expire, and whether it
+ is the user or process timeout. */
+ user = kwsysProcessGetTimeoutTime(cp, userTimeout, &timeoutTime);
+
+ /* Data can only be available when pipes are open. If the process
+ is not running, cp->PipesLeft will be 0. */
+ while(cp->PipesLeft > 0)
+ {
+ /* Check for any open pipes with data reported ready by the last
+ call to select. */
+ for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
+ {
+ if(cp->PipeReadEnds[i] >= 0 &&
+ FD_ISSET(cp->PipeReadEnds[i], &cp->PipeSet))
+ {
+ int n;
+
+ /* We are handling this pipe now. Remove it from the set. */
+ FD_CLR(cp->PipeReadEnds[i], &cp->PipeSet);
+
+ /* The pipe is ready to read without blocking. Keep trying to
+ read until the operation is not interrupted. */
+ while(((n = read(cp->PipeReadEnds[i], cp->PipeBuffer,
+ KWSYSPE_PIPE_BUFFER_SIZE)) < 0) && (errno == EINTR));
+ if(n > 0)
+ {
+ /* We have data on this pipe. */
+ if(i == KWSYSPE_PIPE_ERROR)
+ {
+ /* This is data on the special error reporting pipe. The
+ child process failed to execute the program. */
+ cp->ChildError = 1;
+ if(n > KWSYSPE_PIPE_BUFFER_SIZE - cp->ErrorMessageLength)
+ {
+ n = KWSYSPE_PIPE_BUFFER_SIZE - cp->ErrorMessageLength;
+ }
+ if(n > 0)
+ {
+ memcpy(cp->ErrorMessage+cp->ErrorMessageLength,
+ cp->PipeBuffer, n);
+ cp->ErrorMessageLength += n;
+ cp->ErrorMessage[cp->ErrorMessageLength] = 0;
+ }
+ }
+ else if(pipes & (1 << i))
+ {
+ /* Caller wants this data. Report it. */
+ *data = cp->PipeBuffer;
+ *length = n;
+ pipeId = (1 << i);
+ break;
+ }
+ }
+ else
+ {
+ /* We are done reading from this pipe. */
+ kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
+ --cp->PipesLeft;
+ }
+ }
+ }
+
+ /* If we have data, break early. */
+ if(pipeId)
+ {
+ break;
+ }
+
+ /* Make sure the set is empty (it should always be empty here
+ anyway). */
+ FD_ZERO(&cp->PipeSet);
+
+ /* Add the pipe reading ends that are still open. */
+ max = -1;
+ for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
+ {
+ if(cp->PipeReadEnds[i] >= 0)
+ {
+ FD_SET(cp->PipeReadEnds[i], &cp->PipeSet);
+ if(cp->PipeReadEnds[i] > max)
+ {
+ max = cp->PipeReadEnds[i];
+ }
+ }
+ }
+
+ /* Make sure we have a non-empty set. */
+ if(max < 0)
+ {
+ /* All pipes have closed. Child has terminated. */
+ break;
+ }
+
+ /* Setup a timeout if required. */
+ if(timeoutTime.tv_sec < 0)
+ {
+ timeout = 0;
+ }
+ else
+ {
+ timeout = &timeoutLength;
+ }
+ if(kwsysProcessGetTimeoutLeft(&timeoutTime, &timeoutLength))
+ {
+ /* Timeout has already expired. */
+ expired = 1;
+ break;
+ }
+
+ /* Run select to block until data are available. Repeat call
+ until it is not interrupted. */
+ while(((numReady = select(max+1, &cp->PipeSet, 0, 0, timeout)) < 0) &&
+ (errno == EINTR));
+
+ /* Check result of select. */
+ if(numReady == 0)
+ {
+ /* Select's timeout expired. */
+ expired = 1;
+ break;
+ }
+ else if(numReady < 0)
+ {
+ /* Select returned an error. Leave the error description in the
+ pipe buffer. */
+ snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE,
+ "%s", strerror(errno));
+
+ /* Kill the child now. */
+ kwsysProcess_Kill(cp);
+ cp->Killed = 0;
+ cp->ChildError = 1;
+ cp->PipesLeft = 0;
+ }
+ }
+
+ /* Update the user timeout. */
+ if(userTimeout)
+ {
+ kwsysProcessTime userEndTime = kwsysProcessTimeGetCurrent();
+ kwsysProcessTime difference = kwsysProcessTimeSubtract(userEndTime,
+ userStartTime);
+ double d = kwsysProcessTimeToDouble(difference);
+ *userTimeout -= d;
+ if(*userTimeout < 0)
+ {
+ *userTimeout = 0;
+ }
+ }
+
+ /* Check what happened. */
+ if(pipeId)
+ {
+ /* Data are ready on a pipe. */
+ return pipeId;
+ }
+ else if(expired)
+ {
+ /* A timeout has expired. */
+ if(user)
+ {
+ /* The user timeout has expired. It has no time left. */
+ return kwsysProcess_Timeout;
+ }
+ else
+ {
+ /* The process timeout has expired. Kill the child now. */
+ kwsysProcess_Kill(cp);
+ cp->Killed = 0;
+ cp->TimeoutExpired = 1;
+ cp->PipesLeft = 0;
+ return 0;
+ }
+ }
+ else
+ {
+ /* No pipes are left open. */
+ return 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
+{
+ int result = 0;
+ int status = 0;
+ int pipe = 0;
+
+ /* Make sure we are executing a process. */
+ if(cp->State != kwsysProcess_Executing)
+ {
+ return 1;
+ }
+
+ /* Wait for all the pipes to close. Ignore all data. */
+ while((pipe = kwsysProcess_WaitForData(cp, 0, 0, 0, userTimeout)) > 0)
+ {
+ if(pipe == kwsysProcess_Timeout)
+ {
+ return 0;
+ }
+ }
+
+ /* Wait for the child to terminate. The process should have already
+ exited because KWSYSPE_PIPE_ERROR has been closed by this point.
+ Repeat the call until it is not interrupted. */
+ while(((result = waitpid(cp->ForkPID, &status, 0)) < 0) && (errno == EINTR));
+ if(result <= 0)
+ {
+ /* Unexpected error. */
+ kwsysProcessCleanup(cp, 1);
+ return 1;
+ }
+
+ /* Check whether the child reported an error invoking the process. */
+ if(cp->ChildError)
+ {
+ /* The error message is already in its buffer. Tell
+ kwsysProcessCleanup to not create it. */
+ kwsysProcessCleanup(cp, 0);
+ cp->State = kwsysProcess_Error;
+ return 1;
+ }
+
+ /* Determine the outcome. */
+ if(cp->Killed)
+ {
+ /* We killed the child. */
+ cp->State = kwsysProcess_Killed;
+ }
+ else if(cp->TimeoutExpired)
+ {
+ /* The timeout expired. */
+ cp->State = kwsysProcess_Expired;
+ }
+ else if(WIFEXITED(status))
+ {
+ /* The child exited. */
+ cp->State = kwsysProcess_Exited;
+ cp->ExitCode = (int)WEXITSTATUS(status);
+ }
+ else if(WIFSIGNALED(status))
+ {
+ /* The child received an unhandled signal. */
+ cp->State = kwsysProcess_Signalled;
+ cp->ExitCode = (int)WTERMSIG(status);
+ }
+ else
+ {
+ /* Error getting the child return code. */
+ strcpy(cp->ErrorMessage, "Error getting child return code.");
+ cp->State = kwsysProcess_Error;
+ }
+
+ /* Normal cleanup. */
+ kwsysProcessCleanup(cp, 0);
+ return 1;
+}
+
+/*--------------------------------------------------------------------------*/
+void kwsysProcess_Kill(kwsysProcess* cp)
+{
+ /* Make sure we are executing a process. */
+ if(cp->State != kwsysProcess_Executing)
+ {
+ return;
+ }
+
+ /* Kill the child. */
+ cp->Killed = 1;
+ kill(cp->ForkPID, SIGKILL);
+}
+
+/*--------------------------------------------------------------------------*/
+/* Initialize a process control structure for kwsysProcess_Execute. */
+void kwsysProcessInitialize(kwsysProcess* cp)
+{
+ int i;
+ for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
+ {
+ cp->PipeReadEnds[i] = -1;
+ cp->PipeWriteEnds[i] = -1;
+ }
+ cp->ForkPID = -1;
+ cp->ChildError = 0;
+ cp->StartTime.tv_sec = -1;
+ cp->StartTime.tv_usec = -1;
+ cp->TimeoutTime.tv_sec = -1;
+ cp->TimeoutTime.tv_usec = -1;
+ cp->TimeoutExpired = 0;
+ cp->PipesLeft = 0;
+ FD_ZERO(&cp->PipeSet);
+ cp->State = kwsysProcess_Starting;
+ cp->Killed = 0;
+ cp->ExitCode = 0;
+ cp->ErrorMessage[0] = 0;
+ cp->ErrorMessageLength = 0;
+}
+
+/*--------------------------------------------------------------------------*/
+/* Free all resources used by the given kwsysProcess instance that were
+ allocated by kwsysProcess_Execute. */
+void kwsysProcessCleanup(kwsysProcess* cp, int error)
+{
+ int i;
+
+ /* If cleaning up due to an error, report the error message. */
+ if(error)
+ {
+ snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE, "%s", strerror(errno));
+ cp->State = kwsysProcess_Error;
+ }
+
+ /* Restore the SIGCHLD handler. */
+ while((sigaction(SIGCHLD, &cp->OldSigChldAction, 0) < 0) &&
+ (errno == EINTR));
+
+ /* Close pipe handles. */
+ for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
+ {
+ kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]);
+ kwsysProcessCleanupDescriptor(&cp->PipeWriteEnds[i]);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+/* Close the given file descriptor if it is open. Reset its value to -1. */
+void kwsysProcessCleanupDescriptor(int* pfd)
+{
+ if(pfd && *pfd >= 0)
+ {
+ /* Keep trying to close until it is not interrupted by a
+ * signal. */
+ while((close(*pfd) < 0) && (errno == EINTR));
+ *pfd = -1;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+/* Get the time at which either the process or user timeout will
+ expire. Returns 1 if the user timeout is first, and 0 otherwise. */
+int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
+ kwsysProcessTime* timeoutTime)
+{
+ /* The first time this is called, we need to calculate the time at
+ which the child will timeout. */
+ if(cp->Timeout && cp->TimeoutTime.tv_sec < 0)
+ {
+ kwsysProcessTime length = kwsysProcessTimeFromDouble(cp->Timeout);
+ cp->TimeoutTime = kwsysProcessTimeAdd(cp->StartTime, length);
+ }
+
+ /* Start with process timeout. */
+ *timeoutTime = cp->TimeoutTime;
+
+ /* Check if the user timeout is earlier. */
+ if(userTimeout)
+ {
+ kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent();
+ kwsysProcessTime userTimeoutLength = kwsysProcessTimeFromDouble(*userTimeout);
+ kwsysProcessTime userTimeoutTime = kwsysProcessTimeAdd(currentTime,
+ userTimeoutLength);
+ if(kwsysProcessTimeLess(userTimeoutTime, *timeoutTime))
+ {
+ *timeoutTime = userTimeoutTime;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+/* Get the length of time before the given timeout time arrives.
+ Returns 1 if the time has already arrived, and 0 otherwise. */
+int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime,
+ kwsysProcessTime* timeoutLength)
+{
+ if(timeoutTime->tv_sec < 0)
+ {
+ /* No timeout time has been requested. */
+ return 0;
+ }
+ else
+ {
+ /* Calculate the remaining time. */
+ kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent();
+ *timeoutLength = kwsysProcessTimeSubtract(*timeoutTime, currentTime);
+ if(timeoutLength->tv_sec < 0)
+ {
+ /* Timeout has already expired. */
+ return 1;
+ }
+ else
+ {
+ /* There is some time left. */
+ return 0;
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+kwsysProcessTime kwsysProcessTimeGetCurrent()
+{
+ kwsysProcessTime current;
+ gettimeofday(&current, 0);
+ return current;
+}
+
+/*--------------------------------------------------------------------------*/
+double kwsysProcessTimeToDouble(kwsysProcessTime t)
+{
+ return (double)t.tv_sec + t.tv_usec*0.000001;
+}
+
+/*--------------------------------------------------------------------------*/
+kwsysProcessTime kwsysProcessTimeFromDouble(double d)
+{
+ kwsysProcessTime t;
+ t.tv_sec = (long)d;
+ t.tv_usec = (long)((d-t.tv_sec)*1000000);
+ return t;
+}
+
+/*--------------------------------------------------------------------------*/
+int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2)
+{
+ return ((in1.tv_sec < in2.tv_sec) ||
+ ((in1.tv_sec == in2.tv_sec) && (in1.tv_usec < in2.tv_usec)));
+}
+
+/*--------------------------------------------------------------------------*/
+kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, kwsysProcessTime in2)
+{
+ kwsysProcessTime out;
+ out.tv_sec = in1.tv_sec + in2.tv_sec;
+ out.tv_usec = in1.tv_usec + in2.tv_usec;
+ if(out.tv_usec > 1000000)
+ {
+ out.tv_usec -= 1000000;
+ out.tv_sec += 1;
+ }
+ return out;
+}
+
+/*--------------------------------------------------------------------------*/
+kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProcessTime in2)
+{
+ kwsysProcessTime out;
+ out.tv_sec = in1.tv_sec - in2.tv_sec;
+ out.tv_usec = in1.tv_usec - in2.tv_usec;
+ if(out.tv_usec < 0)
+ {
+ out.tv_usec += 1000000;
+ out.tv_sec -= 1;
+ }
+ return out;
+}
+
+/*--------------------------------------------------------------------------*/
+/* When the child process encounters an error before its program is
+ invoked, this is called to report the error to the parent and
+ exit. */
+void kwsysProcessChildErrorExit(kwsysProcess* cp)
+{
+ /* Construct the error message. */
+ char buffer[KWSYSPE_PIPE_BUFFER_SIZE];
+ snprintf(buffer, KWSYSPE_PIPE_BUFFER_SIZE, "%s", strerror(errno));
+
+ /* Report the error to the parent through the special pipe. */
+ write(cp->PipeWriteEnds[KWSYSPE_PIPE_ERROR], buffer, strlen(buffer));
+
+ /* Terminate without cleanup. */
+ _exit(1);
+}
+
+/*--------------------------------------------------------------------------*/
+/* Restores all signal handlers to their default values. */
+void kwsysProcessRestoreDefaultSignalHandlers()
+{
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+#ifdef SIGHUP
+ sigaction(SIGHUP, &act, 0);
+#endif
+#ifdef SIGINT
+ sigaction(SIGINT, &act, 0);
+#endif
+#ifdef SIGQUIT
+ sigaction(SIGQUIT, &act, 0);
+#endif
+#ifdef SIGILL
+ sigaction(SIGILL, &act, 0);
+#endif
+#ifdef SIGTRAP
+ sigaction(SIGTRAP, &act, 0);
+#endif
+#ifdef SIGABRT
+ sigaction(SIGABRT, &act, 0);
+#endif
+#ifdef SIGIOT
+ sigaction(SIGIOT, &act, 0);
+#endif
+#ifdef SIGBUS
+ sigaction(SIGBUS, &act, 0);
+#endif
+#ifdef SIGFPE
+ sigaction(SIGFPE, &act, 0);
+#endif
+#ifdef SIGUSR1
+ sigaction(SIGUSR1, &act, 0);
+#endif
+#ifdef SIGSEGV
+ sigaction(SIGSEGV, &act, 0);
+#endif
+#ifdef SIGUSR2
+ sigaction(SIGUSR2, &act, 0);
+#endif
+#ifdef SIGPIPE
+ sigaction(SIGPIPE, &act, 0);
+#endif
+#ifdef SIGALRM
+ sigaction(SIGALRM, &act, 0);
+#endif
+#ifdef SIGTERM
+ sigaction(SIGTERM, &act, 0);
+#endif
+#ifdef SIGSTKFLT
+ sigaction(SIGSTKFLT, &act, 0);
+#endif
+#ifdef SIGCLD
+ sigaction(SIGCLD, &act, 0);
+#endif
+#ifdef SIGCHLD
+ sigaction(SIGCHLD, &act, 0);
+#endif
+#ifdef SIGCONT
+ sigaction(SIGCONT, &act, 0);
+#endif
+#ifdef SIGTSTP
+ sigaction(SIGTSTP, &act, 0);
+#endif
+#ifdef SIGTTIN
+ sigaction(SIGTTIN, &act, 0);
+#endif
+#ifdef SIGTTOU
+ sigaction(SIGTTOU, &act, 0);
+#endif
+#ifdef SIGURG
+ sigaction(SIGURG, &act, 0);
+#endif
+#ifdef SIGXCPU
+ sigaction(SIGXCPU, &act, 0);
+#endif
+#ifdef SIGXFSZ
+ sigaction(SIGXFSZ, &act, 0);
+#endif
+#ifdef SIGVTALRM
+ sigaction(SIGVTALRM, &act, 0);
+#endif
+#ifdef SIGPROF
+ sigaction(SIGPROF, &act, 0);
+#endif
+#ifdef SIGWINCH
+ sigaction(SIGWINCH, &act, 0);
+#endif
+#ifdef SIGPOLL
+ sigaction(SIGPOLL, &act, 0);
+#endif
+#ifdef SIGIO
+ sigaction(SIGIO, &act, 0);
+#endif
+#ifdef SIGPWR
+ sigaction(SIGPWR, &act, 0);
+#endif
+#ifdef SIGSYS
+ sigaction(SIGSYS, &act, 0);
+#endif
+#ifdef SIGUNUSED
+ sigaction(SIGUNUSED, &act, 0);
+#endif
+}
diff --git a/Source/kwsys/ProcessWin32.c b/Source/kwsys/ProcessWin32.c
new file mode 100644
index 0000000..5a168da
--- /dev/null
+++ b/Source/kwsys/ProcessWin32.c
@@ -0,0 +1,1234 @@
+/*=========================================================================
+
+Program: KWSys - Kitware System Library
+Module: $RCSfile$
+Language: C++
+Date: $Date$
+Version: $Revision$
+
+Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
+See http://www.cmake.org/HTML/Copyright.html for details.
+
+This software is distributed WITHOUT ANY WARRANTY; without even
+the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the above copyright notices for more information.
+
+=========================================================================*/
+#define KWSYS_IN_PROCESS_C
+#include <Process.h>
+
+/*
+
+Implementation for Windows
+
+On windows, a thread is created to wait for data on each pipe. The
+threads are synchronized with the main thread to simulate the use of
+a UNIX-style select system call.
+
+On Windows9x platforms, a small WIN32 console application is spawned
+in-between the calling process and the actual child to be executed.
+This is to work-around a problem with connecting pipes from WIN16
+console applications to WIN32 applications.
+
+For more information, please check Microsoft Knowledge Base Articles
+Q190351 and Q150956.
+
+*/
+
+#include <windows.h> /* Windows API */
+#include <string.h> /* strlen, strdup */
+#include <stdio.h> /* sprintf */
+#include <process.h> /* _getpid */
+#include <io.h> /* _unlink */
+
+/* The number of pipes for the child's output. The standard stdout
+ and stderr pipes are the first two. One more pipe is used on Win9x
+ for the forwarding executable to use in reporting problems. */
+#define CMPE_PIPE_COUNT 3
+#define CMPE_PIPE_STDOUT 0
+#define CMPE_PIPE_STDERR 1
+#define CMPE_PIPE_ERROR 2
+
+/* The maximum amount to read from a pipe at a time. */
+#define CMPE_PIPE_BUFFER_SIZE 1024
+
+#define kwsysEncodedWriteArrayProcessFwd kwsys(EncodedWriteArrayProcessFwd)
+
+typedef LARGE_INTEGER kwsysProcessTime;
+
+/*--------------------------------------------------------------------------*/
+typedef struct kwsysProcessPipeData_s kwsysProcessPipeData;
+static DWORD WINAPI kwsysProcessPipeThread(LPVOID ptd);
+static void kwsysProcessPipeThreadReadPipe(kwsysProcess* cp, kwsysProcessPipeData* td);
+static void kwsysProcessCleanupHandle(PHANDLE h);
+static void kwsysProcessCleanup(kwsysProcess* cp, int error);
+static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
+ kwsysProcessTime* timeoutTime);
+static int kwsysProcessGetTimeoutLeft(kwsysProcess* cp, kwsysProcessTime* timeoutTime,
+ kwsysProcessTime* timeoutLength);
+static kwsysProcessTime kwsysProcessTimeGetCurrent();
+static DWORD kwsysProcessTimeToDWORD(kwsysProcessTime t);
+static double kwsysProcessTimeToDouble(kwsysProcessTime t);
+static kwsysProcessTime kwsysProcessTimeFromDouble(double d);
+static int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2);
+static kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, kwsysProcessTime in2);
+static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProcessTime in2);
+extern int kwsysEncodedWriteArrayProcessFwd9x(const char* fname);
+
+/*--------------------------------------------------------------------------*/
+/* A structure containing data for each pipe's thread. */
+struct kwsysProcessPipeData_s
+{
+ /* ------------- Data managed per instance of kwsysProcess ------------- */
+
+ /* Handle for the thread for this pipe. */
+ HANDLE Thread;
+
+ /* Semaphore indicating a process and pipe are available. */
+ HANDLE Ready;
+
+ /* Semaphore indicating when this thread's buffer is empty. */
+ HANDLE Empty;
+
+ /* Semaphore indicating a pipe thread has reset for another process. */
+ HANDLE Reset;
+
+ /* Index of this pipe. */
+ int Index;
+
+ /* The kwsysProcess instance owning this pipe. */
+ kwsysProcess* Process;
+
+ /* ------------- Data managed per call to Execute ------------- */
+
+ /* Buffer for data read in this pipe's thread. */
+ char DataBuffer[CMPE_PIPE_BUFFER_SIZE];
+
+ /* The length of the data stored in the buffer. */
+ DWORD DataLength;
+
+ /* Whether the pipe has been closed. */
+ int Closed;
+
+ /* Handle for the read end of this pipe. */
+ HANDLE Read;
+
+ /* Handle for the write end of this pipe. */
+ HANDLE Write;
+};
+
+/*--------------------------------------------------------------------------*/
+/* Structure containing data used to implement the child's execution. */
+struct kwsysProcess_s
+{
+ /* ------------- Data managed per instance of kwsysProcess ------------- */
+
+ /* The status of the process. */
+ int State;
+
+ /* The command line to execute. */
+ char* Command;
+
+ /* On Win9x platforms, the path to the forwarding executable. */
+ char* Win9x;
+
+ /* On Win9x platforms, the kill event for the forwarding executable. */
+ HANDLE Win9xKillEvent;
+
+ /* Mutex to protect the shared index used by threads to report data. */
+ HANDLE SharedIndexMutex;
+
+ /* Semaphore used by threads to signal data ready. */
+ HANDLE Full;
+
+ /* The number of pipes needed to implement the child's execution.
+ This is 3 on Win9x and 2 otherwise. */
+ int PipeCount;
+
+ /* Whether we are currently deleting this kwsysProcess instance. */
+ int Deleting;
+
+ /* Data specific to each pipe and its thread. */
+ kwsysProcessPipeData Pipe[CMPE_PIPE_COUNT];
+
+ /* ------------- Data managed per call to Execute ------------- */
+
+ /* The process exit code, if any. */
+ int ExitCode;
+
+ /* Index of last pipe to report data, if any. */
+ int CurrentIndex;
+
+ /* Index shared by threads to report data. */
+ int SharedIndex;
+
+ /* The timeout length. */
+ double Timeout;
+
+ /* Time at which the child started. */
+ kwsysProcessTime StartTime;
+
+ /* Time at which the child will timeout. Negative for no timeout. */
+ kwsysProcessTime TimeoutTime;
+
+ /* Flag for whether the process was killed. */
+ int Killed;
+
+ /* Flag for whether the timeout expired. */
+ int TimeoutExpired;
+
+ /* Flag for whether the process has terminated. */
+ int Terminated;
+
+ /* The number of pipes still open during execution and while waiting
+ for pipes to close after process termination. */
+ int PipesLeft;
+
+ /* Buffer for error messages (possibly from Win9x child). */
+ char ErrorMessage[CMPE_PIPE_BUFFER_SIZE+1];
+ int ErrorMessageLength;
+
+ /* The actual command line that will be used to create the process. */
+ char* RealCommand;
+
+ /* Windows process information data. */
+ PROCESS_INFORMATION ProcessInformation;
+};
+
+/*--------------------------------------------------------------------------*/
+kwsysProcess* kwsysProcess_New()
+{
+ int i;
+
+ /* Process control structure. */
+ kwsysProcess* cp;
+
+ /* Path to Win9x forwarding executable. */
+ char* win9x = 0;
+
+ /* Windows version number data. */
+ OSVERSIONINFO osv;
+
+ /* Allocate a process control structure. */
+ cp = (kwsysProcess*)malloc(sizeof(kwsysProcess));
+ ZeroMemory(cp, sizeof(*cp));
+
+ /* Set initial status. */
+ cp->State = kwsysProcess_Starting;
+
+ /* Choose a method of running the child based on version of
+ windows. */
+ ZeroMemory(&osv, sizeof(osv));
+ osv.dwOSVersionInfoSize = sizeof(osv);
+ GetVersionEx(&osv);
+ if(osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
+ {
+ /* This is Win9x. We need the console forwarding executable to
+ work-around a Windows 9x bug. */
+ char fwdName[_MAX_FNAME+1] = "";
+ char tempDir[_MAX_PATH+1] = "";
+
+ /* We will try putting the executable in the system temp
+ directory. */
+ DWORD length = GetEnvironmentVariable("TEMP", tempDir, _MAX_PATH);
+
+ /* Construct the executable name from the process id and kwsysProcess
+ instance. This should be unique. */
+ sprintf(fwdName, "cmw9xfwd_%u_%p.exe", _getpid(), cp);
+
+ /* If the environment variable "TEMP" gave us a directory, use it. */
+ if(length > 0 && length <= _MAX_PATH)
+ {
+ /* Make sure there is no trailing slash. */
+ size_t tdlen = strlen(tempDir);
+ if(tempDir[tdlen-1] == '/' || tempDir[tdlen-1] == '\\')
+ {
+ tempDir[tdlen-1] = 0;
+ --tdlen;
+ }
+
+ /* Allocate a buffer to hold the forwarding executable path. */
+ win9x = (char*)malloc(tdlen + strlen(fwdName) + 2);
+ if(!win9x)
+ {
+ kwsysProcess_Delete(cp);
+ return 0;
+ }
+
+ /* Construct the full path to the forwarding executable. */
+ sprintf(win9x, "%s/%s", tempDir, fwdName);
+ }
+
+ /* If we found a place to put the forwarding executable, try to
+ write it. */
+ if(win9x)
+ {
+ if(!kwsysEncodedWriteArrayProcessFwd9x(win9x))
+ {
+ /* Failed to create forwarding executable. Give up. */
+ free(win9x);
+ kwsysProcess_Delete(cp);
+ return 0;
+ }
+ }
+ else
+ {
+ /* Failed to find a place to put forwarding executable. */
+ kwsysProcess_Delete(cp);
+ return 0;
+ }
+ }
+
+ /* We need the extra error pipe on Win9x. */
+ cp->Win9x = win9x;
+ cp->PipeCount = cp->Win9x? 3:2;
+
+ /* Initially no thread owns the mutex. Initialize semaphore to 1. */
+ if(!(cp->SharedIndexMutex = CreateSemaphore(0, 1, 1, 0)))
+ {
+ kwsysProcess_Delete(cp);
+ return 0;
+ }
+
+ /* Initially no data are available. Initialize semaphore to 0. */
+ if(!(cp->Full = CreateSemaphore(0, 0, 1, 0)))
+ {
+ kwsysProcess_Delete(cp);
+ return 0;
+ }
+
+ if(cp->Win9x)
+ {
+ /* Create an event to tell the forwarding executable to kill the
+ child. */
+ SECURITY_ATTRIBUTES sa;
+ ZeroMemory(&sa, sizeof(sa));
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ if(!(cp->Win9xKillEvent = CreateEvent(&sa, TRUE, 0, 0)))
+ {
+ kwsysProcess_Delete(cp);
+ return 0;
+ }
+ }
+
+ /* Create the thread to read each pipe. */
+ for(i=0; i < cp->PipeCount; ++i)
+ {
+ DWORD dummy=0;
+
+ /* Assign the thread its index. */
+ cp->Pipe[i].Index = i;
+
+ /* Give the thread a pointer back to the kwsysProcess instance. */
+ cp->Pipe[i].Process = cp;
+
+ /* The pipe is not yet ready to read. Initialize semaphore to 0. */
+ if(!(cp->Pipe[i].Ready = CreateSemaphore(0, 0, 1, 0)))
+ {
+ kwsysProcess_Delete(cp);
+ return 0;
+ }
+
+ /* The pipe is not yet reset. Initialize semaphore to 0. */
+ if(!(cp->Pipe[i].Reset = CreateSemaphore(0, 0, 1, 0)))
+ {
+ kwsysProcess_Delete(cp);
+ return 0;
+ }
+
+ /* The thread's buffer is initially empty. Initialize semaphore to 1. */
+ if(!(cp->Pipe[i].Empty = CreateSemaphore(0, 1, 1, 0)))
+ {
+ kwsysProcess_Delete(cp);
+ return 0;
+ }
+
+ /* Create the thread. It will block immediately. */
+ if(!(cp->Pipe[i].Thread = CreateThread(0, 0, kwsysProcessPipeThread,
+ &cp->Pipe[i], 0, &dummy)))
+ {
+ kwsysProcess_Delete(cp);
+ return 0;
+ }
+ }
+
+ return cp;
+}
+
+/*--------------------------------------------------------------------------*/
+void kwsysProcess_Delete(kwsysProcess* cp)
+{
+ int i;
+
+ /* If the process is executing, wait for it to finish. */
+ if(cp->State == kwsysProcess_Executing)
+ {
+ kwsysProcess_WaitForExit(cp, 0);
+ }
+
+ /* We are deleting the kwsysProcess instance. */
+ cp->Deleting = 1;
+
+ /* Terminate each of the threads. */
+ for(i=0; i < cp->PipeCount; ++i)
+ {
+ if(cp->Pipe[i].Thread)
+ {
+ /* Signal the thread we are ready for it. It will terminate
+ immediately since Deleting is set. */
+ ReleaseSemaphore(cp->Pipe[i].Ready, 1, 0);
+
+ /* Wait for the thread to exit. */
+ WaitForSingleObject(cp->Pipe[i].Thread, INFINITE);
+
+ /* Close the handle to the thread. */
+ kwsysProcessCleanupHandle(&cp->Pipe[i].Thread);
+ }
+
+ /* Cleanup the pipe's semaphores. */
+ kwsysProcessCleanupHandle(&cp->Pipe[i].Ready);
+ kwsysProcessCleanupHandle(&cp->Pipe[i].Empty);
+ }
+
+ /* Close the shared semaphores. */
+ kwsysProcessCleanupHandle(&cp->SharedIndexMutex);
+ kwsysProcessCleanupHandle(&cp->Full);
+
+ /* Close the Win9x kill event handle. */
+ if(cp->Win9x)
+ {
+ kwsysProcessCleanupHandle(&cp->Win9xKillEvent);
+ }
+
+ /* Free memory. */
+ kwsysProcess_SetCommand(cp, 0);
+ if(cp->Win9x)
+ {
+ _unlink(cp->Win9x);
+ free(cp->Win9x);
+ }
+ free(cp);
+}
+
+/*--------------------------------------------------------------------------*/
+void kwsysProcess_SetCommand(kwsysProcess* cp, char const* const* command)
+{
+ if(cp->Command)
+ {
+ free(cp->Command);
+ cp->Command = 0;
+ }
+ if(command)
+ {
+ /* We need to construct a single string representing the command
+ and its arguments. We will surround each argument with
+ double-quotes so it can contain spaces. We need to escape
+ double-quotes and all backslashes before them. We also need to
+ escape backslashes at the end of an argument because they come
+ before the closing double-quote for the argument. */
+ char* cmd;
+ char const* const* arg;
+ int length = 0;
+ /* First determine the length of the final string. */
+ for(arg = command; *arg; ++arg)
+ {
+ /* Keep track of how many backslashes have been encountered in a
+ row in this argument. */
+ int backslashes = 0;
+ const char* c;
+
+ /* Add the length of the argument, plus 3 for the double quotes
+ and space separating the arguments. */
+ length += strlen(*arg) + 3;
+
+ /* Scan the string to find characters that need escaping. */
+ for(c=*arg; *c; ++c)
+ {
+ if(*c == '\\')
+ {
+ /* Found a backslash. It may need to be escaped later. */
+ ++backslashes;
+ }
+ else if(*c == '"')
+ {
+ /* Found a double-quote. We need to escape it and all
+ immediately preceding backslashes. */
+ length += backslashes + 1;
+ backslashes = 0;
+ }
+ else
+ {
+ /* Found another character. This eliminates the possibility
+ that any immediately preceding backslashes will be
+ escaped. */
+ backslashes = 0;
+ }
+ }
+
+ /* We need to escape all ending backslashes. */
+ length += backslashes;
+ }
+
+ /* Allocate enough space for the command. We do not need an extra
+ byte for the terminating null because we allocated a space for
+ the first argument that we will not use. */
+ cp->Command = (char*)malloc(length);
+
+ /* Construct the command line in the allocated buffer. */
+ cmd = cp->Command;
+ for(arg = command; *arg; ++arg)
+ {
+ /* Keep track of how many backslashes have been encountered in a
+ row in an argument. */
+ int backslashes = 0;
+ const char* c;
+
+ /* Add the separating space if this is not the first argument. */
+ if(arg != command)
+ {
+ *cmd++ = ' ';
+ }
+
+ /* Add the opening double-quote for this argument. */
+ *cmd++ = '"';
+
+ /* Add the characters of the argument, possibly escaping them. */
+ for(c=*arg; *c; ++c)
+ {
+ if(*c == '\\')
+ {
+ /* Found a backslash. It may need to be escaped later. */
+ ++backslashes;
+ *cmd++ = '\\';
+ }
+ else if(*c == '"')
+ {
+ /* Add enough backslashes to escape any that preceded the
+ double-quote. */
+ while(backslashes > 0)
+ {
+ --backslashes;
+ *cmd++ = '\\';
+ }
+
+ /* Add the backslash to escape the double-quote. */
+ *cmd++ = '\\';
+
+ /* Add the double-quote itself. */
+ *cmd++ = '"';
+ }
+ else
+ {
+ /* We encountered a normal character. This eliminates any
+ escaping needed for preceding backslashes. Add the
+ character. */
+ backslashes = 0;
+ *cmd++ = *c;
+ }
+ }
+
+ /* Add enough backslashes to escape any trailing ones. */
+ while(backslashes > 0)
+ {
+ --backslashes;
+ *cmd++ = '\\';
+ }
+
+ /* Add the opening double-quote for this argument. */
+ *cmd++ = '"';
+ }
+
+ /* Add the terminating null character to the command line. */
+ *cmd++ = 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+void kwsysProcess_SetTimeout(kwsysProcess* cp, double timeout)
+{
+ cp->Timeout = timeout;
+}
+
+/*--------------------------------------------------------------------------*/
+int kwsysProcess_GetState(kwsysProcess* cp)
+{
+ return cp->State;
+}
+
+/*--------------------------------------------------------------------------*/
+int kwsysProcess_GetExitCode(kwsysProcess* cp)
+{
+ return cp->ExitCode;
+}
+
+/*--------------------------------------------------------------------------*/
+const char* kwsysProcess_GetErrorString(kwsysProcess* cp)
+{
+ return cp->ErrorMessage;
+}
+
+/*--------------------------------------------------------------------------*/
+void kwsysProcess_Execute(kwsysProcess* cp)
+{
+ int i=0;
+
+ /* Windows child startup control data. */
+ STARTUPINFO si;
+
+ /* Do not execute a second time. */
+ if(cp->State == kwsysProcess_Executing)
+ {
+ return;
+ }
+
+ /* Initialize startup info data. */
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+
+ /* Reset internal status flags. */
+ cp->TimeoutExpired = 0;
+ cp->Terminated = 0;
+ cp->Killed = 0;
+
+ /* Reset error data. */
+ cp->ErrorMessage[0] = 0;
+ cp->ErrorMessageLength = 0;
+
+ /* Reset the Win9x kill event. */
+ if(cp->Win9x)
+ {
+ if(!ResetEvent(cp->Win9xKillEvent))
+ {
+ kwsysProcessCleanup(cp, 1);
+ return;
+ }
+ }
+
+ /* Create a pipe for each child output. */
+ for(i=0; i < cp->PipeCount; ++i)
+ {
+ HANDLE writeEnd;
+ DWORD dummy=0;
+
+ /* The pipe is not closed. */
+ cp->Pipe[i].Closed = 0;
+
+ /* Create the pipe. Neither end is directly inherited. */
+ if(!CreatePipe(&cp->Pipe[i].Read, &writeEnd, 0, 0))
+ {
+ kwsysProcessCleanup(cp, 1);
+ return;
+ }
+
+ /* Create an inherited duplicate of the write end. This also closes
+ the non-inherited version. */
+ if(!DuplicateHandle(GetCurrentProcess(), writeEnd,
+ GetCurrentProcess(), &cp->Pipe[i].Write,
+ 0, TRUE, (DUPLICATE_CLOSE_SOURCE |
+ DUPLICATE_SAME_ACCESS)))
+ {
+ kwsysProcessCleanup(cp, 1);
+ CloseHandle(writeEnd);
+ return;
+ }
+ }
+
+ /* Construct the real command line. */
+ if(cp->Win9x)
+ {
+ /* Windows 9x */
+
+ /* The forwarding executable is given a handle to the error pipe
+ and a handle to the kill event. */
+ cp->RealCommand = malloc(strlen(cp->Win9x)+strlen(cp->Command)+100);
+ sprintf(cp->RealCommand, "%s %d %d %s", cp->Win9x,
+ cp->Pipe[CMPE_PIPE_ERROR].Write,
+ cp->Win9xKillEvent, cp->Command);
+ }
+ else
+ {
+ /* Not Windows 9x */
+ cp->RealCommand = strdup(cp->Command);
+ }
+
+ /* Connect the child's output pipes to the threads. */
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdOutput = cp->Pipe[CMPE_PIPE_STDOUT].Write;
+ si.hStdError = cp->Pipe[CMPE_PIPE_STDERR].Write;
+
+ /* Hide the forwarding executable console on Windows 9x. */
+ si.dwFlags |= STARTF_USESHOWWINDOW;
+ if(cp->Win9x)
+ {
+ si.wShowWindow = SW_HIDE;
+ }
+ else
+ {
+ si.wShowWindow = SW_SHOWDEFAULT;
+ }
+
+ /* The timeout period starts now. */
+ cp->StartTime = kwsysProcessTimeGetCurrent();
+ cp->TimeoutTime = kwsysProcessTimeFromDouble(-1);
+
+ /* CREATE THE CHILD PROCESS */
+ if(!CreateProcess(0, cp->RealCommand, 0, 0, TRUE, CREATE_NEW_CONSOLE, 0,
+ 0, &si, &cp->ProcessInformation))
+ {
+ kwsysProcessCleanup(cp, 1);
+ return;
+ }
+
+ /* ---- It is no longer safe to call kwsysProcessCleanup. ----- */
+ /* Tell the pipe threads that a process has started. */
+ for(i=0; i < cp->PipeCount; ++i)
+ {
+ ReleaseSemaphore(cp->Pipe[i].Ready, 1, 0);
+ }
+
+ /* We don't care about the child's main thread. */
+ kwsysProcessCleanupHandle(&cp->ProcessInformation.hThread);
+
+ /* No pipe has reported data. */
+ cp->CurrentIndex = CMPE_PIPE_COUNT;
+ cp->PipesLeft = cp->PipeCount;
+
+ /* The process has now started. */
+ cp->State = kwsysProcess_Executing;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int kwsysProcess_WaitForData(kwsysProcess* cp, int pipes, char** data, int* length,
+ double* userTimeout)
+{
+ kwsysProcessTime userStartTime;
+ kwsysProcessTime timeoutLength;
+ kwsysProcessTime timeoutTime;
+ DWORD timeout;
+ int user;
+ int done = 0;
+ int expired = 0;
+ int pipeId = 0;
+ DWORD w;
+ HANDLE events[2];
+
+ /* Make sure we are executing a process. */
+ if(cp->State != kwsysProcess_Executing || cp->Killed || cp->TimeoutExpired)
+ {
+ return 0;
+ }
+
+ /* We will wait for data until the process termiantes or data are
+ available. */
+ events[0] = cp->Full;
+ events[1] = cp->ProcessInformation.hProcess;
+
+ /* Record the time at which user timeout period starts. */
+ if(userTimeout)
+ {
+ userStartTime = kwsysProcessTimeGetCurrent();
+ }
+
+ /* Calculate the time at which a timeout will expire, and whether it
+ is the user or process timeout. */
+ user = kwsysProcessGetTimeoutTime(cp, userTimeout, &timeoutTime);
+
+ /* Loop until we have a reason to return. */
+ while(!done && cp->PipesLeft > 0)
+ {
+ /* If we previously got data from a thread, let it know we are
+ done with the data. */
+ if(cp->CurrentIndex < CMPE_PIPE_COUNT)
+ {
+ ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Empty, 1, 0);
+ cp->CurrentIndex = CMPE_PIPE_COUNT;
+ }
+
+ /* Setup a timeout if required. */
+ if(kwsysProcessGetTimeoutLeft(cp, &timeoutTime, &timeoutLength))
+ {
+ /* Timeout has already expired. */
+ expired = 1;
+ done = 1;
+ break;
+ }
+ if(timeoutTime.QuadPart < 0)
+ {
+ timeout = INFINITE;
+ }
+ else
+ {
+ timeout = kwsysProcessTimeToDWORD(timeoutLength);
+ }
+
+ /* Wait for a pipe's thread to signal or the application to
+ terminate. */
+ w = WaitForMultipleObjects(cp->Terminated?1:2, events, 0, timeout);
+ if(w == WAIT_TIMEOUT)
+ {
+ /* Timeout has expired. */
+ expired = 1;
+ done = 1;
+ }
+ else if(w == WAIT_OBJECT_0)
+ {
+ /* Save the index of the reporting thread and release the mutex.
+ The thread will block until we signal its Empty mutex. */
+ cp->CurrentIndex = cp->SharedIndex;
+ ReleaseSemaphore(cp->SharedIndexMutex, 1, 0);
+
+ /* Data are available or a pipe closed. */
+ if(cp->Pipe[cp->CurrentIndex].Closed)
+ {
+ /* The pipe closed. */
+ --cp->PipesLeft;
+ }
+ else if(cp->CurrentIndex == CMPE_PIPE_ERROR)
+ {
+ /* This is data on the special error reporting pipe for Win9x.
+ Append it to the error buffer. */
+ int length = cp->Pipe[cp->CurrentIndex].DataLength;
+ if(length > CMPE_PIPE_BUFFER_SIZE - cp->ErrorMessageLength)
+ {
+ length = CMPE_PIPE_BUFFER_SIZE - cp->ErrorMessageLength;
+ }
+ if(length > 0)
+ {
+ memcpy(cp->ErrorMessage+cp->ErrorMessageLength,
+ cp->Pipe[cp->CurrentIndex].DataBuffer, length);
+ cp->ErrorMessageLength += length;
+ }
+ else
+ {
+ cp->ErrorMessage[cp->ErrorMessageLength] = 0;
+ }
+ }
+ else if(pipes & (1 << cp->CurrentIndex))
+ {
+ /* Caller wants this data. Report it. */
+ *data = cp->Pipe[cp->CurrentIndex].DataBuffer;
+ *length = cp->Pipe[cp->CurrentIndex].DataLength;
+ pipeId = (1 << cp->CurrentIndex);
+ done = 1;
+ }
+ else
+ {
+ /* Caller does not care about this pipe. Ignore the data. */
+ }
+ }
+ else
+ {
+ int i;
+
+ /* Process has terminated. */
+ cp->Terminated = 1;
+
+ /* Close our copies of the pipe write handles so the pipe
+ threads can detect end-of-data. */
+ for(i=0; i < cp->PipeCount; ++i)
+ {
+ kwsysProcessCleanupHandle(&cp->Pipe[i].Write);
+ }
+ }
+ }
+
+ /* Update the user timeout. */
+ if(userTimeout)
+ {
+ kwsysProcessTime userEndTime = kwsysProcessTimeGetCurrent();
+ kwsysProcessTime difference = kwsysProcessTimeSubtract(userEndTime,
+ userStartTime);
+ double d = kwsysProcessTimeToDouble(difference);
+ *userTimeout -= d;
+ if(*userTimeout < 0)
+ {
+ *userTimeout = 0;
+ }
+ }
+
+ /* Check what happened. */
+ if(pipeId)
+ {
+ /* Data are ready on a pipe. */
+ return pipeId;
+ }
+ else if(expired)
+ {
+ /* A timeout has expired. */
+ if(user)
+ {
+ /* The user timeout has expired. It has no time left. */
+ return kwsysProcess_Timeout;
+ }
+ else
+ {
+ /* The process timeout has expired. Kill the child now. */
+ kwsysProcess_Kill(cp);
+ cp->TimeoutExpired = 1;
+ cp->Killed = 0;
+ return 0;
+ }
+ }
+ else
+ {
+ /* The process has terminated and no more data are available. */
+ return 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout)
+{
+ int i;
+ int pipe = 0;
+ int pipesLeft = cp->PipeCount;
+
+ /* Buffer for child's return value. */
+ int childReturnValue = 0;
+
+ /* Make sure we are executing a process. */
+ if(cp->State != kwsysProcess_Executing)
+ {
+ return 1;
+ }
+
+ /* Wait for the process to terminate. Ignore all data. */
+ while((pipe = kwsysProcess_WaitForData(cp, 0, 0, 0, userTimeout)) > 0)
+ {
+ if(pipe == kwsysProcess_Timeout)
+ {
+ /* The user timeout has expired. */
+ return 0;
+ }
+ }
+
+ /* When the last pipe closes in WaitForData, the loop terminates
+ without releaseing the pipe's thread. Release it now. */
+ if(cp->CurrentIndex < CMPE_PIPE_COUNT)
+ {
+ ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Empty, 1, 0);
+ cp->CurrentIndex = CMPE_PIPE_COUNT;
+ }
+
+ /* Wait for all pipe threads to reset. */
+ for(i=0; i < cp->PipeCount; ++i)
+ {
+ WaitForSingleObject(cp->Pipe[i].Reset, INFINITE);
+ }
+
+ /* ---- It is now safe again to call kwsysProcessCleanup. ----- */
+ /* Close all the pipes. */
+ kwsysProcessCleanup(cp, 0);
+
+ /* We are done reading all data. Wait for the child to terminate.
+ This will only block if we killed the child and are waiting for
+ it to cleanup. */
+ WaitForSingleObject(cp->ProcessInformation.hProcess, INFINITE);
+
+ /* Determine the outcome. */
+ if(cp->Killed)
+ {
+ /* We killed the child. */
+ cp->State = kwsysProcess_Killed;
+ }
+ else if(cp->ErrorMessageLength)
+ {
+ /* Failed to run the process. */
+ cp->State = kwsysProcess_Error;
+ }
+ else if(cp->TimeoutExpired)
+ {
+ /* The timeout expired. */
+ cp->State = kwsysProcess_Expired;
+ }
+ else if(GetExitCodeProcess(cp->ProcessInformation.hProcess,
+ &childReturnValue))
+ {
+ /* The child exited. */
+ cp->State = kwsysProcess_Exited;
+ cp->ExitCode = childReturnValue;
+ }
+ else
+ {
+ /* Error getting the child return code. */
+ strcpy(cp->ErrorMessage, "Error getting child return code.");
+ cp->State = kwsysProcess_Error;
+ }
+
+ /* The child process is terminated. */
+ CloseHandle(cp->ProcessInformation.hProcess);
+
+ return 1;
+}
+
+/*--------------------------------------------------------------------------*/
+void kwsysProcess_Kill(kwsysProcess* cp)
+{
+ int i;
+
+ /* Make sure we are executing a process. */
+ if(cp->State != kwsysProcess_Executing || cp->TimeoutExpired || cp->Killed ||
+ cp->Terminated)
+ {
+ return;
+ }
+
+ /* If we are killing a process that just reported data, release
+ the pipe's thread. */
+ if(cp->CurrentIndex < CMPE_PIPE_COUNT)
+ {
+ ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Empty, 1, 0);
+ cp->CurrentIndex = CMPE_PIPE_COUNT;
+ }
+
+ /* Wake up all the pipe threads with dummy data. */
+ for(i=0; i < cp->PipeCount; ++i)
+ {
+ DWORD dummy;
+ WriteFile(cp->Pipe[i].Write, "", 1, &dummy, 0);
+ }
+
+ /* Tell pipe threads to reset until we run another process. */
+ while(cp->PipesLeft > 0)
+ {
+ WaitForSingleObject(cp->Full, INFINITE);
+ cp->CurrentIndex = cp->SharedIndex;
+ ReleaseSemaphore(cp->SharedIndexMutex, 1, 0);
+ cp->Pipe[cp->CurrentIndex].Closed = 1;
+ ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Empty, 1, 0);
+ --cp->PipesLeft;
+ }
+
+ /* Kill the child. */
+ cp->Killed = 1;
+ if(cp->Win9x)
+ {
+ /* Windows 9x. Tell the forwarding executable to kill the child. */
+ SetEvent(cp->Win9xKillEvent);
+ }
+ else
+ {
+ /* Not Windows 9x. Just terminate the child. */
+ TerminateProcess(cp->ProcessInformation.hProcess, -1);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ Function executed for each pipe's thread. Argument is a pointer to
+ the kwsysProcessPipeData instance for this thread.
+*/
+DWORD WINAPI kwsysProcessPipeThread(LPVOID ptd)
+{
+ kwsysProcessPipeData* td = (kwsysProcessPipeData*)ptd;
+ kwsysProcess* cp = td->Process;
+
+ /* Wait for a process to be ready. */
+ while((WaitForSingleObject(td->Ready, INFINITE), !cp->Deleting))
+ {
+ /* Read output from the process for this thread's pipe. */
+ kwsysProcessPipeThreadReadPipe(cp, td);
+
+ /* We were signalled to exit with our buffer empty. Reset the
+ mutex for a new process. */
+ ReleaseSemaphore(td->Empty, 1, 0);
+
+ /* Signal the main thread we have reset for a new process. */
+ ReleaseSemaphore(td->Reset, 1, 0);
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ Function called in each pipe's thread to handle data for one
+ execution of a subprocess.
+*/
+void kwsysProcessPipeThreadReadPipe(kwsysProcess* cp, kwsysProcessPipeData* td)
+{
+ /* Wait for space in the thread's buffer. */
+ while((WaitForSingleObject(td->Empty, INFINITE), !td->Closed))
+ {
+ /* Read data from the pipe. This may block until data are available. */
+ if(!ReadFile(td->Read, td->DataBuffer, CMPE_PIPE_BUFFER_SIZE,
+ &td->DataLength, 0))
+ {
+ if(GetLastError() != ERROR_BROKEN_PIPE)
+ {
+ /* UNEXPECTED failure to read the pipe. */
+ }
+
+ /* The pipe closed. There are no more data to read. */
+ td->Closed = 1;
+ }
+
+ /* Wait for our turn to be handled by the main thread. */
+ WaitForSingleObject(cp->SharedIndexMutex, INFINITE);
+
+ /* Tell the main thread we have something to report. */
+ cp->SharedIndex = td->Index;
+ ReleaseSemaphore(cp->Full, 1, 0);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+/* Close the given handle if it is open. Reset its value to 0. */
+void kwsysProcessCleanupHandle(PHANDLE h)
+{
+ if(h && *h)
+ {
+ CloseHandle(*h);
+ *h = 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+/* Close all handles created by kwsysProcess_Execute. */
+void kwsysProcessCleanup(kwsysProcess* cp, int error)
+{
+ int i;
+
+ /* If this is an error case, report the error. */
+ if(error)
+ {
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ cp->ErrorMessage, CMPE_PIPE_BUFFER_SIZE, 0);
+ cp->State = kwsysProcess_Error;
+ }
+
+ /* Free memory. */
+ if(cp->RealCommand)
+ {
+ free(cp->RealCommand);
+ cp->RealCommand = 0;
+ }
+
+ /* Close each pipe. */
+ for(i=0; i < cp->PipeCount; ++i)
+ {
+ kwsysProcessCleanupHandle(&cp->Pipe[i].Write);
+ kwsysProcessCleanupHandle(&cp->Pipe[i].Read);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+/* Get the time at which either the process or user timeout will
+ expire. Returns 1 if the user timeout is first, and 0 otherwise. */
+int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
+ kwsysProcessTime* timeoutTime)
+{
+ /* The first time this is called, we need to calculate the time at
+ which the child will timeout. */
+ if(cp->Timeout && cp->TimeoutTime.QuadPart < 0)
+ {
+ kwsysProcessTime length = kwsysProcessTimeFromDouble(cp->Timeout);
+ cp->TimeoutTime = kwsysProcessTimeAdd(cp->StartTime, length);
+ }
+
+ /* Start with process timeout. */
+ *timeoutTime = cp->TimeoutTime;
+
+ /* Check if the user timeout is earlier. */
+ if(userTimeout)
+ {
+ kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent();
+ kwsysProcessTime userTimeoutLength = kwsysProcessTimeFromDouble(*userTimeout);
+ kwsysProcessTime userTimeoutTime = kwsysProcessTimeAdd(currentTime,
+ userTimeoutLength);
+ if(kwsysProcessTimeLess(userTimeoutTime, *timeoutTime))
+ {
+ *timeoutTime = userTimeoutTime;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+/* Get the length of time before the given timeout time arrives.
+ Returns 1 if the time has already arrived, and 0 otherwise. */
+int kwsysProcessGetTimeoutLeft(kwsysProcess* cp, kwsysProcessTime* timeoutTime,
+ kwsysProcessTime* timeoutLength)
+{
+ if(timeoutTime->QuadPart < 0)
+ {
+ /* No timeout time has been requested. */
+ return 0;
+ }
+ else
+ {
+ /* Calculate the remaining time. */
+ kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent();
+ *timeoutLength = kwsysProcessTimeSubtract(*timeoutTime, currentTime);
+ if(timeoutLength->QuadPart < 0)
+ {
+ /* Timeout has already expired. */
+ return 1;
+ }
+ else
+ {
+ /* There is some time left. */
+ return 0;
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+kwsysProcessTime kwsysProcessTimeGetCurrent()
+{
+ kwsysProcessTime current;
+ FILETIME ft;
+ GetSystemTimeAsFileTime(&ft);
+ current.LowPart = ft.dwLowDateTime;
+ current.HighPart = ft.dwHighDateTime;
+ return current;
+}
+
+/*--------------------------------------------------------------------------*/
+DWORD kwsysProcessTimeToDWORD(kwsysProcessTime t)
+{
+ return (DWORD)(t.QuadPart * 0.0001);
+}
+
+/*--------------------------------------------------------------------------*/
+double kwsysProcessTimeToDouble(kwsysProcessTime t)
+{
+ return t.QuadPart * 0.0000001;
+}
+
+/*--------------------------------------------------------------------------*/
+kwsysProcessTime kwsysProcessTimeFromDouble(double d)
+{
+ kwsysProcessTime t;
+ t.QuadPart = (LONGLONG)(d*10000000);
+ return t;
+}
+
+/*--------------------------------------------------------------------------*/
+int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2)
+{
+ return in1.QuadPart < in2.QuadPart;
+}
+
+/*--------------------------------------------------------------------------*/
+kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, kwsysProcessTime in2)
+{
+ kwsysProcessTime out;
+ out.QuadPart = in1.QuadPart + in2.QuadPart;
+ return out;
+}
+
+/*--------------------------------------------------------------------------*/
+kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProcessTime in2)
+{
+ kwsysProcessTime out;
+ out.QuadPart = in1.QuadPart - in2.QuadPart;
+ return out;
+}