Skip to content

Commit

Permalink
w32: improve quoting when spawning cross-compiler
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
avih committed Nov 22, 2024
1 parent a6ef318 commit 1cf33fe
Showing 1 changed file with 31 additions and 14 deletions.
45 changes: 31 additions & 14 deletions tcctools.c
Original file line number Diff line number Diff line change
Expand Up @@ -497,21 +497,38 @@ ST_FUNC int tcc_tool_cross(TCCState *s1, char **argv, int option)
#ifdef _WIN32
#include <process.h>

/* quote quotes in string and quote string if it contains spaces */
static char *quote_win32(const char *s0)
/* - Empty argument or with space/tab (not newline) requires quoting.
* - Double-quotes at the value require '\'-escape, retardless of quoting.
* - Consecutive (or 1) backslashes at the value all need '\'-escape only if
* followed by [escaped] double quote, else taken literally, e.g. <x\\y\>
* remains literal without quoting or esc, but <x\\"y\> becomes <x\\\\\"y\>.
* - This "before double quote" rule applies also before delimiting quoting,
* e.g. <x\y \"z\> becomes <"x\y \\\"z\\"> (quoting required because space).
*
* https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments
*/
static char *quote_win32(const char *s)
{
const char *s;
char *p, *q;
int a = 0, b = 0, c;
for (s = s0; !!(c = *s); ++s)
a += c == '"', b |= c == ' ';
q = p = tcc_malloc(s - s0 + a + b + b + 1);
*q = '"', q += b;
for (s = s0; !!(c = *s); *q++ = c, ++s)
if (c == '"')
*q++ = '\\';
*q = '"', q += b, *q = '\0';
return p;
char *o, *r = tcc_malloc(2 * strlen(s) + 3); /* max-esc, quotes, \0 */
int cbs = 0, quoted = !*s; /* consecutive backslashes before current */

for (o = r; *s; *o++ = *s++) {
quoted |= *s == ' ' || *s == '\t';
if (*s == '\\' || *s == '"')
*o++ = '\\';
else
o -= cbs; /* undo cbs escpaes, if any (not followed by DQ) */
cbs = *s == '\\' ? cbs + 1 : 0;
}
if (quoted) {
memmove(r + 1, r, o++ - r);
*r = *o++ = '"';
} else {
o -= cbs;
}

*o = 0;
return r; /* don't bother with realloc(r, o-r+1) */
}

static int execvp_win32(const char *prog, char **argv)
Expand Down

0 comments on commit 1cf33fe

Please sign in to comment.