diff --git a/src/flutter_pty_win.c b/src/flutter_pty_win.c index b1f85bc..97ba67b 100644 --- a/src/flutter_pty_win.c +++ b/src/flutter_pty_win.c @@ -7,22 +7,76 @@ #include "include/dart_api_dl.h" #include "include/dart_native_api.h" +// Windows' CreateProcessW takes the whole command line as one string, so any +// token containing a space or tab must be quoted or it splits into several +// argv entries for the child (and, worse, can throw off CreateProcessW's own +// search for where the executable name ends when lpApplicationName is NULL - +// e.g. "C:\Program Files\Git\bin\bash.exe" without quotes). Windows filenames +// cannot contain a double quote, so a token that needs quoting here never +// contains one that would itself need escaping. +static int needs_quoting(const char *token) +{ + if (token == NULL || token[0] == 0) + { + return 1; + } + + for (int i = 0; token[i] != 0; i++) + { + if (token[i] == ' ' || token[i] == '\t') + { + return 1; + } + } + + return 0; +} + +static int quoted_length(char *token) +{ + int length = (int)strlen(token); + return needs_quoting(token) ? length + 2 : length; +} + +static void append_token(LPWSTR command, int *pos, char *token) +{ + int quote = needs_quoting(token); + + if (quote) + { + command[(*pos)++] = L'"'; + } + + for (int j = 0; token[j] != 0; j++) + { + command[(*pos)++] = (WCHAR)token[j]; + } + + if (quote) + { + command[(*pos)++] = L'"'; + } +} + static LPWSTR build_command(char *executable, char **arguments) { int command_length = 0; if (executable != NULL) { - command_length += (int)strlen(executable); + command_length += quoted_length(executable); } - if (arguments != NULL) + // arguments[0] duplicates executable - flutter_pty's Dart side always + // prepends it so Unix's execvp() gets a conventional argv[0], but here + // it would otherwise be written out a second time. Start at index 1. + if (arguments != NULL && arguments[0] != NULL) { - int i = 0; + int i = 1; while (arguments[i] != NULL) { - command_length += (int)strlen(arguments[i]) + 1; + command_length += quoted_length(arguments[i]) + 1; i++; } } @@ -31,42 +85,26 @@ static LPWSTR build_command(char *executable, char **arguments) if (command != NULL) { - int i = 0; + int pos = 0; if (executable != NULL) { - int j = 0; - - while (executable[j] != 0) - { - command[i] = (WCHAR)executable[j]; - i++; - j++; - } + append_token(command, &pos, executable); } - if (arguments != NULL) + if (arguments != NULL && arguments[0] != NULL) { - int j = 0; + int i = 1; - while (arguments[j] != NULL) + while (arguments[i] != NULL) { - command[i++] = ' '; - - int k = 0; - - while (arguments[j][k] != 0) - { - command[i] = (WCHAR)arguments[j][k]; - i++; - k++; - } - - j++; + command[pos++] = L' '; + append_token(command, &pos, arguments[i]); + i++; } } - command[i] = 0; + command[pos] = 0; } return command; @@ -467,4 +505,4 @@ FFI_PLUGIN_EXPORT int pty_getpid(PtyHandle *handle) FFI_PLUGIN_EXPORT char *pty_error() { return error_message; -} +} \ No newline at end of file