Mingw uses the exec* functions in the MSVCRT runtime. (By definition.)
What you are seeing is just how MSVCRT works.
MSVCRT contains in addition to the ANSI C functionality some Unixish
functions, like exec*. However, Windows is not Unix, and many of the
Unixish functions in MSVCRT differ in various ways from how they work
on Unix. The argument passing mechanism to child processes is one
thing that is very different between Windows and Unix.
1) Strictly speaking, in Unix you don't pass arguments to child
processes, but to a new program that will replace the current one
running the current process, in the exec*() call. Child processes are
created with fork().
In Windows, there is no separate fork() and exec(), but one call,
CreateProcess(), that runs the specified program as a child process.
However, in your case this detail doesn't really matter.
2) in Unix arguments are passed in exec*() as an argument vector. The
kernel knows this, it grabs the vector from the execing program, and
passes it unmodified to the new program being execed.
In Windows, the low-level API (CreateProcess()) doesn't use an
argument vector, but a command line string. Argument vectors are
something that is implemented in the C runtime (MSVCRT). The Win32
kernel knows nothing about them.
The C runtime builds a single command line in exec*() from the passed
argument vector, passed that to the Win32 kernel in CreateProcess(),
and then in the new process, the C runtime tries to reconstruct the
original argument vector from the command line.
The code that builds the command line from argv doesn't look into each
argv member. It simply concatenates them. The code that reconstructs
an argv from a command line does recognize some quoting, though. But
even if it superficially seems to use the same characters for quoting
(double quotes, backslashes), its quoting rules are not at all like
that used by Unix shells when they parse a command line into
I.e., if you want to pass argv members with funny stuff in them (like
spaces, backslashes, or quotes), you need to very carefully mangle
them before passing to exec*(), so that the argv reconstruction will
produce the intended result.
Not to make things too easy, the program name (the first element on
the command line), is parsed using different quoting rules...
The following comment is from GLib's gspawn-win32.c:
/* Quote each argv element if necessary, so that it will get
* reconstructed correctly in the C runtime startup code. Note that
* the unquoting algorithm in the C runtime is really weird, and
* rather different than what Unix shells do. See stdargv.c in the C
* runtime sources (in the Platform SDK, in src/crt).
* Note that an new_argv constructed by this function should
* *not* be passed as the filename argument to a spawn* or exec*
* family function. That argument should be the real file name
* without any quoting.
Also note that the Windows command interpreters (various versions of
command.com and cmd.exe) do not use Unix quoting rules either.
In order to fully understand what's happening, you really need to look
into the C runtime sources (as supplied with the freely downloadable
Platform SDK, for instance). Especially, see execve.c, cenvarg.c,
dospawn.c, crt0.c, wincmdln.c and stdargv.c. But, *importantly*,
please note that one should not quote snippets from those sources on
this mailing list, and not really even discuss implementation details
directly learned from the sources, but only information available in