On Thu, 10 Jan 2002, Uwe Bonnes wrote:
Hallo,
some application builds a commandline with arguments enclosed by quotes '"', starts a daughter process with this commandline and relies on the quotes being intact in the daughter process. Wine happily removed the quotes and so the daughter process failed. Appended patch keeps quotes intact, tested via CreateProcess and calling a programm from the shell. When invoking wine from the shell, quotes need to be escaped, like any other special characters.
No. Your patch mishandles quotes.
Normal behavior: $ ./createp Type the command to execute: main a"b "c"d\" e CreateProcess returned: 1 error=0 GetCommandLine=[main a"b c"d\ e] main -> argc=4 0 [main] 1 [a"b] 2 [c"d] 3 [e]
With your patch: main a"b "c"d\" e CreateProcess returned: 1 error=0 GetCommandLine=[main "a\b "c\d\"" e] main -> argc=3 0 [main] 1 [a\b "c\d"] 2 [e] main -> __argc=3 0 [main] 1 [a\b "c\d"] 2 [e]
With your patch we: * change the command line from what was specified, just as before * add quotes where there are none * misinterpret quotes * don't split arguments properly anymore
The existing quote handling is perfect! I tested and retested it. The problem you are having is that we cannot garantee that the child process will get the exact same commandline because of the cmdline -> argv -> cmdline conversion. The fix for your problem is to pass the command line to the child process via a wineserver call or something.
Guess which is the first test I am going to write with the new testing framework!!! (and I would much prefer to print the contents of argv to file than to try and hard-code all the checks in the child, plus how am I going to know which was the command line that was given to me anyway!)
-- Francois Gouget fgouget@free.fr http://fgouget.free.fr/ In a world without fences who needs Gates?
On Thu, 10 Jan 2002, Francois Gouget wrote:
On Thu, 10 Jan 2002, Uwe Bonnes wrote:
Hallo,
some application builds a commandline with arguments enclosed by quotes '"', starts a daughter process with this commandline and relies on the quotes being intact in the daughter process. Wine happily removed the quotes and so the daughter process failed. Appended patch keeps quotes intact, tested via CreateProcess and calling a programm from the shell. When invoking wine from the shell, quotes need to be escaped, like any other special characters.
No. Your patch mishandles quotes.
Hmm. If so, here's some stuff from the WineX tree (because SafeDisc needs the quotes to be preserved in the child process's command line), at least what's left of it after the last WineHQ merge. If it still works, is it better than Uwe's patch?
Gavriel State gav@transgaming.com Preserve exact command line - including quotes - used when creating a process. Certain copy-protection software expects this...
diff -u wine/scheduler/process.c:1.1.1.23 wine/scheduler/process.c:1.11 --- wine/scheduler/process.c:1.1.1.23 Mon Dec 31 02:52:36 2001 +++ wine/scheduler/process.c Mon Dec 31 08:07:18 2001 @@ -476,14 +476,34 @@
if (!main_exe_name[0]) { + char *argv_noquotes; + DWORD argv_len; + if (!app_argv[0]) OPTIONS_Usage();
- /* open the exe file */ - if (!SearchPathA( NULL, app_argv[0], ".exe", sizeof(main_exe_name), main_exe_name, NULL) && - !SearchPathA( NULL, app_argv[0], NULL, sizeof(main_exe_name), main_exe_name, NULL)) + /* Remove any quotes around the file name, without modifying argv[0] */ + argv_len = strlen(app_argv[0]); + if ((argv_noquotes = HeapAlloc( GetProcessHeap(), 0, argv_len+1 ))) { - MESSAGE( "%s: cannot find '%s'\n", argv0, app_argv[0] ); - goto error; + char *p = app_argv[0]; + char *q; + /* If there are quotes around the *full* argument only, remove them */ + if ((*p == '"') && (q = strchr( p + 1, '"' )) && (q == (p+argv_len-1))) + { + strncpy(argv_noquotes, p+1, argv_len-2); + argv_noquotes[argv_len-2] = 0; + } + else + strcpy(argv_noquotes, p); + + /* open the exe file */ + if (!SearchPathA( NULL, argv_noquotes, ".exe", sizeof(main_exe_name), main_exe_name, NULL ) && + !SearchPathA( NULL, argv_noquotes, NULL, sizeof(main_exe_name), main_exe_name, NULL )) + { + MESSAGE( "%s: cannot find '%s'\n", argv0, argv[0] ); + goto error; + } + HeapFree( GetProcessHeap(), 0, argv_noquotes); } }
"Francois" == Francois Gouget fgouget@free.fr writes:
Francois> On Thu, 10 Jan 2002, Uwe Bonnes wrote: >> Hallo, >> >> some application builds a commandline with arguments enclosed by >> quotes '"', starts a daughter process with this commandline and >> relies on the quotes being intact in the daughter process. Wine >> happily removed the quotes and so the daughter process failed. >> Appended patch keeps quotes intact, tested via CreateProcess and >> calling a programm from the shell. When invoking wine from the shell, >> quotes need to be escaped, like any other special characters.
Francois> No. Your patch mishandles quotes.
Francois> Normal behavior: $ ./createp Type the command to execute: main Francois> a"b "c"d\" e CreateProcess returned: 1 error=0 Francois> GetCommandLine=[main a"b c"d\ e] main -> argc=4 0 [main] 1 Francois> [a"b] 2 [c"d] 3 [e]
Show me what lpCmdLine your test program passes to CreateProsess. Are you sure createp code doesn't misshandle things. Trying with my commandline test program, it looks right: : > wine -- cmdline.exe a"b "c"d\" e : fixme:win32:PE_CreateModule Security directory ignored : "a"b c"d\ e" The first and last quotes are added by my program.
E.g. tcsh chokes on your argument arangement (but tcsh isn't _the_ reference:-): : > wine -- cmdline.exe a"b "c"d\" e : Überflüssiges ". "Überflüssiges" == Surplus
Francois> With your patch: main a"b "c"d\" e CreateProcess returned: Francois> 1 error=0 GetCommandLine=[main "a\b "c\d\"" e] main -> argc=3 Francois> 0 [main] 1 [a\b "c\d"] 2 [e] main -> __argc=3 0 [main] 1 [a\b Francois> "c\d"] 2 [e]
Francois> With your patch we: * change the command line from what was Francois> specified, just as before * add quotes where there are none * Francois> misinterpret quotes * don't split arguments properly anymore
Francois> The existing quote handling is perfect!
Run a.exe of the tests appended and see the difference. Run against the DOS Shell in WinXX(/Vmware) to see what should happen.
Francois> I tested and Francois> retested it. The problem you are having is that we cannot Francois> garantee that the child process will get the exact same Francois> commandline because of the cmdline -> argv -> cmdline Francois> conversion. The fix for your problem is to pass the command Francois> line to the child process via a wineserver call or something.
Francois> Guess which is the first test I am going to write with the Francois> new testing framework!!! (and I would much prefer to print Francois> the contents of argv to file than to try and hard-code all the Francois> checks in the child, plus how am I going to know which was the Francois> command line that was given to me anyway!
Contend of my tests: caller.c: Programm that calls cmdline via CreateProcess cmdline.c: Print the arguments ucmdline.c: prints the arguments( Unix-File)
On Thu, 10 Jan 2002, Uwe Bonnes wrote:
"Francois" == Francois Gouget fgouget@free.fr writes:
[...]
Show me what lpCmdLine your test program passes to CreateProsess.
The lpCmdline parameter is exactly as it was printed in my email. This is why createp uses 'gets' to retrieve it:
main a"b "c"d\" e
That's if I run it as Winelib applications but if you run it in Windows you will see:
H:\tests\cmdline\Debug>createp Type the command to execute: main a"b "c"d\" e CreateProcess returned: 1 error=0 GetCommandLine=[main a"b "c"d\" e] main -> argc=4 0 [main] 1 [a"b] 2 [c"d] 3 [e]
Which you will notice is identical.
Are you sure createp code doesn't misshandle things. Trying with my commandline test program, it looks right: : > wine -- cmdline.exe a"b "c"d\" e : fixme:win32:PE_CreateModule Security directory ignored : "a"b c"d\ e" The first and last quotes are added by my program.
If I run wine -- cmdline.exe a"b "c"d\" e then it works. That's because in that case Wine knows the exact command line and does not call BUILD_CommandLine. But now, modify your caller.c program to pass the above arguments (or use my createp program) and your will get:
""a\b "c\d\"" e"
In that case Wine: * added extra quotes. This is not wrong, but it is no better than always removing extraneous quotes either * it also misinterpreted the " between a and b. This is a bug.
The difference between 'wine --' and CreateProcess is that in the latter case Wine has to rebuild the command line from the argv array.
E.g. tcsh chokes on your argument arangement (but tcsh isn't _the_ reference:-):
Strange. But I don't use C shells... Bash likes it just fine.
[...]
Run a.exe of the tests appended and see the difference. Run against the DOS Shell in WinXX(/Vmware) to see what should happen.
What your test program shows is that we currently loose extraneous quotes. It is impossible to guess what the original command line is from an argv array: argv[1]=ab can come from any of the following command lines (using [] to delimit the command line): [ab] [ab ] trailing spaces can be significant ["ab"] [""ab] [""ab] [ab""] [ab"" ] [a"b] (in the above the " is not closed but it does not matter here)
There are many more possibilities as soon as you have more than one argument (one or more spaces between args, quotes for one, the other or both, etc.). But I guess you get the idea.
I really don't think that systematically adding quotes around arguments is going to help. In fact I suspect that many applications would not handle them correctly.
The only reasonable I see to improve on the current code is to pass the command line as it was specified to CreateProcess, directly to the child. There two ways to do so: via an environment variable, or via the wineserver. Using an environment variable is not very clean, especially since either the parent or the child may not be Wine applications. To make matters even more complex the child may not be a Wine process but the grand-child may be one and then it should not use the command line that was intended for its parent. Passing the command line via the wineserver code seems like the best approach.
Note, you should be able to find createp and my other tests program in the wine-dev or wine-patches archives. I cannot give you the exact URL because my internet connection is down but the following should help you find it:
Date: Sun, 2 Sep 2001 19:37:43 -0700 (PDT) From: Francois Gouget fgouget@free.fr To: wine-devel@winehq.com Subject: ' ' & '"' handling in the command line
-- Francois Gouget fgouget@free.fr http://fgouget.free.fr/ War doesn't determine who's right. War determines who's left.