Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:21796 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 84654 invoked by uid 1010); 6 Feb 2006 03:01:40 -0000 Delivered-To: ezmlm-scan-internals@lists.php.net Delivered-To: ezmlm-internals@lists.php.net Received: (qmail 84639 invoked from network); 6 Feb 2006 03:01:40 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 6 Feb 2006 03:01:40 -0000 X-Host-Fingerprint: 202.63.61.242 cust3058.vic01.dataco.com.au Received: from ([202.63.61.242:25689] helo=localhost.localdomain) by pb1.pair.com (ecelerity 2.0 beta r(6323M)) with SMTP id 89/3A-45475-11CB6E34 for ; Sun, 05 Feb 2006 22:01:37 -0500 Message-ID: <89.3A.45475.11CB6E34@pb1.pair.com> To: internals@lists.php.net Date: Mon, 06 Feb 2006 14:01:37 +1100 User-Agent: Mozilla Thunderbird 1.0 (Windows/20041206) X-Accept-Language: en-us, en MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------090805080209010701060801" X-Posted-By: 202.63.61.242 Subject: [PATCH] Bug #34671 Incorrect calling convention to cmd.exe From: t.starling@physics.unimelb.edu.au (Tim Starling) --------------090805080209010701060801 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit This patch is untested, my apologies for that. I don't have a working Visual C++ environment in which to compile PHP at the moment. I have, however, tested the concepts involved. I can also provide test cases and expected output if that would help. The problem is PHP's incorrect invocation of cmd.exe, which causes no end of trouble to those developing PHP applications on Windows systems. You can find many workarounds for it in the manual comments, but a permanent, robust fix is actually quite simple, at least for NT-based versions of Windows. Here is an extract of the output of "help cmd" on Windows XP: CMD [/A | /U] [/Q] [/D] [/E:ON | /E:OFF] [/F:ON | /F:OFF] [/V:ON | /V:OFF] [[/S] [/C | /K] string] /C Carries out the command specified by string and then terminates /K Carries out the command specified by string but remains [...] If /C or /K is specified, then the remainder of the command line after the switch is processed as a command line, where the following logic is used to process quote (") characters: 1. If all of the following conditions are met, then quote characters on the command line are preserved: - no /S switch - exactly two quote characters - no special characters between the two quote characters, where special is one of: &<>()@^| - there are one or more whitespace characters between the the two quote characters - the string between the two quote characters is the name of an executable file. 2. Otherwise, old behavior is to see if the first character is a quote character and if so, strip the leading character and remove the last quote character on the command line, preserving any text after the last quote character. [end quote] This provides a simple, robust means to execute a command via cmd.exe: cmd.exe /s /c "...command goes here..." Otherwise, the programmer risks being plunged into a world of heuristic pain that only a Microsoft developer could love. Some instructive examples: C:\>cmd /c "C:\Program Files\ImageMagick-6.2.6-Q16\convert.exe" Version: ImageMagick 6.2.6 01/27/06 Q16 http://www.imagemagick.org Copyright: Copyright (C) 1999-2006 ImageMagick Studio LLC Usage: convert.exe [options ...] file [ [options ...] file ...] [options ...] file [...] C:\>cmd /c "C:\Program Files\ImageMagick-6.2.6-Q16\convert.exe" -version Version: ImageMagick 6.2.6 01/27/06 Q16 http://www.imagemagick.org Copyright: Copyright (C) 1999-2006 ImageMagick Studio LLC C:\>cmd /c "C:\Program Files\ImageMagick-6.2.6-Q16\convert.exe" "some file" 'C:\Program' is not recognized as an internal or external command, operable program or batch file. C:\>cd "C:\Program Files\ImageMagick-6.2.6-Q16" C:\Program Files\ImageMagick-6.2.6-Q16>cmd /c "convert source dest" convert: unable to open image `source': No such file or directory. convert: missing an image filename `dest'. C:\Program Files\ImageMagick-6.2.6-Q16>echo @echo G'day > "convert source dest.bat" C:\Program Files\ImageMagick-6.2.6-Q16>cmd /c "convert source dest" G'day Positively scary. /s restores sanity to the situation: C:\Program Files\ImageMagick-6.2.6-Q16>cmd /s /c "convert source dest" convert: unable to open image `source': No such file or directory. convert: missing an image filename `dest'. Users of COMMAND.COM are not so blessed. There's no handling for double quotes at all, so if you want to execute a program with spaces in the name, you just have to use the 8.3 alias. In my patch, this is the responsibility of the application. C:\>command.com /s /c "echo hello" Invalid switch Bad command or file name The problem as it manifests itself in PHP was reported as Bug #34671, and closed as bogus by a trigger-happy admin. You can't work around it properly in application-level code, because you can't specify /s. -- Tim Starling --------------090805080209010701060801 Content-Type: text/plain; name="cmd_exe_invocation.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="cmd_exe_invocation.patch" Index: TSRM/tsrm_win32.c =================================================================== RCS file: /repository/TSRM/tsrm_win32.c,v retrieving revision 1.28 diff -u -r1.28 tsrm_win32.c --- TSRM/tsrm_win32.c 1 Jan 2006 13:09:48 -0000 1.28 +++ TSRM/tsrm_win32.c 6 Feb 2006 01:53:29 -0000 @@ -209,8 +209,15 @@ startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); } - cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /c ")); - sprintf(cmd, "%s /c %s", TWG(comspec), command); + if (GetVersion()<0x80000000) { + /* NT calling convention: quotes around everything, /s to disable heuristics */ + cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /s /c \"\"")); + sprintf(cmd, "%s /s /c \"%s\"", TWG(comspec), command); + } else { + /* 95/98/Me calling convention. Long names need to be converted to short names by the application. */ + cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /c ")); + sprintf(cmd, "%s /c %s", TWG(comspec), command); + } if (!CreateProcess(NULL, cmd, &security, &security, security.bInheritHandle, NORMAL_PRIORITY_CLASS, env, cwd, &startup, &process)) { return NULL; } --------------090805080209010701060801--