Dan Osborne wrote, quoting Greg Chicares:
>> [snip discussion of replacement for system()]
>>
>> Perhaps, as an alternative, one could write a program that reads
>> its arguments, changes "/c" to "-c", and passes the rest to a
>> shell it invokes; and then set ComSpec to that program's name.
>>
>> I haven't tried this, and it seems silly, yet unintrusive; if it
>> works, then it might be used even when the source of the calling
>> program is unavailable.
>
> But would the "quoting problem" still remain?
Yes, it would.
>> I don't know of a quoting problem that can't be solved by placing
>> the command in double quotes, e.g.
>> sh -c "your commands here"
>> but that's not to say there isn't one:
There is! It occurs when any of the individual command line args
have embedded white space, or literal double quotes -- simply
wrapping the line in one pair of double quotes isn't sufficient
to preserve the proper grouping.
But, Greg's idea is perhaps not all that silly. I *have* tried it,
at least conceptually using a `dumpargs' tool as the intermediate
program, and I believe it could work:--
<file name="tryit.c>
#include <stdlib.h>
int main()
{
putenv( "COMSPEC=D:/MSYS/1.0/home/keith/dumpargs.exe" );
system( "echo \"hello everybody,\" \"how are you.\"" );
return 0;
}
</file
<file name="dumpargs.c>
#include <stdio.h>
int main( int argc, char **argv)
{
int i = 0;
while( argc-- )
printf( "argv[%d]: %s\n", i++, *argv++ );
return 0;
}
</file>
<output>
$ ./tryit
argv[0]: D:/MSYS/1.0/home/keith/dumpargs.exe
argv[1]: /c
argv[2]: echo
argv[3]: hello everybody,
argv[4]: how are you.
</output>
So, a real intermediate program would need to:--
1) replace argv[0] with the path for the shell it is going to
invoke, (perhaps use getenv( "SHELL" )).
2) replace argv[1] with "-c".
3) insert appropriate quoting for argv[2]..argv[n], (n == 4, in
the above example), following the quoting rules kindly stated
by Jeremy Bettis, (more on this below).
4) EITHER ...
concatenate all of argv[0]..argv[n] into a single string,
inserting a space between each, and pass this string as the
command to execute, in a CreateProcess() call,
OR...
concatenate argv[2]..argv[n] in like fashion, assigning the
result to argv[2]; set *argv[3] = NULL, perform a *second*
round of requoting for argv[2], as in step 3, and hand off
the reassembled argv to _spawnv( _P_WAIT, *argv[0], argv).
This is essentially what my set of wrapper functions do, without
the overhead of the intermediate process fork. Taking spawnlp()
as an example:--
int spawnlp_wrapper( int mode, char *prog, char *arg, ...)
{
return spawnvp_wrapper( mode, prog, &arg );
}
int spawnvp_wrapper( int mode, char *prog, char **argv )
{
char **quoted_argv = make_quoted_argv( argv );
int status = _spawnvp( mode, prog, quoted_argv );
release_quoted_argv( quoted_argv );
return status;
}
In reality, I use macros to generate the entire family of spawn
and exec functions, from just a single pair of templates -- that,
together with the detail of the make_quoted_argv() function, and
its complementary "destructor", release_quoted_argv(), are GPL,
(and I have assigned copyright to FSF). However, the quoting
implementation does closely follow the "rules" stated by Jeremy
Bettis, which I reproduce here, with my own clarifying comments:
> I assume here that the result will be passed to CreateProcess.
>
> CommandLine = all args (including exe name) joined by spaces.
> for each arg
> If the arg contains any of newline, tab, space or " (double
> quote), then quote it.
That is, make a copy of it to the assembled CommandLine, starting
starting with a separating space, (if not the first arg), then one
literal " char, append the text of the arg, with any additional
internal adjustment as required, (see below), and append a further
literal " to finish.
> If the arg is a null string, then quote it.
So, literally append ` ""' to the assembled CommandLine, for any
*arg == '\0'.
> If the arg contains double back slash \\, then quote it.
Not sure about this one, but it can't hurt. I haven't found it
necessary to quote, UNLESS the arg contains embedded white space
or double quotes. Any sequence of LITERAL backslashes, regardless
of how many, should be copied VERBATIM to the assembled CommandLine,
whether the arg is quoted or not, BUT when quoting, any sequence of
`n' backslashes must be copied as `2*n+1' when they are immediately
followed by a literal double quote, or by `2*n' when immediately
followed by the terminal '\0' of the ARG (NOT just the LINE).
> Quoting in this context means:
> " before and after
As in my comment above.
> if you find a \, then look at the next non-\ character.
> If it is a " or the end of the string, then replace \ with \\
This isn't correct, (at least not as I would interpret it); see my
previous comment on handling backslashes.
> embedded " becomes \"
And this is included in the `2n+1', in my description above.
Also note that MSVCRT *doesn't* assign any special significance to
single quotes; just copy them literally, when you see them.
> So:
>
> foo -> foo
> foo bar -> "foo bar"
> foo\bar -> foo\bar
> foo\ bar -> "foo\ bar"
> foo"bar -> "foo\"bar"
> foo\\bar -> foo\\bar
> fo o\\bar -> "fo o\\bar"
> foo bar\ -> "foo bar\\"
> fo\\o\\\"ba\\\r -> "fo\\o\\\\\\"ba\\\r"
This last example is incorrect; there should be one more backslash
before the `"bar', in the quoted string.
> See the documentation for CommandLineToArgvW for more infomation.
>
> Then call CreateProcess(0, CommandLine, .....). I always call
> CreateProcess since I don't trust exec or spawn to properly quote
> the arguments.
Since I don't develop for Windoze, I prefer spawn -- CreateProcess
is too M$ specific, and spawn's similarity to exec makes it generally
more suitable for use in cross platform code, IMHO. But, Jeremy is
correct -- spawn does't quote properly, but my wrapper functions
fix this.
BTW, the broken quoting isn't the only problem with M$'s [broken]
exec() implementation; it breaks the process hierarchy as well!
(Try waiting for a child process which calls exec() -- you can't
do it, because the exec() just calls CreateProcess(), creating a
*detached* process, and the parent stops waiting prematurely).
My wrappers don't fix this problem -- yet; I simply use spawn
instead, as a work around.
Regards,
Keith.
|