Fun with argv
Paul Millar
paulm at astro.gla.ac.uk
Sat Feb 16 12:30:54 CST 2002
Here's the patch, BTW.
ChangeLog:
Remove support for double quotes within command arguments as standard
backslash escaping is incompatible with Windows.
Paul.
On Sat, 16 Feb 2002, Paul Millar wrote:
> Hi everyone,
>
> I've been looking at the installer for Office97. You call setup.exe
> which runs acmsetup.exe with a bunch of options, specifically:
> 'E:\~MSSETUP.T\~msstfof.t\acmsetup /T Off97Pro.stf /S "D:\office\" '
>
> Here, I've used single quotes to delimit the string. I'll carry on this
> convention because it will get confusing otherwise.
>
> The problem is build_argv() assumes the command line is suitably escaped
> (as from a bash command line, for example), and so mangles the above line
> to the following values:
> 'E:\~MSSETUP.T\~msstfof.t\acmsetup'
> '/T'
> 'Off97Pro.stf'
> '/S'
> 'D:\office" '
>
> But, the fun continues as you can pass arguments to setup.exe, which
> augment the arguments passed to acmsetup.exe, so running:
>
> wine setup.exe 'a a' '"b "B'
>
> results in argv[] for setup.exe being:
> 'setup.exe'
> 'a a'
> '"b "B'
>
> setup.exe then calls GetCommandLineA, which returns:
> 'setup.exe "a a" "\"b \"B"'
>
> and then it tries to exec
> 'E:\~MSSETUP.T\~msstfof.t\acmsetup /T Off97Pro.stf /S "D:\office\" "a a"
> "\"b \"B"'
> A command line with both unix-style escaped quotes and normal text. The
> build_argv result is:
> 'E:\~MSSETUP.T\~msstfof.t\acmsetup'
> [...]
> '/S'
> 'D:\office" a'
> 'a "b'
> '"B'
>
> and subsequent call to GetCommandLineA returns:
> 'E:\~MSSETUP.T\~msstfof.t\acmsetup /T Off97Pro.stf /S "D:\office\" a" "a
> \"b" \"B'
>
> What a mess!
>
> >From what I can see, the problem is ENV_BuildCommandLine build a command
> line that is \-escaped (" => \" and \ => \\). Windows programs might use
> this as part of a WinExec call (as above), which in general is not
> \-escaped.
>
> The only way around this problem (I could think of) is to stop
> ENV_BuildCommandLine from \-escaping the command line. This implies that
> we cannot accept double-quotes on the command line as part of an argument.
>
> I have a patch which is a hack-and-slash to remove the \-escaping and the
> resulting build processes arguments consistently (and Office97 setup gets
> a bit further). However, someone's obviously gone to the trouble of coding
> in support for double-quotes, so is this needed? Is there an alternative
> solution?
>
> Cheers,
>
> Paul.
-------------- next part --------------
Index: memory/environ.c
===================================================================
RCS file: /home/wine/wine/memory/environ.c,v
retrieving revision 1.27
diff -u -r1.27 environ.c
--- memory/environ.c 2001/10/02 17:49:20 1.27
+++ memory/environ.c 2002/02/16 15:19:39
@@ -18,6 +18,11 @@
#include "ntddk.h"
#include "selectors.h"
+#include "debugtools.h"
+
+DEFAULT_DEBUG_CHANNEL(process);
+
+
/* Win32 process environment database */
typedef struct _ENVDB
{
@@ -175,51 +180,34 @@
* Note that it does NOT necessarily include the file name.
* Sometimes we don't even have any command line options at all.
*
- * We must quote and escape characters so that the argv array can be rebuilt
- * from the command line:
+ * We must quote characters so that the argv array can be rebuilt from
+ * the command line:
* - spaces and tabs must be quoted
* 'a b' -> '"a b"'
- * - quotes must be escaped
- * '"' -> '\"'
- * - if '\'s are followed by a '"', they must be doubled and followed by '\"',
- * resulting in an odd number of '\' followed by a '"'
- * '\"' -> '\\\"'
- * '\\"' -> '\\\\\"'
- * - '\'s that are not followed by a '"' can be left as is
- * 'a\b' == 'a\b'
- * 'a\\b' == 'a\\b'
+ * - any double quotes are removed as there is no method of escaping these
+ * in Windows
*/
BOOL ENV_BuildCommandLine( char **argv )
{
int len;
- char *p, **arg;
+ char *a, *p, **arg;
len = 0;
for (arg = argv; *arg; arg++)
{
- int has_space,bcount;
- char* a;
+ int has_space,qcount;
has_space=0;
- bcount=0;
- a=*arg;
- while (*a!='\0') {
- if (*a=='\\') {
- bcount++;
- } else {
- if (*a==' ' || *a=='\t') {
- has_space=1;
- } else if (*a=='"') {
- /* doubling of '\' preceeding a '"',
- * plus escaping of said '"'
- */
- len+=2*bcount+1;
- }
- bcount=0;
+ qcount=0;
+ for( a=*arg; *a != '\0'; a++) {
+ if (*a==' ' || *a=='\t') {
+ has_space=1;
+ } else if (*a == '"') {
+ WARN( "Quote detected in argument, ignoring it.\n");
+ qcount++;
}
- a++;
}
- len+=(a-*arg)+1 /* for the separating space */;
+ len+=(a-*arg)+1-qcount /* 1 for the separating space */;
if (has_space)
len+=2; /* for the quotes */
}
@@ -252,34 +240,10 @@
/* Now transfer it to the command line */
if (has_space)
*p++='"';
- if (has_quote) {
- int bcount;
- char* a;
-
- bcount=0;
- a=*arg;
- while (*a!='\0') {
- if (*a=='\\') {
- *p++=*a;
- bcount++;
- } else {
- if (*a=='"') {
- int i;
-
- /* Double all the '\\' preceeding this '"', plus one */
- for (i=0;i<=bcount;i++)
- *p++='\\';
- *p++='"';
- } else {
- *p++=*a;
- }
- bcount=0;
- }
- a++;
- }
- } else {
- strcpy(p,*arg);
- p+=strlen(*arg);
+
+ for( a=*arg; *a!='\0'; a++) {
+ if (*a!='"')
+ *p++=*a;
}
if (has_space)
*p++='"';
Index: scheduler/process.c
===================================================================
RCS file: /home/wine/wine/scheduler/process.c,v
retrieving revision 1.170
diff -u -r1.170 process.c
--- scheduler/process.c 2002/02/02 18:13:51 1.170
+++ scheduler/process.c 2002/02/16 15:19:45
@@ -585,10 +585,9 @@
int argc;
char** argv;
char *arg,*s,*d;
- int in_quotes,bcount;
+ int in_quotes;
argc=reserved+1;
- bcount=0;
in_quotes=0;
s=cmdline;
while (1) {
@@ -601,18 +600,9 @@
}
if (*s=='\0')
break;
- bcount=0;
continue;
- } else if (*s=='\\') {
- /* '\', count them */
- bcount++;
- } else if ((*s=='"') && ((bcount & 1)==0)) {
- /* unescaped '"' */
+ } else if (*s=='"') {
in_quotes=!in_quotes;
- bcount=0;
- } else {
- /* a regular character */
- bcount=0;
}
s++;
}
@@ -621,13 +611,12 @@
return NULL;
arg=d=s=cmdline;
- bcount=0;
in_quotes=0;
argc=reserved;
while (*s) {
if ((*s==' ' || *s=='\t') && !in_quotes) {
/* Close the argument and copy it */
- *d=0;
+ *d='\0';
argv[argc++]=arg;
/* skip the remaining spaces */
@@ -637,33 +626,12 @@
/* Start with a new argument */
arg=d=s;
- bcount=0;
- } else if (*s=='\\') {
- /* '\\' */
- *d++=*s++;
- bcount++;
} else if (*s=='"') {
- /* '"' */
- if ((bcount & 1)==0) {
- /* Preceeded by an even number of '\', this is half that
- * number of '\', plus a '"' which we discard.
- */
- d-=bcount/2;
- s++;
- in_quotes=!in_quotes;
- } else {
- /* Preceeded by an odd number of '\', this is half that
- * number of '\' followed by a '"'
- */
- d=d-bcount/2-1;
- *d++='"';
- s++;
- }
- bcount=0;
+ in_quotes=!in_quotes;
+ s++;
} else {
/* a regular character */
*d++=*s++;
- bcount=0;
}
}
if (*arg) {
More information about the wine-devel
mailing list