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