Fixed bug 4692 - Command line parsing Galadrim As I have seen, SDL implements its own command line parser for Windows in SDL_windows_main.c. Unfortunately, it doesn't seem to allow command line arguments with trailing backslashes if quoting is required. Usually, when you write an application that gets command line arguments passed as argc and argv, the parsing is done by parse_cmdline. The Windows API also provides the function CommandLineToArgvW, so an application can parse itself if only the command line string is provided. Both functions behave almost identically according to their documentation. If the argument "\\" (including the quotes) is passed, they both turn it into a single backslash. The SDL command line parser on the other hand doesn't recognize the second quote character as the closing character in this example and therefore includes it in the parsed argument. The parser does not count the number of backslashes preceding a quote. It always treats a quote as escaped if a backslash is in front of it. Therefore, it should be impossible to quote and escape an argument correctly, if it has a trailing backslash and contains characters that require quoting. Of course, each application is allowed to implement its own parsing rules, so SDL is free to do so. But the problem I see is that there are arguments, that are impossible to be passed to the parser correctly, as I described above. Is there a reason, why SDL does not simply use CommandLineToArgvW instead of implementing its own parser? Here are some links that show that correct argument parsing, as it is usually done in Windows, is quite complicated: https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-commandlinetoargvw http://www.windowsinspired.com/how-a-windows-programs-splits-its-command-line-into-individual-arguments/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
diff --git a/src/main/windows/SDL_windows_main.c b/src/main/windows/SDL_windows_main.c
index 32f6727..aa1f7d3 100644
--- a/src/main/windows/SDL_windows_main.c
+++ b/src/main/windows/SDL_windows_main.c
@@ -9,6 +9,7 @@
/* Include this so we define UNICODE properly */
#include "../../core/windows/SDL_windows.h"
+#include <shellapi.h> /* CommandLineToArgvW() */
/* Include the SDL main definition header */
#include "SDL.h"
@@ -18,87 +19,7 @@
# undef main
#endif /* main */
-static void
-UnEscapeQuotes(char *arg)
-{
- char *last = NULL;
-
- while (*arg) {
- if (*arg == '"' && (last != NULL && *last == '\\')) {
- char *c_curr = arg;
- char *c_last = last;
-
- while (*c_curr) {
- *c_last = *c_curr;
- c_last = c_curr;
- c_curr++;
- }
- *c_last = '\0';
- }
- last = arg;
- arg++;
- }
-}
-
-/* Parse a command line buffer into arguments */
-static int
-ParseCommandLine(char *cmdline, char **argv)
-{
- char *bufp;
- char *lastp = NULL;
- int argc, last_argc;
-
- argc = last_argc = 0;
- for (bufp = cmdline; *bufp;) {
- /* Skip leading whitespace */
- while (*bufp == ' ' || *bufp == '\t') {
- ++bufp;
- }
- /* Skip over argument */
- if (*bufp == '"') {
- ++bufp;
- if (*bufp) {
- if (argv) {
- argv[argc] = bufp;
- }
- ++argc;
- }
- /* Skip over word */
- lastp = bufp;
- while (*bufp && (*bufp != '"' || *lastp == '\\')) {
- lastp = bufp;
- ++bufp;
- }
- } else {
- if (*bufp) {
- if (argv) {
- argv[argc] = bufp;
- }
- ++argc;
- }
- /* Skip over word */
- while (*bufp && (*bufp != ' ' && *bufp != '\t')) {
- ++bufp;
- }
- }
- if (*bufp) {
- if (argv) {
- *bufp = '\0';
- }
- ++bufp;
- }
-
- /* Strip out \ from \" sequences */
- if (argv && last_argc != argc) {
- UnEscapeQuotes(argv[last_argc]);
- }
- last_argc = argc;
- }
- if (argv) {
- argv[argc] = NULL;
- }
- return (argc);
-}
+#define WIN_WStringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(WCHAR))
/* Pop up an out of memory message, returns to Windows */
static BOOL
@@ -119,65 +40,44 @@ OutOfMemory(void)
/* Gets the arguments with GetCommandLine, converts them to argc and argv
and calls SDL_main */
static int
-main_getcmdline()
+main_getcmdline(void)
{
+ LPWSTR *argvw;
char **argv;
- int argc;
- char *cmdline = NULL;
- int retval = 0;
- int cmdalloc = 0;
- const TCHAR *text = GetCommandLine();
- const TCHAR *ptr;
- int argc_guess = 2; /* space for NULL and initial argument. */
- int rc;
-
- /* make a rough guess of command line arguments. Overestimates if there
- are quoted things. */
- for (ptr = text; *ptr; ptr++) {
- if ((*ptr == ' ') || (*ptr == '\t')) {
- argc_guess++;
- }
- }
+ int i, argc;
+ int result;
-#if UNICODE
- rc = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL);
- if (rc > 0) {
- cmdalloc = rc + (sizeof (char *) * argc_guess);
- argv = (char **) VirtualAlloc(NULL, cmdalloc, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
- if (argv) {
- int rc2;
- cmdline = (char *) (argv + argc_guess);
- rc2 = WideCharToMultiByte(CP_UTF8, 0, text, -1, cmdline, rc, NULL, NULL);
- SDL_assert(rc2 == rc);
- }
- }
-#else
- /* !!! FIXME: are these in the system codepage? We need to convert to UTF-8. */
- rc = ((int) SDL_strlen(text)) + 1;
- cmdalloc = rc + (sizeof (char *) * argc_guess);
- argv = (char **) VirtualAlloc(NULL, cmdalloc, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
- if (argv) {
- cmdline = (char *) (argv + argc_guess);
- SDL_strcpy(cmdline, text);
- }
-#endif
- if (cmdline == NULL) {
+ argvw = CommandLineToArgvW(GetCommandLineW(), &argc);
+ if (argvw == NULL) {
return OutOfMemory();
}
/* Parse it into argv and argc */
- SDL_assert(ParseCommandLine(cmdline, NULL) <= argc_guess);
- argc = ParseCommandLine(cmdline, argv);
+ argv = (char **)SDL_calloc(argc + 1, sizeof(*argv));
+ if (!argv) {
+ return OutOfMemory();
+ }
+ for (i = 0; i < argc; ++i) {
+ argv[i] = WIN_WStringToUTF8(argvw[i]);
+ if (!argv[i]) {
+ return OutOfMemory();
+ }
+ }
+ argv[i] = NULL;
+ LocalFree(argvw);
SDL_SetMainReady();
/* Run the application main() code */
- retval = SDL_main(argc, argv);
+ result = SDL_main(argc, argv);
- VirtualFree(argv, cmdalloc, MEM_DECOMMIT);
- VirtualFree(argv, 0, MEM_RELEASE);
+ /* Free argv, to avoid memory leak */
+ for (i = 0; i < argc; ++i) {
+ SDL_free(argv[i]);
+ }
+ SDL_free(argv);
- return retval;
+ return result;
}
/* This is where execution begins [console apps, ansi] */