The previous code had few issues: - Didn't quote an empty string. - Didn't quote if the string contains tabs. - Didn't take into account existing backslashes at the string. E.g. previously broken (in POSIX sh, like cygwin or busybox-w32): - tcc -m 32 test.c -D 'CSTR="foo'$'\t''bar"' (wrongly rejected by tcc). - tcc -m 32 test.c -D 'CSTR="foo\"bar"' (incorrect CSTR at test.c). Both issues are fixed with the new code, and presumably any others. The empty/tabs issues could be fixed within the current code, but the backslashes issue is not worth shoehorning it, so this is a rewrite.
Unified · Split
| @@ -497,21 +497,38 @@ ST_FUNC int tcc_tool_cross(TCCState *s1, char **argv, int option) · 496 unmodified lines | ||
| 497 | 497 | #ifdef _WIN32 |
| 498 | 498 | #include <process.h> |
| 499 | 499 | |
| 500 | −/* quote quotes in string and quote string if it contains spaces */ | |
| 501 | −static char *quote_win32(const char *s0) | |
| 500 | +/* - Empty argument or with space/tab (not newline) requires quoting. | |
| 501 | + * - Double-quotes at the value require '\'-escape, retardless of quoting. | |
| 502 | + * - Consecutive (or 1) backslashes at the value all need '\'-escape only if | |
| 503 | + * followed by [escaped] double quote, else taken literally, e.g. <x\\y\> | |
| 504 | + * remains literal without quoting or esc, but <x\\"y\> becomes <x\\\\\"y\>. | |
| 505 | + * - This "before double quote" rule applies also before delimiting quoting, | |
| 506 | + * e.g. <x\y \"z\> becomes <"x\y \\\"z\\"> (quoting required because space). | |
| 507 | + * | |
| 508 | + * https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments | |
| 509 | + */ | |
| 510 | +static char *quote_win32(const char *s) | |
| 502 | 511 | { |
| 503 | − const char *s; | |
| 504 | − char *p, *q; | |
| 505 | − int a = 0, b = 0, c; | |
| 506 | − for (s = s0; !!(c = *s); ++s) | |
| 507 | − a += c == '"', b |= c == ' '; | |
| 508 | − q = p = tcc_malloc(s - s0 + a + b + b + 1); | |
| 509 | − *q = '"', q += b; | |
| 510 | − for (s = s0; !!(c = *s); *q++ = c, ++s) | |
| 511 | − if (c == '"') | |
| 512 | − *q++ = '\\'; | |
| 513 | − *q = '"', q += b, *q = '\0'; | |
| 514 | − return p; | |
| 512 | + char *o, *r = tcc_malloc(2 * strlen(s) + 3); /* max-esc, quotes, \0 */ | |
| 513 | + int cbs = 0, quoted = !*s; /* consecutive backslashes before current */ | |
| 514 | + | |
| 515 | + for (o = r; *s; *o++ = *s++) { | |
| 516 | + quoted |= *s == ' ' || *s == '\t'; | |
| 517 | + if (*s == '\\' || *s == '"') | |
| 518 | + *o++ = '\\'; | |
| 519 | + else | |
| 520 | + o -= cbs; /* undo cbs escpaes, if any (not followed by DQ) */ | |
| 521 | + cbs = *s == '\\' ? cbs + 1 : 0; | |
| 522 | + } | |
| 523 | + if (quoted) { | |
| 524 | + memmove(r + 1, r, o++ - r); | |
| 525 | + *r = *o++ = '"'; | |
| 526 | + } else { | |
| 527 | + o -= cbs; | |
| 528 | + } | |
| 529 | + | |
| 530 | + *o = 0; | |
| 531 | + return r; /* don't bother with realloc(r, o-r+1) */ | |
| 515 | 532 | } |
| 516 | 533 | |
| 517 | 534 | static int execvp_win32(const char *prog, char **argv) |