This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "CMake".

The branch, next has been updated
       via  3394db5b8debc195eae4275ae1e9c8e74611d422 (commit)
       via  da98b89612d59906bd938d58a9072b3ce68864de (commit)
       via  129640f279259e51fbb0e81a5bec2ec2dfac543e (commit)
       via  d0915bc86f294707411ae525e70fa20965f1aeec (commit)
       via  1feafc643b1c50fd0fa8171a4170065ca39d4d4c (commit)
      from  7899f6955a3e4782db656c46390087f4c8a8e205 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=3394db5b8debc195eae4275ae1e9c8e74611d422
commit 3394db5b8debc195eae4275ae1e9c8e74611d422
Merge: 7899f69 da98b89
Author:     Brad King <brad.k...@kitware.com>
AuthorDate: Fri Jul 31 09:29:18 2015 -0400
Commit:     CMake Topic Stage <kwro...@kitware.com>
CommitDate: Fri Jul 31 09:29:18 2015 -0400

    Merge topic 'update-kwsys' into next
    
    da98b896 Tests: In CTestTest2 skip the kwsys.testProcess-10 test that leaks
    129640f2 CTestCustom: Ignore kwsys.testProcess-10 for MemCheck as KWSys does
    d0915bc8 Merge branch 'upstream-kwsys' into update-kwsys
    1feafc64 KWSys 2015-07-30 (f63febb7)


http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=da98b89612d59906bd938d58a9072b3ce68864de
commit da98b89612d59906bd938d58a9072b3ce68864de
Author:     Brad King <brad.k...@kitware.com>
AuthorDate: Thu Jul 23 09:47:03 2015 -0400
Commit:     Brad King <brad.k...@kitware.com>
CommitDate: Fri Jul 31 09:27:08 2015 -0400

    Tests: In CTestTest2 skip the kwsys.testProcess-10 test that leaks
    
    This test intentionally leaks memory so KWSys excludes it from MemCheck.
    However, when CTestTest2 runs under our own MemCheck then valgrind may
    recursively check tests run by ctest_test() calls in our test.cmake
    script.  Teach these calls to exluce testProcess-10 too.  Also read
    the KWSys CTestCustom.cmake file so ctest_memcheck() will ignore the
    test too.

diff --git a/Tests/CTestTest2/test.cmake.in b/Tests/CTestTest2/test.cmake.in
index 852bb6b..825b957 100644
--- a/Tests/CTestTest2/test.cmake.in
+++ b/Tests/CTestTest2/test.cmake.in
@@ -39,14 +39,19 @@ CMAKE_CXX_COMPILER_ARG1:STRING=@CMAKE_CXX_COMPILER_ARG1@
 CTEST_TEST_KWSYS:BOOL=ON
 ")
 
+set(test_exclude
+  kwsys.testProcess-10
+  )
+
 CTEST_START(Experimental)
 #CTEST_UPDATE(SOURCE "${CTEST_SOURCE_DIRECTORY}" RETURN_VALUE res)
 CTEST_CONFIGURE(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)
+CTEST_READ_CUSTOM_FILES(${CTEST_BINARY_DIRECTORY})
 CTEST_BUILD(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)
-CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res START 1 END 5 
STRIDE 2)
-CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res START 7 STRIDE 2 
SUBMIT_INDEX 1)
-CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res START 2 END 4 
STRIDE 2 SUBMIT_INDEX 2)
-CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res START 6 STRIDE 2 
SUBMIT_INDEX 3)
+CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res EXCLUDE 
${test_exclude} START 1 END 5 STRIDE 2)
+CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res EXCLUDE 
${test_exclude} START 7 STRIDE 2 SUBMIT_INDEX 1)
+CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res EXCLUDE 
${test_exclude} START 2 END 4 STRIDE 2 SUBMIT_INDEX 2)
+CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res EXCLUDE 
${test_exclude} START 6 STRIDE 2 SUBMIT_INDEX 3)
 CTEST_MEMCHECK(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res STRIDE 1.5)
 CTEST_COVERAGE(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)
 

http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=129640f279259e51fbb0e81a5bec2ec2dfac543e
commit 129640f279259e51fbb0e81a5bec2ec2dfac543e
Author:     Brad King <brad.k...@kitware.com>
AuthorDate: Thu Jul 23 09:22:25 2015 -0400
Commit:     Brad King <brad.k...@kitware.com>
CommitDate: Fri Jul 31 09:27:01 2015 -0400

    CTestCustom: Ignore kwsys.testProcess-10 for MemCheck as KWSys does
    
    The test covers exceptional behavior that leaks memory, so ignore it for
    dynamic analysis runs.

diff --git a/CTestCustom.cmake.in b/CTestCustom.cmake.in
index 7f20d10..f29ac70 100644
--- a/CTestCustom.cmake.in
+++ b/CTestCustom.cmake.in
@@ -109,3 +109,7 @@ set(CTEST_CUSTOM_COVERAGE_EXCLUDE
   # Exclude Qt source files from coverage results:
   "[A-Za-z]./[Qq]t/qt-.+-opensource-src"
   )
+
+list(APPEND CTEST_CUSTOM_MEMCHECK_IGNORE
+  kwsys.testProcess-10 # See Source/kwsys/CTestCustom.cmake.in
+  )

http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=d0915bc86f294707411ae525e70fa20965f1aeec
commit d0915bc86f294707411ae525e70fa20965f1aeec
Merge: 5967803 1feafc6
Author:     Brad King <brad.k...@kitware.com>
AuthorDate: Fri Jul 31 09:26:35 2015 -0400
Commit:     Brad King <brad.k...@kitware.com>
CommitDate: Fri Jul 31 09:26:35 2015 -0400

    Merge branch 'upstream-kwsys' into update-kwsys

diff --cc Source/kwsys/CTestCustom.cmake.in
index 0000000,d6f802e..d6f802e
mode 000000,100644..100644
--- a/Source/kwsys/CTestCustom.cmake.in
+++ b/Source/kwsys/CTestCustom.cmake.in

http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=1feafc643b1c50fd0fa8171a4170065ca39d4d4c
commit 1feafc643b1c50fd0fa8171a4170065ca39d4d4c
Author:     KWSys Robot <kwro...@kitware.com>
AuthorDate: Thu Jul 30 09:08:41 2015 -0400
Commit:     Brad King <brad.k...@kitware.com>
CommitDate: Fri Jul 31 09:26:30 2015 -0400

    KWSys 2015-07-30 (f63febb7)
    
    Extract upstream KWSys using the following shell commands.
    
    $ git archive --prefix=upstream-kwsys/ f63febb7 | tar x
    $ git shortlog --no-merges --abbrev=8 --format='%h %s' c9336bcf..f63febb7
    Brad King (1):
          83b4a6b8 Process: Fix conversion warning in testProcess.c
    
    James Johnston (7):
          4cd8846c Process: Remove trailing whitespace in ProcessUNIX.c
          b1c44c58 Process: Refactor sleeping code in testProcess.c.
          faff2ab0 Process: Wait for children to terminate on Ctrl+C.
          ef517b19 Process: Added initial support for process groups.
          906c2cae Process: Added test cases for testing Ctrl+C and process 
groups.
          52874e6a Process: Fix leaked file descriptor in ProcessUNIX
          f63febb7 Process: Fix error message for startup failure on Windows

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7eb678b..017d619 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1237,7 +1237,7 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
     IF(NOT CYGWIN)
       SET(KWSYS_TEST_PROCESS_7 7)
     ENDIF()
-    FOREACH(n 1 2 3 4 5 6 ${KWSYS_TEST_PROCESS_7})
+    FOREACH(n 1 2 3 4 5 6 ${KWSYS_TEST_PROCESS_7} 9 10)
       ADD_TEST(kwsys.testProcess-${n} 
${EXEC_DIR}/${KWSYS_NAMESPACE}TestProcess ${n})
       SET_PROPERTY(TEST kwsys.testProcess-${n} PROPERTY LABELS 
${KWSYS_LABELS_TEST})
       SET_TESTS_PROPERTIES(kwsys.testProcess-${n} PROPERTIES TIMEOUT 120)
@@ -1270,6 +1270,10 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
       MESSAGE(STATUS "GET_TEST_PROPERTY returned: ${wfv}")
     ENDIF()
 
+    # Set up ctest custom configuration file.
+    CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/CTestCustom.cmake.in
+                   ${PROJECT_BINARY_DIR}/CTestCustom.cmake @ONLY)
+
     # Suppress known consistent failures on buggy systems.
     IF(KWSYS_TEST_BOGUS_FAILURES)
       SET_TESTS_PROPERTIES(${KWSYS_TEST_BOGUS_FAILURES} PROPERTIES WILL_FAIL 
ON)
diff --git a/CTestCustom.cmake.in b/CTestCustom.cmake.in
new file mode 100644
index 0000000..d6f802e
--- /dev/null
+++ b/CTestCustom.cmake.in
@@ -0,0 +1,15 @@
+# kwsys.testProcess-10 involves sending SIGINT to a child process, which then
+# exits abnormally via a call to _exit(). (On Windows, a call to ExitProcess).
+# Naturally, this results in plenty of memory being "leaked" by this child
+# process - the memory check results are not meaningful in this case.
+#
+# kwsys.testProcess-9 also tests sending SIGINT to a child process.  However,
+# normal operation of that test involves the child process timing out, and the
+# host process kills (SIGKILL) it as a result.  Since it was SIGKILL'ed, the
+# resulting memory leaks are not logged by valgrind anyway.  Therefore, we
+# don't have to exclude it.
+
+set(CTEST_CUSTOM_MEMCHECK_IGNORE
+  ${CTEST_CUSTOM_MEMCHECK_IGNORE}
+  kwsys.testProcess-10
+  )
diff --git a/Process.h.in b/Process.h.in
index e35939f..c5ebc97 100644
--- a/Process.h.in
+++ b/Process.h.in
@@ -23,58 +23,60 @@
 # define kwsysEXPORT @KWSYS_NAMESPACE@_EXPORT
 #endif
 #if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS
-# define kwsysProcess                     kwsys_ns(Process)
-# define kwsysProcess_s                   kwsys_ns(Process_s)
-# define kwsysProcess_New                 kwsys_ns(Process_New)
-# define kwsysProcess_Delete              kwsys_ns(Process_Delete)
-# define kwsysProcess_SetCommand          kwsys_ns(Process_SetCommand)
-# define kwsysProcess_AddCommand          kwsys_ns(Process_AddCommand)
-# define kwsysProcess_SetTimeout          kwsys_ns(Process_SetTimeout)
-# define kwsysProcess_SetWorkingDirectory kwsys_ns(Process_SetWorkingDirectory)
-# define kwsysProcess_SetPipeFile         kwsys_ns(Process_SetPipeFile)
-# define kwsysProcess_SetPipeNative       kwsys_ns(Process_SetPipeNative)
-# define kwsysProcess_SetPipeShared       kwsys_ns(Process_SetPipeShared)
-# define kwsysProcess_Option_Detach       kwsys_ns(Process_Option_Detach)
-# define kwsysProcess_Option_HideWindow   kwsys_ns(Process_Option_HideWindow)
-# define kwsysProcess_Option_MergeOutput  kwsys_ns(Process_Option_MergeOutput)
-# define kwsysProcess_Option_Verbatim     kwsys_ns(Process_Option_Verbatim)
-# define kwsysProcess_GetOption           kwsys_ns(Process_GetOption)
-# define kwsysProcess_SetOption           kwsys_ns(Process_SetOption)
-# define kwsysProcess_Option_e            kwsys_ns(Process_Option_e)
-# define kwsysProcess_State_Starting      kwsys_ns(Process_State_Starting)
-# define kwsysProcess_State_Error         kwsys_ns(Process_State_Error)
-# define kwsysProcess_State_Exception     kwsys_ns(Process_State_Exception)
-# define kwsysProcess_State_Executing     kwsys_ns(Process_State_Executing)
-# define kwsysProcess_State_Exited        kwsys_ns(Process_State_Exited)
-# define kwsysProcess_State_Expired       kwsys_ns(Process_State_Expired)
-# define kwsysProcess_State_Killed        kwsys_ns(Process_State_Killed)
-# define kwsysProcess_State_Disowned      kwsys_ns(Process_State_Disowned)
-# define kwsysProcess_GetState            kwsys_ns(Process_GetState)
-# define kwsysProcess_State_e             kwsys_ns(Process_State_e)
-# define kwsysProcess_Exception_None      kwsys_ns(Process_Exception_None)
-# define kwsysProcess_Exception_Fault     kwsys_ns(Process_Exception_Fault)
-# define kwsysProcess_Exception_Illegal   kwsys_ns(Process_Exception_Illegal)
-# define kwsysProcess_Exception_Interrupt kwsys_ns(Process_Exception_Interrupt)
-# define kwsysProcess_Exception_Numerical kwsys_ns(Process_Exception_Numerical)
-# define kwsysProcess_Exception_Other     kwsys_ns(Process_Exception_Other)
-# define kwsysProcess_GetExitException    kwsys_ns(Process_GetExitException)
-# define kwsysProcess_Exception_e         kwsys_ns(Process_Exception_e)
-# define kwsysProcess_GetExitCode         kwsys_ns(Process_GetExitCode)
-# define kwsysProcess_GetExitValue        kwsys_ns(Process_GetExitValue)
-# define kwsysProcess_GetErrorString      kwsys_ns(Process_GetErrorString)
-# define kwsysProcess_GetExceptionString  kwsys_ns(Process_GetExceptionString)
-# define kwsysProcess_Execute             kwsys_ns(Process_Execute)
-# define kwsysProcess_Disown              kwsys_ns(Process_Disown)
-# define kwsysProcess_WaitForData         kwsys_ns(Process_WaitForData)
-# define kwsysProcess_Pipes_e             kwsys_ns(Process_Pipes_e)
-# define kwsysProcess_Pipe_None           kwsys_ns(Process_Pipe_None)
-# define kwsysProcess_Pipe_STDIN          kwsys_ns(Process_Pipe_STDIN)
-# define kwsysProcess_Pipe_STDOUT         kwsys_ns(Process_Pipe_STDOUT)
-# define kwsysProcess_Pipe_STDERR         kwsys_ns(Process_Pipe_STDERR)
-# define kwsysProcess_Pipe_Timeout        kwsys_ns(Process_Pipe_Timeout)
-# define kwsysProcess_Pipe_Handle         kwsys_ns(Process_Pipe_Handle)
-# define kwsysProcess_WaitForExit         kwsys_ns(Process_WaitForExit)
-# define kwsysProcess_Kill                kwsys_ns(Process_Kill)
+# define kwsysProcess                           kwsys_ns(Process)
+# define kwsysProcess_s                         kwsys_ns(Process_s)
+# define kwsysProcess_New                       kwsys_ns(Process_New)
+# define kwsysProcess_Delete                    kwsys_ns(Process_Delete)
+# define kwsysProcess_SetCommand                kwsys_ns(Process_SetCommand)
+# define kwsysProcess_AddCommand                kwsys_ns(Process_AddCommand)
+# define kwsysProcess_SetTimeout                kwsys_ns(Process_SetTimeout)
+# define kwsysProcess_SetWorkingDirectory       
kwsys_ns(Process_SetWorkingDirectory)
+# define kwsysProcess_SetPipeFile               kwsys_ns(Process_SetPipeFile)
+# define kwsysProcess_SetPipeNative             kwsys_ns(Process_SetPipeNative)
+# define kwsysProcess_SetPipeShared             kwsys_ns(Process_SetPipeShared)
+# define kwsysProcess_Option_Detach             kwsys_ns(Process_Option_Detach)
+# define kwsysProcess_Option_HideWindow         
kwsys_ns(Process_Option_HideWindow)
+# define kwsysProcess_Option_MergeOutput        
kwsys_ns(Process_Option_MergeOutput)
+# define kwsysProcess_Option_Verbatim           
kwsys_ns(Process_Option_Verbatim)
+# define kwsysProcess_Option_CreateProcessGroup 
kwsys_ns(Process_Option_CreateProcessGroup)
+# define kwsysProcess_GetOption                 kwsys_ns(Process_GetOption)
+# define kwsysProcess_SetOption                 kwsys_ns(Process_SetOption)
+# define kwsysProcess_Option_e                  kwsys_ns(Process_Option_e)
+# define kwsysProcess_State_Starting            
kwsys_ns(Process_State_Starting)
+# define kwsysProcess_State_Error               kwsys_ns(Process_State_Error)
+# define kwsysProcess_State_Exception           
kwsys_ns(Process_State_Exception)
+# define kwsysProcess_State_Executing           
kwsys_ns(Process_State_Executing)
+# define kwsysProcess_State_Exited              kwsys_ns(Process_State_Exited)
+# define kwsysProcess_State_Expired             kwsys_ns(Process_State_Expired)
+# define kwsysProcess_State_Killed              kwsys_ns(Process_State_Killed)
+# define kwsysProcess_State_Disowned            
kwsys_ns(Process_State_Disowned)
+# define kwsysProcess_GetState                  kwsys_ns(Process_GetState)
+# define kwsysProcess_State_e                   kwsys_ns(Process_State_e)
+# define kwsysProcess_Exception_None            
kwsys_ns(Process_Exception_None)
+# define kwsysProcess_Exception_Fault           
kwsys_ns(Process_Exception_Fault)
+# define kwsysProcess_Exception_Illegal         
kwsys_ns(Process_Exception_Illegal)
+# define kwsysProcess_Exception_Interrupt       
kwsys_ns(Process_Exception_Interrupt)
+# define kwsysProcess_Exception_Numerical       
kwsys_ns(Process_Exception_Numerical)
+# define kwsysProcess_Exception_Other           
kwsys_ns(Process_Exception_Other)
+# define kwsysProcess_GetExitException          
kwsys_ns(Process_GetExitException)
+# define kwsysProcess_Exception_e               kwsys_ns(Process_Exception_e)
+# define kwsysProcess_GetExitCode               kwsys_ns(Process_GetExitCode)
+# define kwsysProcess_GetExitValue              kwsys_ns(Process_GetExitValue)
+# define kwsysProcess_GetErrorString            
kwsys_ns(Process_GetErrorString)
+# define kwsysProcess_GetExceptionString        
kwsys_ns(Process_GetExceptionString)
+# define kwsysProcess_Execute                   kwsys_ns(Process_Execute)
+# define kwsysProcess_Disown                    kwsys_ns(Process_Disown)
+# define kwsysProcess_WaitForData               kwsys_ns(Process_WaitForData)
+# define kwsysProcess_Pipes_e                   kwsys_ns(Process_Pipes_e)
+# define kwsysProcess_Pipe_None                 kwsys_ns(Process_Pipe_None)
+# define kwsysProcess_Pipe_STDIN                kwsys_ns(Process_Pipe_STDIN)
+# define kwsysProcess_Pipe_STDOUT               kwsys_ns(Process_Pipe_STDOUT)
+# define kwsysProcess_Pipe_STDERR               kwsys_ns(Process_Pipe_STDERR)
+# define kwsysProcess_Pipe_Timeout              kwsys_ns(Process_Pipe_Timeout)
+# define kwsysProcess_Pipe_Handle               kwsys_ns(Process_Pipe_Handle)
+# define kwsysProcess_WaitForExit               kwsys_ns(Process_WaitForExit)
+# define kwsysProcess_Interrupt                 kwsys_ns(Process_Interrupt)
+# define kwsysProcess_Kill                      kwsys_ns(Process_Kill)
 #endif
 
 #if defined(__cplusplus)
@@ -199,6 +201,15 @@ kwsysEXPORT void kwsysProcess_SetPipeNative(kwsysProcess* 
cp, int pipe,
  *                                 and ignore the rest of the arguments.
  *         0 = No (default)
  *         1 = Yes
+ *
+ *  kwsysProcess_Option_CreateProcessGroup = Whether to place the process in a
+ *                                           new process group.  This is
+ *                                           useful if you want to send Ctrl+C
+ *                                           to the process.  On UNIX, also
+ *                                           places the process in a new
+ *                                           session.
+ *         0 = No (default)
+ *         1 = Yes
  */
 kwsysEXPORT int kwsysProcess_GetOption(kwsysProcess* cp, int optionId);
 kwsysEXPORT void kwsysProcess_SetOption(kwsysProcess* cp, int optionId,
@@ -208,7 +219,8 @@ enum kwsysProcess_Option_e
   kwsysProcess_Option_HideWindow,
   kwsysProcess_Option_Detach,
   kwsysProcess_Option_MergeOutput,
-  kwsysProcess_Option_Verbatim
+  kwsysProcess_Option_Verbatim,
+  kwsysProcess_Option_CreateProcessGroup
 };
 
 /**
@@ -363,6 +375,17 @@ enum kwsysProcess_Pipes_e
 kwsysEXPORT int kwsysProcess_WaitForExit(kwsysProcess* cp, double* timeout);
 
 /**
+ * Interrupt the process group for the child process that is currently
+ * running by sending it the appropriate operating-system specific signal.
+ * The caller should call WaitForExit after this returns to wait for the
+ * child to terminate.
+ *
+ * WARNING:  If you didn't specify kwsysProcess_Option_CreateProcessGroup,
+ * you will interrupt your own process group.
+ */
+kwsysEXPORT void kwsysProcess_Interrupt(kwsysProcess* cp);
+
+/**
  * Forcefully terminate the child process that is currently running.
  * The caller should call WaitForExit after this returns to wait for
  * the child to terminate.
@@ -394,6 +417,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
 #  undef kwsysProcess_Option_HideWindow
 #  undef kwsysProcess_Option_MergeOutput
 #  undef kwsysProcess_Option_Verbatim
+#  undef kwsysProcess_Option_CreateProcessGroup
 #  undef kwsysProcess_GetOption
 #  undef kwsysProcess_SetOption
 #  undef kwsysProcess_Option_e
@@ -430,6 +454,7 @@ kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp);
 #  undef kwsysProcess_Pipe_Timeout
 #  undef kwsysProcess_Pipe_Handle
 #  undef kwsysProcess_WaitForExit
+#  undef kwsysProcess_Interrupt
 #  undef kwsysProcess_Kill
 # endif
 #endif
diff --git a/ProcessUNIX.c b/ProcessUNIX.c
index 0393a6d..6d9b109 100644
--- a/ProcessUNIX.c
+++ b/ProcessUNIX.c
@@ -88,7 +88,7 @@ typedef ssize_t kwsysProcess_ssize_t;
 typedef int kwsysProcess_ssize_t;
 #endif
 
-#if defined(__BEOS__) && !defined(__ZETA__) 
+#if defined(__BEOS__) && !defined(__ZETA__)
 /* BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. */
 # include <be/kernel/OS.h>
 static inline void kwsysProcess_usleep(unsigned int msec)
@@ -151,6 +151,7 @@ typedef struct kwsysProcessCreateInformation_s
 } kwsysProcessCreateInformation;
 
 /*--------------------------------------------------------------------------*/
+static void kwsysProcessVolatileFree(volatile void* p);
 static int kwsysProcessInitialize(kwsysProcess* cp);
 static void kwsysProcessCleanup(kwsysProcess* cp, int error);
 static void kwsysProcessCleanupDescriptor(int* pfd);
@@ -197,7 +198,7 @@ struct kwsysProcess_s
 {
   /* The command lines to execute.  */
   char*** Commands;
-  int NumberOfCommands;
+  volatile int NumberOfCommands;
 
   /* Descriptors for the read ends of the child's output pipes and
      the signal pipe. */
@@ -213,8 +214,10 @@ struct kwsysProcess_s
   /* Buffer for pipe data.  */
   char PipeBuffer[KWSYSPE_PIPE_BUFFER_SIZE];
 
-  /* Process IDs returned by the calls to fork.  */
-  pid_t* ForkPIDs;
+  /* Process IDs returned by the calls to fork.  Everything is volatile
+     because the signal handler accesses them.  You must be very careful
+     when reaping PIDs or modifying this array to avoid race conditions.  */
+  volatile pid_t* volatile ForkPIDs;
 
   /* Flag for whether the children were terminated by a faild select.  */
   int SelectError;
@@ -237,6 +240,9 @@ struct kwsysProcess_s
   /* Whether to merge stdout/stderr of the child.  */
   int MergeOutput;
 
+  /* Whether to create the process in a new process group.  */
+  volatile sig_atomic_t CreateProcessGroup;
+
   /* Time at which the child started.  Negative for no timeout.  */
   kwsysProcessTime StartTime;
 
@@ -257,8 +263,9 @@ struct kwsysProcess_s
   /* The number of children still executing.  */
   int CommandsLeft;
 
-  /* The current status of the child process. */
-  int State;
+  /* The current status of the child process.  Must be atomic because
+     the signal handler checks this to avoid a race.  */
+  volatile sig_atomic_t State;
 
   /* The exceptional behavior that terminated the child process, if
    * any.  */
@@ -271,7 +278,7 @@ struct kwsysProcess_s
   int ExitValue;
 
   /* Whether the process was killed.  */
-  int Killed;
+  volatile sig_atomic_t Killed;
 
   /* Buffer for error message in case of failure.  */
   char ErrorMessage[KWSYSPE_PIPE_BUFFER_SIZE+1];
@@ -649,6 +656,8 @@ int kwsysProcess_GetOption(kwsysProcess* cp, int optionId)
     case kwsysProcess_Option_Detach: return cp->OptionDetach;
     case kwsysProcess_Option_MergeOutput: return cp->MergeOutput;
     case kwsysProcess_Option_Verbatim: return cp->Verbatim;
+    case kwsysProcess_Option_CreateProcessGroup:
+      return cp->CreateProcessGroup;
     default: return 0;
     }
 }
@@ -666,6 +675,8 @@ void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, 
int value)
     case kwsysProcess_Option_Detach: cp->OptionDetach = value; break;
     case kwsysProcess_Option_MergeOutput: cp->MergeOutput = value; break;
     case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break;
+    case kwsysProcess_Option_CreateProcessGroup:
+      cp->CreateProcessGroup = value; break;
     default: break;
     }
 }
@@ -1490,6 +1501,45 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* 
userTimeout)
 }
 
 /*--------------------------------------------------------------------------*/
+void kwsysProcess_Interrupt(kwsysProcess* cp)
+{
+  int i;
+  /* Make sure we are executing a process.  */
+  if(!cp || cp->State != kwsysProcess_State_Executing || cp->TimeoutExpired ||
+     cp->Killed)
+    {
+    return;
+    }
+
+  /* Interrupt the children.  */
+  if (cp->CreateProcessGroup)
+    {
+    if(cp->ForkPIDs)
+      {
+      for(i=0; i < cp->NumberOfCommands; ++i)
+        {
+        /* Make sure the PID is still valid. */
+        if(cp->ForkPIDs[i])
+          {
+          /* The user created a process group for this process.  The group ID
+             is the process ID for the original process in the group.  */
+          kill(-cp->ForkPIDs[i], SIGINT);
+          }
+        }
+      }
+    }
+  else
+    {
+    /* No process group was created.  Kill our own process group.
+       NOTE:  While one could argue that we could call kill(cp->ForkPIDs[i],
+       SIGINT) as a way to still interrupt the process even though it's not in
+       a special group, this is not an option on Windows.  Therefore, we kill
+       the current process group for consistency with Windows.  */
+    kill(0, SIGINT);
+    }
+}
+
+/*--------------------------------------------------------------------------*/
 void kwsysProcess_Kill(kwsysProcess* cp)
 {
   int i;
@@ -1539,10 +1589,28 @@ void kwsysProcess_Kill(kwsysProcess* cp)
 }
 
 /*--------------------------------------------------------------------------*/
+/* Call the free() function with a pointer to volatile without causing
+   compiler warnings.  */
+static void kwsysProcessVolatileFree(volatile void* p)
+{
+  /* clang has made it impossible to free memory that points to volatile
+     without first using special pragmas to disable a warning...  */
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wcast-qual"
+#endif
+  free((void*)p); /* The cast will silence most compilers, but not clang.  */
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#endif
+}
+
+/*--------------------------------------------------------------------------*/
 /* Initialize a process control structure for kwsysProcess_Execute.  */
 static int kwsysProcessInitialize(kwsysProcess* cp)
 {
   int i;
+  volatile pid_t* oldForkPIDs;
   for(i=0; i < KWSYSPE_PIPE_COUNT; ++i)
     {
     cp->PipeReadEnds[i] = -1;
@@ -1571,16 +1639,21 @@ static int kwsysProcessInitialize(kwsysProcess* cp)
   cp->ErrorMessage[0] = 0;
   strcpy(cp->ExitExceptionString, "No exception");
 
-  if(cp->ForkPIDs)
+  oldForkPIDs = cp->ForkPIDs;
+  cp->ForkPIDs = (volatile pid_t*)malloc(
+    sizeof(volatile pid_t)*(size_t)(cp->NumberOfCommands));
+  if(oldForkPIDs)
     {
-    free(cp->ForkPIDs);
+    kwsysProcessVolatileFree(oldForkPIDs);
     }
-  cp->ForkPIDs = (pid_t*)malloc(sizeof(pid_t)*(size_t)(cp->NumberOfCommands));
   if(!cp->ForkPIDs)
     {
     return 0;
     }
-  memset(cp->ForkPIDs, 0, sizeof(pid_t)*(size_t)(cp->NumberOfCommands));
+  for(i=0; i < cp->NumberOfCommands; ++i)
+    {
+    cp->ForkPIDs[i] = 0; /* can't use memset due to volatile */
+    }
 
   if(cp->CommandExitCodes)
     {
@@ -1671,7 +1744,7 @@ static void kwsysProcessCleanup(kwsysProcess* cp, int 
error)
   /* Free memory.  */
   if(cp->ForkPIDs)
     {
-    free(cp->ForkPIDs);
+    kwsysProcessVolatileFree(cp->ForkPIDs);
     cp->ForkPIDs = 0;
     }
   if(cp->RealWorkingDirectory)
@@ -1758,15 +1831,49 @@ int decc$set_child_standard_streams(int fd1, int fd2, 
int fd3);
 static int kwsysProcessCreate(kwsysProcess* cp, int prIndex,
                               kwsysProcessCreateInformation* si)
 {
+  sigset_t mask, old_mask;
+  int pgidPipe[2];
+  char tmp;
+  ssize_t readRes;
+
   /* Create the error reporting pipe.  */
   if(pipe(si->ErrorPipe) < 0)
     {
     return 0;
     }
 
-  /* Set close-on-exec flag on the error pipe's write end.  */
-  if(fcntl(si->ErrorPipe[1], F_SETFD, FD_CLOEXEC) < 0)
+  /* Create a pipe for detecting that the child process has created a process
+     group and session.  */
+  if(pipe(pgidPipe) < 0)
     {
+    kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]);
+    kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
+    return 0;
+    }
+
+  /* Set close-on-exec flag on the pipe's write end.  */
+  if(fcntl(si->ErrorPipe[1], F_SETFD, FD_CLOEXEC) < 0 ||
+     fcntl(pgidPipe[1], F_SETFD, FD_CLOEXEC) < 0)
+    {
+    kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]);
+    kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
+    kwsysProcessCleanupDescriptor(&pgidPipe[0]);
+    kwsysProcessCleanupDescriptor(&pgidPipe[1]);
+    return 0;
+    }
+
+  /* Block SIGINT / SIGTERM while we start.  The purpose is so that our signal
+     handler doesn't get called from the child process after the fork and
+     before the exec, and subsequently start kill()'ing PIDs from ForkPIDs. */
+  sigemptyset(&mask);
+  sigaddset(&mask, SIGINT);
+  sigaddset(&mask, SIGTERM);
+  if(sigprocmask(SIG_BLOCK, &mask, &old_mask) < 0)
+    {
+    kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]);
+    kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
+    kwsysProcessCleanupDescriptor(&pgidPipe[0]);
+    kwsysProcessCleanupDescriptor(&pgidPipe[1]);
     return 0;
     }
 
@@ -1774,13 +1881,19 @@ static int kwsysProcessCreate(kwsysProcess* cp, int 
prIndex,
 #if defined(__VMS)
   /* VMS needs vfork and execvp to be in the same function because
      they use setjmp/longjmp to run the child startup code in the
-     parent!  TODO: OptionDetach.  */
+     parent!  TODO: OptionDetach.  Also
+     TODO:  CreateProcessGroup.  */
   cp->ForkPIDs[prIndex] = vfork();
 #else
   cp->ForkPIDs[prIndex] = kwsysProcessFork(cp, si);
 #endif
   if(cp->ForkPIDs[prIndex] < 0)
     {
+    sigprocmask(SIG_SETMASK, &old_mask, 0);
+    kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]);
+    kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
+    kwsysProcessCleanupDescriptor(&pgidPipe[0]);
+    kwsysProcessCleanupDescriptor(&pgidPipe[1]);
     return 0;
     }
 
@@ -1790,8 +1903,10 @@ static int kwsysProcessCreate(kwsysProcess* cp, int 
prIndex,
     /* Specify standard pipes for child process.  */
     decc$set_child_standard_streams(si->StdIn, si->StdOut, si->StdErr);
 #else
-    /* Close the read end of the error reporting pipe.  */
+    /* Close the read end of the error reporting / process group
+       setup pipe.  */
     close(si->ErrorPipe[0]);
+    close(pgidPipe[0]);
 
     /* Setup the stdin, stdout, and stderr pipes.  */
     if(si->StdIn > 0)
@@ -1819,11 +1934,25 @@ static int kwsysProcessCreate(kwsysProcess* cp, int 
prIndex,
 
     /* Restore all default signal handlers. */
     kwsysProcessRestoreDefaultSignalHandlers();
+
+    /* Now that we have restored default signal handling and created the
+       process group, restore mask.  */
+    sigprocmask(SIG_SETMASK, &old_mask, 0);
+
+    /* Create new process group.  We use setsid instead of setpgid to avoid
+       the child getting hung up on signals like SIGTTOU.  (In the real world,
+       this has been observed where "git svn" ends up calling the "resize"
+       program which opens /dev/tty.  */
+    if(cp->CreateProcessGroup && setsid() < 0)
+      {
+      kwsysProcessChildErrorExit(si->ErrorPipe[1]);
+      }
 #endif
 
     /* Execute the real process.  If successful, this does not return.  */
     execvp(cp->Commands[prIndex][0], cp->Commands[prIndex]);
     /* TODO: What does VMS do if the child fails to start?  */
+    /* TODO: On VMS, how do we put the process in a new group?  */
 
     /* Failure.  Report error to parent and terminate.  */
     kwsysProcessChildErrorExit(si->ErrorPipe[1]);
@@ -1834,12 +1963,34 @@ static int kwsysProcessCreate(kwsysProcess* cp, int 
prIndex,
   decc$set_child_standard_streams(0, 1, 2);
 #endif
 
+  /* We are done with the error reporting pipe and process group setup pipe
+     write end.  */
+  kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
+  kwsysProcessCleanupDescriptor(&pgidPipe[1]);
+
+  /* Make sure the child is in the process group before we proceed.  This
+     avoids race conditions with calls to the kill function that we make for
+     signalling process groups.  */
+  while((readRes = read(pgidPipe[0], &tmp, 1)) > 0);
+  if(readRes < 0)
+    {
+    sigprocmask(SIG_SETMASK, &old_mask, 0);
+    kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]);
+    kwsysProcessCleanupDescriptor(&pgidPipe[0]);
+    return 0;
+    }
+  kwsysProcessCleanupDescriptor(&pgidPipe[0]);
+
+  /* Unmask signals.  */
+  if(sigprocmask(SIG_SETMASK, &old_mask, 0) < 0)
+    {
+    kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]);
+    return 0;
+    }
+
   /* A child has been created.  */
   ++cp->CommandsLeft;
 
-  /* We are done with the error reporting pipe write end.  */
-  kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]);
-
   /* Block until the child's exec call succeeds and closes the error
      pipe or writes data to the pipe to report an error.  */
   {
@@ -1877,6 +2028,17 @@ static void kwsysProcessDestroy(kwsysProcess* cp)
   /* A child process has terminated.  Reap it if it is one handled by
      this object.  */
   int i;
+  /* Temporarily disable signals that access ForkPIDs.  We don't want them to
+     read a reaped PID, and writes to ForkPIDs are not atomic.  */
+  sigset_t mask, old_mask;
+  sigemptyset(&mask);
+  sigaddset(&mask, SIGINT);
+  sigaddset(&mask, SIGTERM);
+  if(sigprocmask(SIG_BLOCK, &mask, &old_mask) < 0)
+    {
+    return;
+    }
+
   for(i=0; i < cp->NumberOfCommands; ++i)
     {
     if(cp->ForkPIDs[i])
@@ -1910,6 +2072,9 @@ static void kwsysProcessDestroy(kwsysProcess* cp)
         }
       }
     }
+
+  /* Re-enable signals.  */
+  sigprocmask(SIG_SETMASK, &old_mask, 0);
 }
 
 /*--------------------------------------------------------------------------*/
@@ -1938,7 +2103,7 @@ static int kwsysProcessSetupOutputPipeFile(int* p, const 
char* name)
 
   /* Assign the replacement descriptor.  */
   *p = fout;
-  return 1;  
+  return 1;
 }
 
 /*--------------------------------------------------------------------------*/
@@ -2582,19 +2747,23 @@ typedef struct kwsysProcessInstances_s
 } kwsysProcessInstances;
 static kwsysProcessInstances kwsysProcesses;
 
-/* The old SIGCHLD handler.  */
+/* The old SIGCHLD / SIGINT / SIGTERM handlers.  */
 static struct sigaction kwsysProcessesOldSigChldAction;
+static struct sigaction kwsysProcessesOldSigIntAction;
+static struct sigaction kwsysProcessesOldSigTermAction;
 
 /*--------------------------------------------------------------------------*/
 static void kwsysProcessesUpdate(kwsysProcessInstances* newProcesses)
 {
-  /* Block SIGCHLD while we update the set of pipes to check.
+  /* Block signals while we update the set of pipes to check.
      TODO: sigprocmask is undefined for threaded apps.  See
      pthread_sigmask.  */
   sigset_t newset;
   sigset_t oldset;
   sigemptyset(&newset);
   sigaddset(&newset, SIGCHLD);
+  sigaddset(&newset, SIGINT);
+  sigaddset(&newset, SIGTERM);
   sigprocmask(SIG_BLOCK, &newset, &oldset);
 
   /* Store the new set in that seen by the signal handler.  */
@@ -2686,21 +2855,36 @@ static int kwsysProcessesAdd(kwsysProcess* cp)
     {
     /* Install our handler for SIGCHLD.  Repeat call until it is not
        interrupted.  */
-    struct sigaction newSigChldAction;
-    memset(&newSigChldAction, 0, sizeof(struct sigaction));
+    struct sigaction newSigAction;
+    memset(&newSigAction, 0, sizeof(struct sigaction));
 #if KWSYSPE_USE_SIGINFO
-    newSigChldAction.sa_sigaction = kwsysProcessesSignalHandler;
-    newSigChldAction.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
+    newSigAction.sa_sigaction = kwsysProcessesSignalHandler;
+    newSigAction.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
 # ifdef SA_RESTART
-    newSigChldAction.sa_flags |= SA_RESTART;
+    newSigAction.sa_flags |= SA_RESTART;
 # endif
 #else
-    newSigChldAction.sa_handler = kwsysProcessesSignalHandler;
-    newSigChldAction.sa_flags = SA_NOCLDSTOP;
+    newSigAction.sa_handler = kwsysProcessesSignalHandler;
+    newSigAction.sa_flags = SA_NOCLDSTOP;
 #endif
-    while((sigaction(SIGCHLD, &newSigChldAction,
+    sigemptyset(&newSigAction.sa_mask);
+    while((sigaction(SIGCHLD, &newSigAction,
                      &kwsysProcessesOldSigChldAction) < 0) &&
           (errno == EINTR));
+
+    /* Install our handler for SIGINT / SIGTERM.  Repeat call until
+       it is not interrupted.  */
+    sigemptyset(&newSigAction.sa_mask);
+    sigaddset(&newSigAction.sa_mask, SIGTERM);
+    while((sigaction(SIGINT, &newSigAction,
+                     &kwsysProcessesOldSigIntAction) < 0) &&
+          (errno == EINTR));
+
+    sigemptyset(&newSigAction.sa_mask);
+    sigaddset(&newSigAction.sa_mask, SIGINT);
+    while((sigaction(SIGTERM, &newSigAction,
+                     &kwsysProcessesOldSigIntAction) < 0) &&
+          (errno == EINTR));
     }
   }
 
@@ -2734,10 +2918,14 @@ static void kwsysProcessesRemove(kwsysProcess* cp)
     /* If this was the last process, disable the signal handler.  */
     if(newProcesses.Count == 0)
       {
-      /* Restore the SIGCHLD handler.  Repeat call until it is not
+      /* Restore the signal handlers.  Repeat call until it is not
          interrupted.  */
       while((sigaction(SIGCHLD, &kwsysProcessesOldSigChldAction, 0) < 0) &&
             (errno == EINTR));
+      while((sigaction(SIGINT, &kwsysProcessesOldSigIntAction, 0) < 0) &&
+            (errno == EINTR));
+      while((sigaction(SIGTERM, &kwsysProcessesOldSigTermAction, 0) < 0) &&
+            (errno == EINTR));
 
       /* Free the table of process pointers since it is now empty.
          This is safe because the signal handler has been removed.  */
@@ -2763,39 +2951,108 @@ static void kwsysProcessesSignalHandler(int signum
 #endif
   )
 {
-  (void)signum;
+  int i, j, procStatus, old_errno = errno;
 #if KWSYSPE_USE_SIGINFO
   (void)info;
   (void)ucontext;
 #endif
 
   /* Signal all process objects that a child has terminated.  */
-  {
-  int i;
-  for(i=0; i < kwsysProcesses.Count; ++i)
+  switch(signum)
     {
-    /* Set the pipe in a signalled state.  */
-    char buf = 1;
-    kwsysProcess* cp = kwsysProcesses.Processes[i];
-    kwsysProcess_ssize_t status=
-      read(cp->PipeReadEnds[KWSYSPE_PIPE_SIGNAL], &buf, 1);
-    (void)status;
-    status=write(cp->SignalPipe, &buf, 1);
-    (void)status;
+    case SIGCHLD:
+      for(i=0; i < kwsysProcesses.Count; ++i)
+        {
+        /* Set the pipe in a signalled state.  */
+        char buf = 1;
+        kwsysProcess* cp = kwsysProcesses.Processes[i];
+        kwsysProcess_ssize_t pipeStatus=
+          read(cp->PipeReadEnds[KWSYSPE_PIPE_SIGNAL], &buf, 1);
+        (void)pipeStatus;
+        pipeStatus=write(cp->SignalPipe, &buf, 1);
+        (void)pipeStatus;
+        }
+      break;
+    case SIGINT:
+    case SIGTERM:
+      /* Signal child processes that are running in new process groups.  */
+      for(i=0; i < kwsysProcesses.Count; ++i)
+        {
+        kwsysProcess* cp = kwsysProcesses.Processes[i];
+        /* Check Killed to avoid data race condition when killing.
+           Check State to avoid data race condition in kwsysProcessCleanup
+           when there is an error (it leaves a reaped PID).  */
+        if(cp->CreateProcessGroup && !cp->Killed &&
+           cp->State != kwsysProcess_State_Error && cp->ForkPIDs)
+          {
+          for(j=0; j < cp->NumberOfCommands; ++j)
+            {
+            /* Make sure the PID is still valid. */
+            if(cp->ForkPIDs[j])
+              {
+              /* The user created a process group for this process.  The group 
ID
+                 is the process ID for the original process in the group.  */
+              kill(-cp->ForkPIDs[j], SIGINT);
+              }
+            }
+          }
+        }
+
+      /* Wait for all processes to terminate.  */
+      while(wait(&procStatus) >= 0 || errno != ECHILD)
+        {
+        }
+
+      /* Terminate the process, which is now in an inconsistent state
+         because we reaped all the PIDs that it may have been reaping
+         or may have reaped in the future.  Reraise the signal so that
+         the proper exit code is returned.  */
+      {
+      /* Install default signal handler.  */
+      struct sigaction defSigAction;
+      sigset_t unblockSet;
+      memset(&defSigAction, 0, sizeof(defSigAction));
+      defSigAction.sa_handler = SIG_DFL;
+      sigemptyset(&defSigAction.sa_mask);
+      while((sigaction(signum, &defSigAction, 0) < 0) &&
+            (errno == EINTR));
+      /* Unmask the signal.  */
+      sigemptyset(&unblockSet);
+      sigaddset(&unblockSet, signum);
+      sigprocmask(SIG_UNBLOCK, &unblockSet, 0);
+      /* Raise the signal again.  */
+      raise(signum);
+      /* We shouldn't get here... but if we do... */
+      _exit(1);
+      }
+      /* break omitted to silence unreachable code clang compiler warning.  */
     }
-  }
 
 #if !KWSYSPE_USE_SIGINFO
-  /* Re-Install our handler for SIGCHLD.  Repeat call until it is not
-     interrupted.  */
+  /* Re-Install our handler.  Repeat call until it is not interrupted.  */
   {
-  struct sigaction newSigChldAction;
-  memset(&newSigChldAction, 0, sizeof(struct sigaction));
+  struct sigaction newSigAction;
+  struct sigaction &oldSigAction;
+  memset(&newSigAction, 0, sizeof(struct sigaction));
   newSigChldAction.sa_handler = kwsysProcessesSignalHandler;
   newSigChldAction.sa_flags = SA_NOCLDSTOP;
-  while((sigaction(SIGCHLD, &newSigChldAction,
-                   &kwsysProcessesOldSigChldAction) < 0) &&
+  sigemptyset(&newSigAction.sa_mask);
+  switch(signum)
+    {
+    case SIGCHLD: oldSigAction = &kwsysProcessesOldSigChldAction; break;
+    case SIGINT:
+      sigaddset(&newSigAction.sa_mask, SIGTERM);
+      oldSigAction = &kwsysProcessesOldSigIntAction; break;
+    case SIGTERM:
+      sigaddset(&newSigAction.sa_mask, SIGINT);
+      oldSigAction = &kwsysProcessesOldSigTermAction; break;
+    default: return 0;
+    }
+  while((sigaction(signum, &newSigAction,
+                   oldSigAction) < 0) &&
         (errno == EINTR));
   }
 #endif
+
+  errno = old_errno;
 }
diff --git a/ProcessWin32.c b/ProcessWin32.c
index a7dd2ca..1f8749f 100644
--- a/ProcessWin32.c
+++ b/ProcessWin32.c
@@ -109,14 +109,15 @@ static DWORD WINAPI kwsysProcessPipeThreadWake(LPVOID 
ptd);
 static void kwsysProcessPipeThreadWakePipe(kwsysProcess* cp,
                                            kwsysProcessPipeData* td);
 static int kwsysProcessInitialize(kwsysProcess* cp);
-static int kwsysProcessCreate(kwsysProcess* cp, int index,
-                              kwsysProcessCreateInformation* si);
+static DWORD kwsysProcessCreate(kwsysProcess* cp, int index,
+                                kwsysProcessCreateInformation* si);
 static void kwsysProcessDestroy(kwsysProcess* cp, int event);
-static int kwsysProcessSetupOutputPipeFile(PHANDLE handle, const char* name);
+static DWORD kwsysProcessSetupOutputPipeFile(PHANDLE handle,
+                                             const char* name);
 static void kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle);
 static void kwsysProcessSetupPipeNative(HANDLE native, PHANDLE handle);
 static void kwsysProcessCleanupHandle(PHANDLE h);
-static void kwsysProcessCleanup(kwsysProcess* cp, int error);
+static void kwsysProcessCleanup(kwsysProcess* cp, DWORD error);
 static void kwsysProcessCleanErrorMessage(kwsysProcess* cp);
 static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout,
                                       kwsysProcessTime* timeoutTime);
@@ -133,6 +134,13 @@ static kwsysProcessTime 
kwsysProcessTimeSubtract(kwsysProcessTime in1, kwsysProc
 static void kwsysProcessSetExitException(kwsysProcess* cp, int code);
 static void kwsysProcessKillTree(int pid);
 static void kwsysProcessDisablePipeThreads(kwsysProcess* cp);
+static int kwsysProcessesInitialize(void);
+static int kwsysTryEnterCreateProcessSection(void);
+static void kwsysLeaveCreateProcessSection(void);
+static int kwsysProcessesAdd(HANDLE hProcess, DWORD dwProcessId,
+                             int newProcessGroup);
+static void kwsysProcessesRemove(HANDLE hProcess);
+static BOOL WINAPI kwsysCtrlHandler(DWORD dwCtrlType);
 
 /*--------------------------------------------------------------------------*/
 /* A structure containing synchronization data for each thread.  */
@@ -222,6 +230,9 @@ struct kwsysProcess_s
   /* Whether to merge stdout/stderr of the child.  */
   int MergeOutput;
 
+  /* Whether to create the process in a new process group.  */
+  int CreateProcessGroup;
+
   /* Mutex to protect the shared index used by threads to report data.  */
   HANDLE SharedIndexMutex;
 
@@ -321,6 +332,16 @@ kwsysProcess* kwsysProcess_New(void)
   /* Windows version number data.  */
   OSVERSIONINFO osv;
 
+  /* Initialize list of processes before we get any farther.  It's especially
+     important that the console Ctrl handler be added BEFORE starting the
+     first process.  This prevents the risk of an orphaned process being
+     started by the main thread while the default Ctrl handler is in
+     progress.  */
+  if(!kwsysProcessesInitialize())
+    {
+    return 0;
+    }
+
   /* Allocate a process control structure.  */
   cp = (kwsysProcess*)malloc(sizeof(kwsysProcess));
   if(!cp)
@@ -836,6 +857,8 @@ int kwsysProcess_GetOption(kwsysProcess* cp, int optionId)
     case kwsysProcess_Option_HideWindow: return cp->HideWindow;
     case kwsysProcess_Option_MergeOutput: return cp->MergeOutput;
     case kwsysProcess_Option_Verbatim: return cp->Verbatim;
+    case kwsysProcess_Option_CreateProcessGroup:
+      return cp->CreateProcessGroup;
     default: return 0;
     }
 }
@@ -854,6 +877,8 @@ void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, 
int value)
     case kwsysProcess_Option_HideWindow: cp->HideWindow = value; break;
     case kwsysProcess_Option_MergeOutput: cp->MergeOutput = value; break;
     case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break;
+    case kwsysProcess_Option_CreateProcessGroup:
+      cp->CreateProcessGroup = value; break;
     default: break;
     }
 }
@@ -945,7 +970,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
     if(!GetCurrentDirectoryW(cp->RealWorkingDirectoryLength,
                             cp->RealWorkingDirectory))
       {
-      kwsysProcessCleanup(cp, 1);
+      kwsysProcessCleanup(cp, GetLastError());
       return;
       }
     SetCurrentDirectoryW(cp->WorkingDirectory);
@@ -957,14 +982,16 @@ void kwsysProcess_Execute(kwsysProcess* cp)
     {
     /* Create a handle to read a file for stdin.  */
     wchar_t* wstdin = kwsysEncoding_DupToWide(cp->PipeFileSTDIN);
+    DWORD error;
     cp->PipeChildStd[0] =
       CreateFileW(wstdin, GENERIC_READ|GENERIC_WRITE,
                   FILE_SHARE_READ|FILE_SHARE_WRITE,
                   0, OPEN_EXISTING, 0, 0);
+    error = GetLastError(); /* Check now in case free changes this.  */
     free(wstdin);
     if(cp->PipeChildStd[0] == INVALID_HANDLE_VALUE)
       {
-      kwsysProcessCleanup(cp, 1);
+      kwsysProcessCleanup(cp, error);
       return;
       }
     }
@@ -990,17 +1017,18 @@ void kwsysProcess_Execute(kwsysProcess* cp)
   if(!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDOUT].Read,
                  &cp->Pipe[KWSYSPE_PIPE_STDOUT].Write, 0, 0))
     {
-    kwsysProcessCleanup(cp, 1);
+    kwsysProcessCleanup(cp, GetLastError());
     return;
     }
 
   if(cp->PipeFileSTDOUT)
     {
     /* Use a file for stdout.  */
-    if(!kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[1],
-                                        cp->PipeFileSTDOUT))
+    DWORD error = kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[1],
+                                                  cp->PipeFileSTDOUT);
+    if(error)
       {
-      kwsysProcessCleanup(cp, 1);
+      kwsysProcessCleanup(cp, error);
       return;
       }
     }
@@ -1023,7 +1051,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
                         GetCurrentProcess(), &cp->PipeChildStd[1],
                         0, FALSE, DUPLICATE_SAME_ACCESS))
       {
-      kwsysProcessCleanup(cp, 1);
+      kwsysProcessCleanup(cp, GetLastError());
       return;
       }
     }
@@ -1034,17 +1062,18 @@ void kwsysProcess_Execute(kwsysProcess* cp)
   if(!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDERR].Read,
                  &cp->Pipe[KWSYSPE_PIPE_STDERR].Write, 0, 0))
     {
-    kwsysProcessCleanup(cp, 1);
+    kwsysProcessCleanup(cp, GetLastError());
     return;
     }
 
   if(cp->PipeFileSTDERR)
     {
     /* Use a file for stderr.  */
-    if(!kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[2],
-                                        cp->PipeFileSTDERR))
+    DWORD error = kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[2],
+                                                  cp->PipeFileSTDERR);
+    if(error)
       {
-      kwsysProcessCleanup(cp, 1);
+      kwsysProcessCleanup(cp, error);
       return;
       }
     }
@@ -1067,7 +1096,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
                         GetCurrentProcess(), &cp->PipeChildStd[2],
                         0, FALSE, DUPLICATE_SAME_ACCESS))
       {
-      kwsysProcessCleanup(cp, 1);
+      kwsysProcessCleanup(cp, GetLastError());
       return;
       }
     }
@@ -1106,11 +1135,12 @@ void kwsysProcess_Execute(kwsysProcess* cp)
       HANDLE p[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
       if (!CreatePipe(&p[0], &p[1], 0, 0))
         {
+        DWORD error = GetLastError();
         if (nextStdInput != cp->PipeChildStd[0])
           {
           kwsysProcessCleanupHandle(&nextStdInput);
           }
-        kwsysProcessCleanup(cp, 1);
+        kwsysProcessCleanup(cp, error);
         return;
         }
       nextStdInput = p[0];
@@ -1119,7 +1149,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
     si.hStdError = cp->MergeOutput? cp->PipeChildStd[1] : cp->PipeChildStd[2];
 
     {
-    int res = kwsysProcessCreate(cp, i, &si);
+    DWORD error = kwsysProcessCreate(cp, i, &si);
 
     /* Close our copies of pipes used between children.  */
     if (si.hStdInput != cp->PipeChildStd[0])
@@ -1134,7 +1164,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
       {
       kwsysProcessCleanupHandle(&si.hStdError);
       }
-    if (res)
+    if (!error)
       {
       cp->ProcessEvents[i+1] = cp->ProcessInformation[i].hProcess;
       }
@@ -1144,7 +1174,7 @@ void kwsysProcess_Execute(kwsysProcess* cp)
         {
         kwsysProcessCleanupHandle(&nextStdInput);
         }
-      kwsysProcessCleanup(cp, 1);
+      kwsysProcessCleanup(cp, error);
       return;
       }
     }
@@ -1460,6 +1490,52 @@ int kwsysProcess_WaitForExit(kwsysProcess* cp, double* 
userTimeout)
 }
 
 /*--------------------------------------------------------------------------*/
+void kwsysProcess_Interrupt(kwsysProcess* cp)
+{
+  int i;
+  /* Make sure we are executing a process.  */
+  if(!cp || cp->State != kwsysProcess_State_Executing || cp->TimeoutExpired ||
+     cp->Killed)
+    {
+    KWSYSPE_DEBUG((stderr, "interrupt: child not executing\n"));
+    return;
+    }
+
+  /* Skip actually interrupting the child if it has already terminated.  */
+  if(cp->Terminated)
+    {
+    KWSYSPE_DEBUG((stderr, "interrupt: child already terminated\n"));
+    return;
+    }
+
+  /* Interrupt the children.  */
+  if (cp->CreateProcessGroup)
+    {
+    if(cp->ProcessInformation)
+      {
+      for(i=0; i < cp->NumberOfCommands; ++i)
+        {
+        /* Make sure the process handle isn't closed (e.g. from disowning). */
+        if(cp->ProcessInformation[i].hProcess)
+          {
+          /* The user created a process group for this process.  The group ID
+             is the process ID for the original process in the group.  Note
+             that we have to use Ctrl+Break: Ctrl+C is not allowed for process
+             groups.  */
+          GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
+                                   cp->ProcessInformation[i].dwProcessId);
+          }
+        }
+      }
+    }
+  else
+    {
+    /* No process group was created.  Kill our own process group...  */
+    GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0);
+    }
+}
+
+/*--------------------------------------------------------------------------*/
 void kwsysProcess_Kill(kwsysProcess* cp)
 {
   int i;
@@ -1487,7 +1563,8 @@ void kwsysProcess_Kill(kwsysProcess* cp)
   for(i=0; i < cp->NumberOfCommands; ++i)
     {
     kwsysProcessKillTree(cp->ProcessInformation[i].dwProcessId);
-    // close the handle if we kill it
+    /* Remove from global list of processes and close handles.  */
+    kwsysProcessesRemove(cp->ProcessInformation[i].hProcess);
     kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread);
     kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess);
     }
@@ -1686,7 +1763,7 @@ int kwsysProcessInitialize(kwsysProcess* cp)
 }
 
 /*--------------------------------------------------------------------------*/
-static int kwsysProcessCreateChildHandle(PHANDLE out, HANDLE in, int isStdIn)
+static DWORD kwsysProcessCreateChildHandle(PHANDLE out, HANDLE in, int isStdIn)
 {
   DWORD flags;
 
@@ -1697,13 +1774,19 @@ static int kwsysProcessCreateChildHandle(PHANDLE out, 
HANDLE in, int isStdIn)
     if (flags & HANDLE_FLAG_INHERIT)
       {
       *out = in;
-      return 1;
+      return ERROR_SUCCESS;
       }
 
     /* Create an inherited copy of this handle.  */
-    return DuplicateHandle(GetCurrentProcess(), in,
-                           GetCurrentProcess(), out,
-                           0, TRUE, DUPLICATE_SAME_ACCESS);
+    if (DuplicateHandle(GetCurrentProcess(), in, GetCurrentProcess(), out,
+                        0, TRUE, DUPLICATE_SAME_ACCESS))
+      {
+      return ERROR_SUCCESS;
+      }
+    else
+      {
+      return GetLastError();
+      }
     }
   else
     {
@@ -1719,29 +1802,46 @@ static int kwsysProcessCreateChildHandle(PHANDLE out, 
HANDLE in, int isStdIn)
                         (GENERIC_WRITE | FILE_READ_ATTRIBUTES)),
                        FILE_SHARE_READ|FILE_SHARE_WRITE,
                        &sa, OPEN_EXISTING, 0, 0);
-    return *out != INVALID_HANDLE_VALUE;
+    return (*out != INVALID_HANDLE_VALUE) ? ERROR_SUCCESS : GetLastError();
     }
-
 }
 
 /*--------------------------------------------------------------------------*/
-int kwsysProcessCreate(kwsysProcess* cp, int index,
-                       kwsysProcessCreateInformation* si)
+DWORD kwsysProcessCreate(kwsysProcess* cp, int index,
+                         kwsysProcessCreateInformation* si)
 {
-  int res =
+  DWORD creationFlags;
+  DWORD error = ERROR_SUCCESS;
+
+  /* Check if we are currently exiting.  */
+  if (!kwsysTryEnterCreateProcessSection())
+    {
+    /* The Ctrl handler is currently working on exiting our process.  Rather
+    than return an error code, which could cause incorrect conclusions to be
+    reached by the caller, we simply hang.  (For example, a CMake try_run
+    configure step might cause the project to configure wrong.)  */
+    Sleep(INFINITE);
+    }
 
-    /* Create inherited copies the handles.  */
-    kwsysProcessCreateChildHandle(&si->StartupInfo.hStdInput,
-                                  si->hStdInput, 1) &&
-    kwsysProcessCreateChildHandle(&si->StartupInfo.hStdOutput,
-                                  si->hStdOutput, 0) &&
-    kwsysProcessCreateChildHandle(&si->StartupInfo.hStdError,
-                                  si->hStdError, 0) &&
+  /* Create the child in a suspended state so we can wait until all
+     children have been created before running any one.  */
+  creationFlags = CREATE_SUSPENDED;
+  if (cp->CreateProcessGroup)
+    {
+    creationFlags |= CREATE_NEW_PROCESS_GROUP;
+    }
 
-    /* Create the child in a suspended state so we can wait until all
-       children have been created before running any one.  */
-    CreateProcessW(0, cp->Commands[index], 0, 0, TRUE, CREATE_SUSPENDED, 0,
-                   0, &si->StartupInfo, &cp->ProcessInformation[index]);
+  /* Create inherited copies of the handles.  */
+  (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdInput,
+                                          si->hStdInput, 1)) ||
+  (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdOutput,
+                                          si->hStdOutput, 0)) ||
+  (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdError,
+                                          si->hStdError, 0)) ||
+  /* Create the process.  */
+  (!CreateProcessW(0, cp->Commands[index], 0, 0, TRUE, creationFlags, 0,
+                  0, &si->StartupInfo, &cp->ProcessInformation[index]) &&
+    (error = GetLastError()));
 
   /* Close the inherited copies of the handles. */
   if (si->StartupInfo.hStdInput != si->hStdInput)
@@ -1757,7 +1857,23 @@ int kwsysProcessCreate(kwsysProcess* cp, int index,
     kwsysProcessCleanupHandle(&si->StartupInfo.hStdError);
     }
 
-  return res;
+  /* Add the process to the global list of processes. */
+  if (!error &&
+      !kwsysProcessesAdd(cp->ProcessInformation[index].hProcess,
+      cp->ProcessInformation[index].dwProcessId, cp->CreateProcessGroup))
+    {
+    /* This failed for some reason.  Kill the suspended process. */
+    TerminateProcess(cp->ProcessInformation[index].hProcess, 1);
+    /* And clean up... */
+    kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hProcess);
+    kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hThread);
+    strcpy(cp->ErrorMessage, "kwsysProcessesAdd function failed");
+    error = ERROR_NOT_ENOUGH_MEMORY; /* Most likely reason.  */
+    }
+
+  /* If the console Ctrl handler is waiting for us, this will release it... */
+  kwsysLeaveCreateProcessSection();
+  return error;
 }
 
 /*--------------------------------------------------------------------------*/
@@ -1779,6 +1895,9 @@ void kwsysProcessDestroy(kwsysProcess* cp, int event)
   GetExitCodeProcess(cp->ProcessInformation[index].hProcess,
                      &cp->CommandExitCodes[index]);
 
+  /* Remove from global list of processes.  */
+  kwsysProcessesRemove(cp->ProcessInformation[index].hProcess);
+
   /* Close the process handle for the terminated process.  */
   kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hProcess);
 
@@ -1813,13 +1932,14 @@ void kwsysProcessDestroy(kwsysProcess* cp, int event)
 }
 
 /*--------------------------------------------------------------------------*/
-int kwsysProcessSetupOutputPipeFile(PHANDLE phandle, const char* name)
+DWORD kwsysProcessSetupOutputPipeFile(PHANDLE phandle, const char* name)
 {
   HANDLE fout;
   wchar_t* wname;
+  DWORD error;
   if(!name)
     {
-    return 1;
+    return ERROR_INVALID_PARAMETER;
     }
 
   /* Close the existing handle.  */
@@ -1829,15 +1949,16 @@ int kwsysProcessSetupOutputPipeFile(PHANDLE phandle, 
const char* name)
   wname = kwsysEncoding_DupToWide(name);
   fout = CreateFileW(wname, GENERIC_WRITE, FILE_SHARE_READ, 0,
                     CREATE_ALWAYS, 0, 0);
+  error = GetLastError();
   free(wname);
   if(fout == INVALID_HANDLE_VALUE)
     {
-    return 0;
+    return error;
     }
 
   /* Assign the replacement handle.  */
   *phandle = fout;
-  return 1;
+  return ERROR_SUCCESS;
 }
 
 /*--------------------------------------------------------------------------*/
@@ -1876,7 +1997,7 @@ void kwsysProcessCleanupHandle(PHANDLE h)
 /*--------------------------------------------------------------------------*/
 
 /* Close all handles created by kwsysProcess_Execute.  */
-void kwsysProcessCleanup(kwsysProcess* cp, int error)
+void kwsysProcessCleanup(kwsysProcess* cp, DWORD error)
 {
   int i;
   /* If this is an error case, report the error.  */
@@ -1886,21 +2007,27 @@ void kwsysProcessCleanup(kwsysProcess* cp, int error)
     if(cp->ErrorMessage[0] == 0)
       {
       /* Format the error message.  */
-      DWORD original = GetLastError();
       wchar_t err_msg[KWSYSPE_PIPE_BUFFER_SIZE];
       DWORD length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
-                                   FORMAT_MESSAGE_IGNORE_INSERTS, 0, original,
+                                   FORMAT_MESSAGE_IGNORE_INSERTS, 0, error,
                                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                    err_msg, KWSYSPE_PIPE_BUFFER_SIZE, 0);
-      WideCharToMultiByte(CP_UTF8, 0, err_msg, -1, cp->ErrorMessage,
-                          KWSYSPE_PIPE_BUFFER_SIZE, NULL, NULL);
       if(length < 1)
         {
         /* FormatMessage failed.  Use a default message.  */
         _snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE,
                   "Process execution failed with error 0x%X.  "
                   "FormatMessage failed with error 0x%X",
-                  original, GetLastError());
+                  error, GetLastError());
+        }
+      if(!WideCharToMultiByte(CP_UTF8, 0, err_msg, -1, cp->ErrorMessage,
+                              KWSYSPE_PIPE_BUFFER_SIZE, NULL, NULL))
+        {
+        /* WideCharToMultiByte failed.  Use a default message.  */
+        _snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE,
+                  "Process execution failed with error 0x%X.  "
+                  "WideCharToMultiByte failed with error 0x%X",
+                  error, GetLastError());
         }
       }
 
@@ -1923,6 +2050,8 @@ void kwsysProcessCleanup(kwsysProcess* cp, int error)
         }
       for(i=0; i < cp->NumberOfCommands; ++i)
         {
+        /* Remove from global list of processes and close handles.  */
+        kwsysProcessesRemove(cp->ProcessInformation[i].hProcess);
         kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread);
         kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess);
         }
@@ -2659,3 +2788,230 @@ static void 
kwsysProcessDisablePipeThreads(kwsysProcess* cp)
     ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0);
     }
 }
+
+/*--------------------------------------------------------------------------*/
+/* Global set of executing processes for use by the Ctrl handler.
+   This global instance will be zero-initialized by the compiler.
+
+   Note that the console Ctrl handler runs on a background thread and so
+   everything it does must be thread safe.  Here, we track the hProcess
+   HANDLEs directly instead of kwsysProcess instances, so that we don't have
+   to make kwsysProcess thread safe.  */
+typedef struct kwsysProcessInstance_s
+{
+  HANDLE hProcess;
+  DWORD dwProcessId;
+  int NewProcessGroup; /* Whether the process was created in a new group.  */
+} kwsysProcessInstance;
+
+typedef struct kwsysProcessInstances_s
+{
+  /* Whether we have initialized key fields below, like critical sections.  */
+  int Initialized;
+
+  /* Ctrl handler runs on a different thread, so we must sync access.  */
+  CRITICAL_SECTION Lock;
+
+  int Exiting;
+  size_t Count;
+  size_t Size;
+  kwsysProcessInstance* Processes;
+} kwsysProcessInstances;
+static kwsysProcessInstances kwsysProcesses;
+
+/*--------------------------------------------------------------------------*/
+/* Initialize critial section and set up console Ctrl handler.  You MUST call
+   this before using any other kwsysProcesses* functions below.  */
+static int kwsysProcessesInitialize(void)
+{
+  /* Initialize everything if not done already.  */
+  if(!kwsysProcesses.Initialized)
+    {
+    InitializeCriticalSection(&kwsysProcesses.Lock);
+
+    /* Set up console ctrl handler.  */
+    if(!SetConsoleCtrlHandler(kwsysCtrlHandler, TRUE))
+      {
+      return 0;
+      }
+
+    kwsysProcesses.Initialized = 1;
+    }
+  return 1;
+}
+
+/*--------------------------------------------------------------------------*/
+/* The Ctrl handler waits on the global list of processes.  To prevent an
+   orphaned process, do not create a new process if the Ctrl handler is
+   already running.  Do so by using this function to check if it is ok to
+   create a process.  */
+static int kwsysTryEnterCreateProcessSection(void)
+{
+  /* Enter main critical section; this means creating a process and the Ctrl
+     handler are mutually exclusive.  */
+  EnterCriticalSection(&kwsysProcesses.Lock);
+  /* Indicate to the caller if they can create a process.  */
+  if(kwsysProcesses.Exiting)
+    {
+    LeaveCriticalSection(&kwsysProcesses.Lock);
+    return 0;
+    }
+  else
+    {
+    return 1;
+    }
+}
+
+/*--------------------------------------------------------------------------*/
+/* Matching function on successful kwsysTryEnterCreateProcessSection return.
+   Make sure you called kwsysProcessesAdd if applicable before calling this.*/
+static void kwsysLeaveCreateProcessSection(void)
+{
+  LeaveCriticalSection(&kwsysProcesses.Lock);
+}
+
+/*--------------------------------------------------------------------------*/
+/* Add new process to global process list.  The Ctrl handler will wait for
+   the process to exit before it returns.  Do not close the process handle
+   until after calling kwsysProcessesRemove.  The newProcessGroup parameter
+   must be set if the process was created with CREATE_NEW_PROCESS_GROUP.  */
+static int kwsysProcessesAdd(HANDLE hProcess, DWORD dwProcessid,
+                             int newProcessGroup)
+{
+  if(!kwsysProcessesInitialize() || !hProcess ||
+      hProcess == INVALID_HANDLE_VALUE)
+    {
+    return 0;
+    }
+
+  /* Enter the critical section. */
+  EnterCriticalSection(&kwsysProcesses.Lock);
+
+  /* Make sure there is enough space for the new process handle.  */
+  if(kwsysProcesses.Count == kwsysProcesses.Size)
+    {
+    size_t newSize;
+    kwsysProcessInstance *newArray;
+    /* Start with enough space for a small number of process handles
+       and double the size each time more is needed.  */
+    newSize = kwsysProcesses.Size? kwsysProcesses.Size*2 : 4;
+
+    /* Try allocating the new block of memory.  */
+    if(newArray = (kwsysProcessInstance*)malloc(
+       newSize*sizeof(kwsysProcessInstance)))
+      {
+      /* Copy the old process handles to the new memory.  */
+      if(kwsysProcesses.Count > 0)
+        {
+        memcpy(newArray, kwsysProcesses.Processes,
+               kwsysProcesses.Count * sizeof(kwsysProcessInstance));
+        }
+      }
+    else
+      {
+      /* Failed to allocate memory for the new process handle set.  */
+      LeaveCriticalSection(&kwsysProcesses.Lock);
+      return 0;
+      }
+
+    /* Free original array. */
+    free(kwsysProcesses.Processes);
+
+    /* Update original structure with new allocation. */
+    kwsysProcesses.Size = newSize;
+    kwsysProcesses.Processes = newArray;
+    }
+
+  /* Append the new process information to the set.  */
+  kwsysProcesses.Processes[kwsysProcesses.Count].hProcess = hProcess;
+  kwsysProcesses.Processes[kwsysProcesses.Count].dwProcessId = dwProcessid;
+  kwsysProcesses.Processes[kwsysProcesses.Count++].NewProcessGroup =
+    newProcessGroup;
+
+  /* Leave critical section and return success. */
+  LeaveCriticalSection(&kwsysProcesses.Lock);
+
+  return 1;
+}
+
+/*--------------------------------------------------------------------------*/
+/* Removes process to global process list.  */
+static void kwsysProcessesRemove(HANDLE hProcess)
+{
+  size_t i;
+
+  if (!hProcess || hProcess == INVALID_HANDLE_VALUE)
+    {
+    return;
+    }
+
+  EnterCriticalSection(&kwsysProcesses.Lock);
+
+  /* Find the given process in the set.  */
+  for(i=0; i < kwsysProcesses.Count; ++i)
+    {
+    if(kwsysProcesses.Processes[i].hProcess == hProcess)
+      {
+      break;
+      }
+    }
+  if(i < kwsysProcesses.Count)
+    {
+    /* Found it!  Remove the process from the set.  */
+    --kwsysProcesses.Count;
+    for(; i < kwsysProcesses.Count; ++i)
+      {
+      kwsysProcesses.Processes[i] = kwsysProcesses.Processes[i+1];
+      }
+
+    /* If this was the last process, free the array.  */
+    if(kwsysProcesses.Count == 0)
+      {
+      kwsysProcesses.Size = 0;
+      free(kwsysProcesses.Processes);
+      kwsysProcesses.Processes = 0;
+      }
+    }
+
+  LeaveCriticalSection(&kwsysProcesses.Lock);
+}
+
+/*--------------------------------------------------------------------------*/
+static BOOL WINAPI kwsysCtrlHandler(DWORD dwCtrlType)
+{
+  size_t i;
+  (void)dwCtrlType;
+  /* Enter critical section.  */
+  EnterCriticalSection(&kwsysProcesses.Lock);
+
+  /* Set flag indicating that we are exiting.  */
+  kwsysProcesses.Exiting = 1;
+
+  /* If some of our processes were created in a new process group, we must
+     manually interrupt them.  They won't otherwise receive a Ctrl+C/Break. */
+  for(i=0; i < kwsysProcesses.Count; ++i)
+    {
+    if(kwsysProcesses.Processes[i].NewProcessGroup)
+      {
+      DWORD groupId = kwsysProcesses.Processes[i].dwProcessId;
+      if(groupId)
+        {
+        GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, groupId);
+        }
+      }
+    }
+
+  /* Wait for each child process to exit.  This is the key step that prevents
+     us from leaving several orphaned children processes running in the
+     background when the user presses Ctrl+C.  */
+  for(i=0; i < kwsysProcesses.Count; ++i)
+    {
+    WaitForSingleObject(kwsysProcesses.Processes[i].hProcess, INFINITE);
+    }
+
+  /* Leave critical section.  */
+  LeaveCriticalSection(&kwsysProcesses.Lock);
+
+  /* Continue on to default Ctrl handler (which calls ExitProcess).  */
+  return FALSE;
+}
diff --git a/testProcess.c b/testProcess.c
index 47c3fb0..d0e20c1 100644
--- a/testProcess.c
+++ b/testProcess.c
@@ -29,26 +29,48 @@
 # include <windows.h>
 #else
 # include <unistd.h>
+# include <signal.h>
 #endif
 
 #if defined(__BORLANDC__)
 # pragma warn -8060 /* possibly incorrect assignment */
 #endif
 
+/* Platform-specific sleep functions. */
+
 #if defined(__BEOS__) && !defined(__ZETA__)
 /* BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. */
 # include <be/kernel/OS.h>
-static inline void testProcess_usleep(unsigned int msec)
+static inline void testProcess_usleep(unsigned int usec)
+{
+  snooze(usec);
+}
+#elif defined(_WIN32)
+/* Windows can only sleep in millisecond intervals. */
+static void testProcess_usleep(unsigned int usec)
 {
-  snooze(msec);
+  Sleep(usec / 1000);
 }
 #else
 # define testProcess_usleep usleep
 #endif
 
+#if defined(_WIN32)
+static void testProcess_sleep(unsigned int sec)
+{
+  Sleep(sec*1000);
+}
+#else
+static void testProcess_sleep(unsigned int sec)
+{
+  sleep(sec);
+}
+#endif
+
 int runChild(const char* cmd[], int state, int exception, int value,
              int share, int output, int delay, double timeout, int poll,
-             int repeat, int disown);
+             int repeat, int disown, int createNewGroup,
+             unsigned int interruptDelay);
 
 static int test1(int argc, const char* argv[])
 {
@@ -73,11 +95,7 @@ static int test3(int argc, const char* argv[])
   fprintf(stderr, "Output before sleep on stderr from timeout test.\n");
   fflush(stdout);
   fflush(stderr);
-#if defined(_WIN32)
-  Sleep(15000);
-#else
-  sleep(15);
-#endif
+  testProcess_sleep(15);
   fprintf(stdout, "Output after sleep on stdout from timeout test.\n");
   fprintf(stderr, "Output after sleep on stderr from timeout test.\n");
   return 0;
@@ -102,7 +120,7 @@ static int test4(int argc, const char* argv[])
 #endif
   (void)argc; (void)argv;
   fprintf(stdout, "Output before crash on stdout from crash test.\n");
-  fprintf(stderr, "Output before crash on stderr from crash test.\n");  
+  fprintf(stderr, "Output before crash on stderr from crash test.\n");
   fflush(stdout);
   fflush(stderr);
   assert(invalidAddress); /* Quiet Clang scan-build. */
@@ -127,7 +145,7 @@ static int test5(int argc, const char* argv[])
   fflush(stdout);
   fflush(stderr);
   r = runChild(cmd, kwsysProcess_State_Exception,
-               kwsysProcess_Exception_Fault, 1, 1, 1, 0, 15, 0, 1, 0);
+               kwsysProcess_Exception_Fault, 1, 1, 1, 0, 15, 0, 1, 0, 0, 0);
   fprintf(stdout, "Output on stdout after recursive test.\n");
   fprintf(stderr, "Output on stderr after recursive test.\n");
   fflush(stdout);
@@ -168,11 +186,7 @@ static int test7(int argc, const char* argv[])
   fflush(stdout);
   fflush(stderr);
   /* Sleep for 1 second.  */
-#if defined(_WIN32)
-  Sleep(1000);
-#else
-  sleep(1);
-#endif
+  testProcess_sleep(1);
   fprintf(stdout, "Output on stdout after sleep.\n");
   fprintf(stderr, "Output on stderr after sleep.\n");
   fflush(stdout);
@@ -196,7 +210,7 @@ static int test8(int argc, const char* argv[])
   fflush(stdout);
   fflush(stderr);
   r = runChild(cmd, kwsysProcess_State_Disowned, kwsysProcess_Exception_None,
-               1, 1, 1, 0, 10, 0, 1, 1);
+               1, 1, 1, 0, 10, 0, 1, 1, 0, 0);
   fprintf(stdout, "Output on stdout after grandchild test.\n");
   fprintf(stderr, "Output on stderr after grandchild test.\n");
   fflush(stdout);
@@ -217,18 +231,137 @@ static int test8_grandchild(int argc, const char* argv[])
      implemented.  */
   fclose(stdout);
   fclose(stderr);
+  testProcess_sleep(15);
+  return 0;
+}
+
+static int test9(int argc, const char* argv[])
+{
+  /* Test Ctrl+C behavior: the root test program will send a Ctrl+C to this
+     process.  Here, we start a child process that sleeps for a long time
+     while ignoring signals.  The test is successful if this process waits
+     for the child to return before exiting from the Ctrl+C handler.
+
+     WARNING:  This test will falsely pass if the share parameter of runChild
+     was set to 0 when invoking the test9 process.  */
+  int r;
+  const char* cmd[4];
+  (void)argc;
+  cmd[0] = argv[0];
+  cmd[1] = "run";
+  cmd[2] = "109";
+  cmd[3] = 0;
+  fprintf(stdout, "Output on stdout before grandchild test.\n");
+  fprintf(stderr, "Output on stderr before grandchild test.\n");
+  fflush(stdout);
+  fflush(stderr);
+  r = runChild(cmd, kwsysProcess_State_Exited,
+               kwsysProcess_Exception_None,
+               0, 1, 1, 0, 30, 0, 1, 0, 0, 0);
+  /* This sleep will avoid a race condition between this function exiting
+     normally and our Ctrl+C handler exiting abnormally after the process
+     exits.  */
+  testProcess_sleep(1);
+  fprintf(stdout, "Output on stdout after grandchild test.\n");
+  fprintf(stderr, "Output on stderr after grandchild test.\n");
+  fflush(stdout);
+  fflush(stderr);
+  return r;
+}
+
 #if defined(_WIN32)
-  Sleep(15000);
+static BOOL WINAPI test9_grandchild_handler(DWORD dwCtrlType)
+{
+  /* Ignore all Ctrl+C/Break signals.  We must use an actual handler function
+     instead of using SetConsoleCtrlHandler(NULL, TRUE) so that we can also
+     ignore Ctrl+Break in addition to Ctrl+C.  */
+  (void)dwCtrlType;
+  return TRUE;
+}
+#endif
+
+static int test9_grandchild(int argc, const char* argv[])
+{
+  /* The grandchild just sleeps for a few seconds while ignoring signals.  */
+  (void)argc; (void)argv;
+#if defined(_WIN32)
+  if(!SetConsoleCtrlHandler(test9_grandchild_handler, TRUE))
+    {
+    return 1;
+    }
 #else
-  sleep(15);
+  struct sigaction sa;
+  memset(&sa, 0, sizeof(sa));
+  sa.sa_handler = SIG_IGN;
+  sigemptyset(&sa.sa_mask);
+  if(sigaction(SIGINT, &sa, 0) < 0)
+    {
+    return 1;
+    }
 #endif
+  fprintf(stdout, "Output on stdout from grandchild before sleep.\n");
+  fprintf(stderr, "Output on stderr from grandchild before sleep.\n");
+  fflush(stdout);
+  fflush(stderr);
+  /* Sleep for 9 seconds.  */
+  testProcess_sleep(9);
+  fprintf(stdout, "Output on stdout from grandchild after sleep.\n");
+  fprintf(stderr, "Output on stderr from grandchild after sleep.\n");
+  fflush(stdout);
+  fflush(stderr);
+  return 0;
+}
+
+static int test10(int argc, const char* argv[])
+{
+  /* Test Ctrl+C behavior: the root test program will send a Ctrl+C to this
+     process.  Here, we start a child process that sleeps for a long time and
+     processes signals normally.  However, this grandchild is created in a new
+     process group - ensuring that Ctrl+C we receive is sent to our process
+     groups.  We make sure it exits anyway.  */
+  int r;
+  const char* cmd[4];
+  (void)argc;
+  cmd[0] = argv[0];
+  cmd[1] = "run";
+  cmd[2] = "110";
+  cmd[3] = 0;
+  fprintf(stdout, "Output on stdout before grandchild test.\n");
+  fprintf(stderr, "Output on stderr before grandchild test.\n");
+  fflush(stdout);
+  fflush(stderr);
+  r = runChild(cmd, kwsysProcess_State_Exception,
+               kwsysProcess_Exception_Interrupt,
+               0, 1, 1, 0, 30, 0, 1, 0, 1, 0);
+  fprintf(stdout, "Output on stdout after grandchild test.\n");
+  fprintf(stderr, "Output on stderr after grandchild test.\n");
+  fflush(stdout);
+  fflush(stderr);
+  return r;
+}
+
+static int test10_grandchild(int argc, const char* argv[])
+{
+  /* The grandchild just sleeps for a few seconds and handles signals.  */
+  (void)argc; (void)argv;
+  fprintf(stdout, "Output on stdout from grandchild before sleep.\n");
+  fprintf(stderr, "Output on stderr from grandchild before sleep.\n");
+  fflush(stdout);
+  fflush(stderr);
+  /* Sleep for 6 seconds.  */
+  testProcess_sleep(6);
+  fprintf(stdout, "Output on stdout from grandchild after sleep.\n");
+  fprintf(stderr, "Output on stderr from grandchild after sleep.\n");
+  fflush(stdout);
+  fflush(stderr);
   return 0;
 }
 
 static int runChild2(kwsysProcess* kp,
               const char* cmd[], int state, int exception, int value,
               int share, int output, int delay, double timeout,
-              int poll, int disown)
+              int poll, int disown, int createNewGroup,
+              unsigned int interruptDelay)
 {
   int result = 0;
   char* data = 0;
@@ -249,6 +382,10 @@ static int runChild2(kwsysProcess* kp,
     {
     kwsysProcess_SetOption(kp, kwsysProcess_Option_Detach, 1);
     }
+  if(createNewGroup)
+    {
+    kwsysProcess_SetOption(kp, kwsysProcess_Option_CreateProcessGroup, 1);
+    }
   kwsysProcess_Execute(kp);
 
   if(poll)
@@ -256,6 +393,12 @@ static int runChild2(kwsysProcess* kp,
     pUserTimeout = &userTimeout;
     }
 
+  if(interruptDelay)
+    {
+    testProcess_sleep(interruptDelay);
+    kwsysProcess_Interrupt(kp);
+    }
+
   if(!share && !disown)
     {
     int p;
@@ -286,17 +429,13 @@ static int runChild2(kwsysProcess* kp,
       if(poll)
         {
         /* Delay to avoid busy loop during polling.  */
-#if defined(_WIN32)
-        Sleep(100);
-#else
         testProcess_usleep(100000);
-#endif
         }
       if(delay)
         {
         /* Purposely sleeping only on Win32 to let pipe fill up.  */
 #if defined(_WIN32)
-        Sleep(100);
+        testProcess_usleep(100000);
 #endif
         }
       }
@@ -337,7 +476,7 @@ static int runChild2(kwsysProcess* kp,
       printf("Error in administrating child process: [%s]\n",
              kwsysProcess_GetErrorString(kp)); break;
     };
-  
+
   if(result)
     {
     if(exception != kwsysProcess_GetExitException(kp))
@@ -353,7 +492,7 @@ static int runChild2(kwsysProcess* kp,
               value, kwsysProcess_GetExitValue(kp));
       }
     }
-  
+
   if(kwsysProcess_GetState(kp) != state)
     {
     fprintf(stderr, "Mismatch in state.  "
@@ -374,9 +513,37 @@ static int runChild2(kwsysProcess* kp,
   return result;
 }
 
+/**
+ * Runs a child process and blocks until it returns.  Arguments as follows:
+ *
+ * cmd            = Command line to run.
+ * state          = Expected return value of kwsysProcess_GetState after exit.
+ * exception      = Expected return value of kwsysProcess_GetExitException.
+ * value          = Expected return value of kwsysProcess_GetExitValue.
+ * share          = Whether to share stdout/stderr child pipes with our pipes
+ *                  by way of kwsysProcess_SetPipeShared.  If false, new pipes
+ *                  are created.
+ * output         = If !share && !disown, whether to write the child's stdout
+ *                  and stderr output to our stdout.
+ * delay          = If !share && !disown, adds an additional short delay to
+ *                  the pipe loop to allow the pipes to fill up; Windows only.
+ * timeout        = Non-zero to sets a timeout in seconds via
+ *                  kwsysProcess_SetTimeout.
+ * poll           = If !share && !disown, we count the number of 0.1 second
+ *                  intervals where the child pipes had no new data.  We fail
+ *                  if not in the bounds of MINPOLL/MAXPOLL.
+ * repeat         = Number of times to run the process.
+ * disown         = If set, the process is disowned.
+ * createNewGroup = If set, the process is created in a new process group.
+ * interruptDelay = If non-zero, number of seconds to delay before
+ *                  interrupting the process.  Note that this delay will occur
+ *                  BEFORE any reading/polling of pipes occurs and before any
+ *                  detachment occurs.
+ */
 int runChild(const char* cmd[], int state, int exception, int value,
              int share, int output, int delay, double timeout,
-             int poll, int repeat, int disown)
+             int poll, int repeat, int disown, int createNewGroup,
+             unsigned int interruptDelay)
 {
   int result = 1;
   kwsysProcess* kp = kwsysProcess_New();
@@ -388,7 +555,8 @@ int runChild(const char* cmd[], int state, int exception, 
int value,
   while(repeat-- > 0)
     {
     result = runChild2(kp, cmd, state, exception, value, share,
-                       output, delay, timeout, poll, disown);
+                       output, delay, timeout, poll, disown, createNewGroup,
+                       interruptDelay);
     }
   kwsysProcess_Delete(kp);
   return result;
@@ -435,7 +603,7 @@ int main(int argc, const char* argv[])
     n = atoi(argv[2]);
     }
   /* Check arguments.  */
-  if(((n >= 1 && n <= 8) || n == 108) && argc == 3)
+  if(((n >= 1 && n <= 10) || n == 108 || n == 109 || n == 110) && argc == 3)
     {
     /* This is the child process for a requested test number.  */
     switch (n)
@@ -448,15 +616,19 @@ int main(int argc, const char* argv[])
       case 6: test6(argc, argv); return 0;
       case 7: return test7(argc, argv);
       case 8: return test8(argc, argv);
+      case 9: return test9(argc, argv);
+      case 10: return test10(argc, argv);
       case 108: return test8_grandchild(argc, argv);
+      case 109: return test9_grandchild(argc, argv);
+      case 110: return test10_grandchild(argc, argv);
       }
     fprintf(stderr, "Invalid test number %d.\n", n);
     return 1;
     }
-  else if(n >= 1 && n <= 8)
+  else if(n >= 1 && n <= 10)
     {
     /* This is the parent process for a requested test number.  */
-    int states[8] =
+    int states[10] =
     {
       kwsysProcess_State_Exited,
       kwsysProcess_State_Exited,
@@ -465,9 +637,11 @@ int main(int argc, const char* argv[])
       kwsysProcess_State_Exited,
       kwsysProcess_State_Expired,
       kwsysProcess_State_Exited,
-      kwsysProcess_State_Exited
+      kwsysProcess_State_Exited,
+      kwsysProcess_State_Expired, /* Ctrl+C handler test */
+      kwsysProcess_State_Exception /* Process group test */
     };
-    int exceptions[8] =
+    int exceptions[10] =
     {
       kwsysProcess_Exception_None,
       kwsysProcess_Exception_None,
@@ -476,14 +650,19 @@ int main(int argc, const char* argv[])
       kwsysProcess_Exception_None,
       kwsysProcess_Exception_None,
       kwsysProcess_Exception_None,
-      kwsysProcess_Exception_None
+      kwsysProcess_Exception_None,
+      kwsysProcess_Exception_None,
+      kwsysProcess_Exception_Interrupt
     };
-    int values[8] = {0, 123, 1, 1, 0, 0, 0, 0};
-    int outputs[8] = {1, 1, 1, 1, 1, 0, 1, 1};
-    int delays[8] = {0, 0, 0, 0, 0, 1, 0, 0};
-    double timeouts[8] = {10, 10, 10, 30, 30, 10, -1, 10};
-    int polls[8] = {0, 0, 0, 0, 0, 0, 1, 0};
-    int repeat[8] = {2, 1, 1, 1, 1, 1, 1, 1};
+    int values[10] = {0, 123, 1, 1, 0, 0, 0, 0, 1, 1};
+    int shares[10] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1};
+    int outputs[10] = {1, 1, 1, 1, 1, 0, 1, 1, 1, 1};
+    int delays[10] = {0, 0, 0, 0, 0, 1, 0, 0, 0, 0};
+    double timeouts[10] = {10, 10, 10, 30, 30, 10, -1, 10, 6, 4};
+    int polls[10] = {0, 0, 0, 0, 0, 0, 1, 0, 0, 0};
+    int repeat[10] = {2, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+    int createNewGroups[10] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1};
+    unsigned int interruptDelays[10] = {0, 0, 0, 0, 0, 0, 0, 0, 3, 2};
     int r;
     const char* cmd[4];
 #ifdef _WIN32
@@ -515,9 +694,10 @@ int main(int argc, const char* argv[])
     fprintf(stderr, "Output on stderr before test %d.\n", n);
     fflush(stdout);
     fflush(stderr);
-    r = runChild(cmd, states[n-1], exceptions[n-1], values[n-1], 0,
+    r = runChild(cmd, states[n-1], exceptions[n-1], values[n-1], shares[n-1],
                  outputs[n-1], delays[n-1], timeouts[n-1],
-                 polls[n-1], repeat[n-1], 0);
+                 polls[n-1], repeat[n-1], 0, createNewGroups[n-1],
+                 interruptDelays[n-1]);
     fprintf(stdout, "Output on stdout after test %d.\n", n);
     fprintf(stderr, "Output on stderr after test %d.\n", n);
     fflush(stdout);
@@ -536,7 +716,8 @@ int main(int argc, const char* argv[])
     int exception = kwsysProcess_Exception_None;
     int value = 0;
     double timeout = 0;
-    int r = runChild(cmd, state, exception, value, 0, 1, 0, timeout, 0, 1, 0);
+    int r = runChild(cmd, state, exception, value, 0, 1, 0, timeout,
+      0, 1, 0, 0, 0);
     return r;
     }
   else

-----------------------------------------------------------------------

Summary of changes:


hooks/post-receive
-- 
CMake
_______________________________________________
Cmake-commits mailing list
Cmake-commits@cmake.org
http://public.kitware.com/mailman/listinfo/cmake-commits

Reply via email to