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 | #ifdef _WIN32 | 497 | #ifdef _WIN32 |
| 498 | #include <process.h> | 498 | #include <process.h> |
| 499 | 499 | ||
| 500 | /* quote quotes in string and quote string if it contains spaces */ | 500 | /* - Empty argument or with space/tab (not newline) requires quoting. |
| 501 | static char *quote_win32(const char *s0) | 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; | 512 | char *o, *r = tcc_malloc(2 * strlen(s) + 3); /* max-esc, quotes, \0 */ |
| 504 | char *p, *q; | 513 | int cbs = 0, quoted = !*s; /* consecutive backslashes before current */ |
| 505 | int a = 0, b = 0, c; | 514 | |
| 506 | for (s = s0; !!(c = *s); ++s) | 515 | for (o = r; *s; *o++ = *s++) { |
| 507 | a += c == '"', b |= c == ' '; | 516 | quoted |= *s == ' ' || *s == '\t'; |
| 508 | q = p = tcc_malloc(s - s0 + a + b + b + 1); | 517 | if (*s == '\\' || *s == '"') |
| 509 | *q = '"', q += b; | 518 | *o++ = '\\'; |
| 510 | for (s = s0; !!(c = *s); *q++ = c, ++s) | 519 | else |
| 511 | if (c == '"') | 520 | o -= cbs; /* undo cbs escpaes, if any (not followed by DQ) */ |
| 512 | *q++ = '\\'; | 521 | cbs = *s == '\\' ? cbs + 1 : 0; |
| 513 | *q = '"', q += b, *q = '\0'; | 522 | } |
| 514 | return p; | 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 | static int execvp_win32(const char *prog, char **argv) | 534 | static int execvp_win32(const char *prog, char **argv) |