diff options
Diffstat (limited to 'Source/kwsys/ProcessWin32.c')
-rw-r--r-- | Source/kwsys/ProcessWin32.c | 407 |
1 files changed, 248 insertions, 159 deletions
diff --git a/Source/kwsys/ProcessWin32.c b/Source/kwsys/ProcessWin32.c index 97e5706764..6929c3e443 100644 --- a/Source/kwsys/ProcessWin32.c +++ b/Source/kwsys/ProcessWin32.c @@ -104,8 +104,14 @@ static void kwsysProcessDestroy(kwsysProcess* cp, int event); static int kwsysProcessSetupOutputPipeFile(PHANDLE handle, const char* name); static int kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle); static void kwsysProcessCleanupHandle(PHANDLE h); +static void kwsysProcessCleanupHandleSafe(PHANDLE h, DWORD nStdHandle); static void kwsysProcessCleanup(kwsysProcess* cp, int error); static void kwsysProcessCleanErrorMessage(kwsysProcess* cp); +static int kwsysProcessComputeCommandLength(kwsysProcess* cp, + char const* const* command); +static void kwsysProcessComputeCommandLine(kwsysProcess* cp, + char const* const* command, + char* cmd); static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout, kwsysProcessTime* timeoutTime); static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime, @@ -205,6 +211,9 @@ struct kwsysProcess_s /* Whether to hide the child process's window. */ int HideWindow; + /* Whether to treat command lines as verbatim. */ + int Verbatim; + /* On Win9x platforms, the path to the forwarding executable. */ char* Win9x; @@ -645,7 +654,7 @@ int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command) char** newCommands; /* Make sure we have a command to add. */ - if(!cp || !command) + if(!cp || !command || !*command) { return 0; } @@ -675,66 +684,8 @@ int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command) 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; - int spaces = 0; - const char* c; - - /* Scan the string for spaces. If there are no spaces, we can - pass the argument verbatim. */ - for(c=*arg; *c; ++c) - { - if(*c == ' ' || *c == '\t') - { - spaces = 1; - break; - } - } - - /* Add the length of the argument, plus 1 for the space - separating the arguments. */ - length += (int)strlen(*arg) + 1; - - if(spaces) - { - /* Add 2 for double quotes since spaces are present. */ - length += 2; - - /* 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; - } - } + int length = kwsysProcessComputeCommandLength(cp, command); /* Allocate enough space for the command. We do not need an extra byte for the terminating null because we allocated a space for @@ -748,94 +699,8 @@ int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command) } /* Construct the command line in the allocated buffer. */ - cmd = newCommands[cp->NumberOfCommands]; - for(arg = command; *arg; ++arg) - { - /* Keep track of how many backslashes have been encountered in a - row in an argument. */ - int backslashes = 0; - int spaces = 0; - const char* c; - - /* Scan the string for spaces. If there are no spaces, we can - pass the argument verbatim. */ - for(c=*arg; *c; ++c) - { - if(*c == ' ' || *c == '\t') - { - spaces = 1; - break; - } - } - - /* Add the separating space if this is not the first argument. */ - if(arg != command) - { - *cmd++ = ' '; - } - - if(spaces) - { - /* 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 closing double-quote for this argument. */ - *cmd++ = '"'; - } - else - { - /* No spaces. Add the argument verbatim. */ - for(c=*arg; *c; ++c) - { - *cmd++ = *c; - } - } - } - - /* Add the terminating null character to the command line. */ - *cmd = 0; + kwsysProcessComputeCommandLine(cp, command, + newCommands[cp->NumberOfCommands]); } /* Save the new array of commands. */ @@ -967,6 +832,7 @@ int kwsysProcess_GetOption(kwsysProcess* cp, int optionId) { case kwsysProcess_Option_Detach: return cp->OptionDetach; case kwsysProcess_Option_HideWindow: return cp->HideWindow; + case kwsysProcess_Option_Verbatim: return cp->Verbatim; default: return 0; } } @@ -983,6 +849,7 @@ void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value) { case kwsysProcess_Option_Detach: cp->OptionDetach = value; break; case kwsysProcess_Option_HideWindow: cp->HideWindow = value; break; + case kwsysProcess_Option_Verbatim: cp->Verbatim = value; break; default: break; } } @@ -1145,7 +1012,8 @@ void kwsysProcess_Execute(kwsysProcess* cp) &si.StartupInfo.hStdError)) { kwsysProcessCleanup(cp, 1); - kwsysProcessCleanupHandle(&si.StartupInfo.hStdError); + kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError, + STD_ERROR_HANDLE); return; } } @@ -1166,9 +1034,12 @@ void kwsysProcess_Execute(kwsysProcess* cp) /* Release resources that may have been allocated for this process before an error occurred. */ kwsysProcessCleanupHandle(&readEnd); - kwsysProcessCleanupHandle(&si.StartupInfo.hStdInput); - kwsysProcessCleanupHandle(&si.StartupInfo.hStdOutput); - kwsysProcessCleanupHandle(&si.StartupInfo.hStdError); + kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdInput, + STD_INPUT_HANDLE); + kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdOutput, + STD_OUTPUT_HANDLE); + kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError, + STD_ERROR_HANDLE); kwsysProcessCleanupHandle(&si.ErrorPipeRead); kwsysProcessCleanupHandle(&si.ErrorPipeWrite); return; @@ -1183,7 +1054,7 @@ void kwsysProcess_Execute(kwsysProcess* cp) processes in the pipeline. The stdout and stdin pipes are not shared among all children and are therefore closed by kwsysProcessCreate after each child is created. */ - kwsysProcessCleanupHandle(&si.StartupInfo.hStdError); + kwsysProcessCleanupHandleSafe(&si.StartupInfo.hStdError, STD_ERROR_HANDLE); /* Restore the working directory. */ if(cp->RealWorkingDirectory) @@ -1493,7 +1364,7 @@ void kwsysProcess_Kill(kwsysProcess* cp) /* Make sure we are executing a process. */ if(!cp || cp->State != kwsysProcess_State_Executing || cp->TimeoutExpired || - cp->Killed || cp->Terminated) + cp->Killed) { return; } @@ -1501,6 +1372,12 @@ void kwsysProcess_Kill(kwsysProcess* cp) /* Disable the reading threads. */ kwsysProcessDisablePipeThreads(cp); + /* Skip actually killing the child if it has already terminated. */ + if(cp->Terminated) + { + return; + } + /* Kill the children. */ cp->Killed = 1; if(cp->Win9x) @@ -1897,8 +1774,10 @@ int kwsysProcessCreate(kwsysProcess* cp, int index, process's copies of the inherited stdout and stdin handles. The stderr handle is shared among all children and is closed by kwsysProcess_Execute after all children have been created. */ - kwsysProcessCleanupHandle(&si->StartupInfo.hStdInput); - kwsysProcessCleanupHandle(&si->StartupInfo.hStdOutput); + kwsysProcessCleanupHandleSafe(&si->StartupInfo.hStdInput, + STD_INPUT_HANDLE); + kwsysProcessCleanupHandleSafe(&si->StartupInfo.hStdOutput, + STD_OUTPUT_HANDLE); return 1; } @@ -1984,13 +1863,27 @@ int kwsysProcessSetupOutputPipeFile(PHANDLE phandle, const char* name) /*--------------------------------------------------------------------------*/ int kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle) { + /* Check whether the handle to be shared is already inherited. */ + DWORD flags; + int inherited = 0; + if(GetHandleInformation(GetStdHandle(nStdHandle), &flags) && + (flags & HANDLE_FLAG_INHERIT)) + { + inherited = 1; + } + /* Cleanup the previous handle. */ kwsysProcessCleanupHandle(handle); - /* Duplicate the standard handle to be sure it is inherited and can - be closed later. Do not close the original handle when + /* If the standard handle is not inherited then duplicate it to + create an inherited copy. Do not close the original handle when duplicating! */ - if(DuplicateHandle(GetCurrentProcess(), GetStdHandle(nStdHandle), + if(inherited) + { + *handle = GetStdHandle(nStdHandle); + return 1; + } + else if(DuplicateHandle(GetCurrentProcess(), GetStdHandle(nStdHandle), GetCurrentProcess(), handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) { @@ -2046,6 +1939,19 @@ void kwsysProcessCleanupHandle(PHANDLE h) /*--------------------------------------------------------------------------*/ +/* Close the given handle if it is open and not a standard handle. + Reset its value to 0. */ +void kwsysProcessCleanupHandleSafe(PHANDLE h, DWORD nStdHandle) +{ + if(h && *h && (*h != GetStdHandle(nStdHandle))) + { + CloseHandle(*h); + *h = 0; + } +} + +/*--------------------------------------------------------------------------*/ + /* Close all handles created by kwsysProcess_Execute. */ void kwsysProcessCleanup(kwsysProcess* cp, int error) { @@ -2159,6 +2065,189 @@ void kwsysProcessCleanErrorMessage(kwsysProcess* cp) } /*--------------------------------------------------------------------------*/ +int kwsysProcessComputeCommandLength(kwsysProcess* cp, + char const* const* command) +{ + int length = 0; + if(cp->Verbatim) + { + /* Treat the first argument as a verbatim command line. Use its + length directly and add space for the null-terminator. */ + length = (int)strlen(*command)+1; + } + else + { + /* Compute the length of the command line when it is converted to + a single string. Space for the null-terminator is allocated by + the whitespace character allocated for the first argument that + will not be used. */ + char const* const* arg; + for(arg = command; *arg; ++arg) + { + /* Keep track of how many backslashes have been encountered in a + row in this argument. */ + int backslashes = 0; + int spaces = 0; + const char* c; + + /* Scan the string for spaces. If there are no spaces, we can + pass the argument verbatim. */ + for(c=*arg; *c; ++c) + { + if(*c == ' ' || *c == '\t') + { + spaces = 1; + break; + } + } + + /* Add the length of the argument, plus 1 for the space + separating the arguments. */ + length += (int)strlen(*arg) + 1; + + if(spaces) + { + /* Add 2 for double quotes since spaces are present. */ + length += 2; + + /* 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; + } + } + } + + return length; +} + +/*--------------------------------------------------------------------------*/ +void kwsysProcessComputeCommandLine(kwsysProcess* cp, + char const* const* command, + char* cmd) +{ + if(cp->Verbatim) + { + /* Copy the verbatim command line into the buffer. */ + strcpy(cmd, *command); + } + else + { + /* Construct the command line in the allocated buffer. */ + char const* const* arg; + for(arg = command; *arg; ++arg) + { + /* Keep track of how many backslashes have been encountered in a + row in an argument. */ + int backslashes = 0; + int spaces = 0; + const char* c; + + /* Scan the string for spaces. If there are no spaces, we can + pass the argument verbatim. */ + for(c=*arg; *c; ++c) + { + if(*c == ' ' || *c == '\t') + { + spaces = 1; + break; + } + } + + /* Add the separating space if this is not the first argument. */ + if(arg != command) + { + *cmd++ = ' '; + } + + if(spaces) + { + /* 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 closing double-quote for this argument. */ + *cmd++ = '"'; + } + else + { + /* No spaces. Add the argument verbatim. */ + for(c=*arg; *c; ++c) + { + *cmd++ = *c; + } + } + } + + /* Add the terminating null character to the command line. */ + *cmd = 0; + } +} + +/*--------------------------------------------------------------------------*/ /* 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, |