Rewrite the LCC process spawning code on Windows, to handle file names containing spaces

This commit is contained in:
Tim Angus 2025-08-12 19:26:36 +01:00
parent f03a012087
commit 787bdba0ec

View File

@ -221,64 +221,81 @@ char *basename(char *name) {
} }
#ifdef WIN32 #ifdef WIN32
#include <process.h> #include <windows.h>
static char *escapeDoubleQuotes(const char *string) { static char *quoteArgument(const char *arg) {
int stringLength = strlen(string); // Quote if it has spaces, tabs, or is empty
int bufferSize = stringLength + 1; if(!*arg || strpbrk(arg, " \t\"")) {
int i, j; size_t length = strlen(arg);
char *newString; size_t bufferSize = length * 2 + 3; // maximum escapes + quotes + terminator
char *buffer = (char *)malloc(bufferSize);
char *p = buffer;
if (string == NULL) *p++ = '"'; // Open quote
return NULL;
for (i = 0; i < stringLength; i++) { for(size_t i = 0; i < length; i++) {
if (string[i] == '"') if(arg[i] == '"') {
bufferSize++; // Escape quotes
*p++ = '\\';
*p++ = '"';
} else {
// Everything else
*p++ = arg[i];
}
}
*p++ = '"'; // Close quote
*p = '\0';
return buffer;
} }
newString = (char*)malloc(bufferSize); // Duping to make memory management easier
return _strdup(arg);
if (newString == NULL)
return NULL;
for (i = 0, j = 0; i < stringLength; i++) {
if (string[i] == '"')
newString[j++] = '\\';
newString[j++] = string[i];
}
newString[j] = '\0';
return newString;
} }
static int spawn(const char *cmdname, char **argv) { static int spawn(const char *cmdname, char **argv) {
int argc = 0; size_t totalLength = 0;
char **newArgv = argv; for(int i = 0; argv[i] != NULL; i++) {
int i; char *quotedArg = quoteArgument(argv[i]);
intptr_t exitStatus; totalLength += strlen(quotedArg) + 1;
free(quotedArg);
}
// _spawnvp removes double quotes from arguments, so we char *cmdline = (char *)malloc(totalLength + 1);
// have to escape them manually cmdline[0] = '\0';
while (*newArgv++ != NULL)
argc++;
newArgv = (char **)malloc(sizeof(char*) * (argc + 1)); for(int i = 0; argv[i] != NULL; i++) {
char *quotedArg = quoteArgument(argv[i]);
strcat(cmdline, quotedArg);
if(argv[i+1]) strcat(cmdline, " ");
free(quotedArg);
}
for (i = 0; i < argc; i++) STARTUPINFOA si = { sizeof(si) };
newArgv[i] = escapeDoubleQuotes(argv[i]); PROCESS_INFORMATION pi;
BOOL result = CreateProcessA(
cmdname, cmdline,
NULL, NULL, FALSE,
0, NULL, NULL,
&si, &pi);
newArgv[argc] = NULL; if(!result) {
fprintf(stderr, "CreateProcess failed (%lu)\n", GetLastError());
free(cmdline);
return -1;
}
exitStatus = _spawnvp(_P_WAIT, cmdname, (const char *const *)newArgv); WaitForSingleObject(pi.hProcess, INFINITE);
for (i = 0; i < argc; i++) DWORD exit_code;
free(newArgv[i]); GetExitCodeProcess(pi.hProcess, &exit_code);
free(newArgv); CloseHandle(pi.hProcess);
return exitStatus; CloseHandle(pi.hThread);
free(cmdline);
return (int)exit_code;
} }
#else #else