Module: wine
Branch: master
Commit: f252e9dfc85303d645ec545f238fd8ee11ff2497
URL: http://source.winehq.org/git/wine.git/?a=commit;h=f252e9dfc85303d645ec545f2…
Author: Jason Edmeades <jason(a)edmeades.me.uk>
Date: Wed Oct 3 00:50:13 2012 +0100
cmd: Change command line parsing away from argv/argc.
---
programs/cmd/tests/test_builtins.cmd | 7 +-
programs/cmd/tests/test_builtins.cmd.exp | 22 ++--
programs/cmd/wcmdmain.c | 185 +++++++++---------------------
3 files changed, 76 insertions(+), 138 deletions(-)
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd
index 3e064b9..bdd6cd0 100644
--- a/programs/cmd/tests/test_builtins.cmd
+++ b/programs/cmd/tests/test_builtins.cmd
@@ -1861,7 +1861,12 @@ cmd.exe /c " echo passed2 "
echo --- Test 14
cmd.exe /c "dir /ad ..\fooba* /b"
echo --- Test 15
-
+cmd.exe /cecho No whitespace
+echo --- Test 16
+cmd.exe /c
+echo --- Test 17
+cmd.exe /c@space@
+echo --- Test 18
rem Ensure no interactive prompting when cmd.exe /c or /k
echo file2 > file2
cmd.exe /c copy file1 file2 >nul
diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp
index 6483f1e..1d004b7 100644
--- a/programs/cmd/tests/test_builtins.cmd.exp
+++ b/programs/cmd/tests/test_builtins.cmd.exp
@@ -910,23 +910,23 @@ value1
------------ cmd.exe command lines ------------
--- Test 1
Line1
-@todo_wine@"Line2"
+"Line2"
--- Test 2
-@todo_wine@Test quotes "&" work
+Test quotes "&" work
--- Test 3
-@todo_wine@"&"
+"&"
--- Test 4
-@todo_wine@"<"
+"<"
--- Test 5
-@todo_wine@">"
+">"
--- Test 6
-@todo_wine@"\"
+"\"
--- Test 7
-@todo_wine@"|"
+"|"
--- Test 8
-@todo_wine@"`"
+"`"
--- Test 9
-@todo_wine@"""
+"""
--- Test 10
--- Test 11
--- Test 12
@@ -936,6 +936,10 @@ passed2@space@
--- Test 14
foobar
--- Test 15
+No whitespace
+--- Test 16
+--- Test 17
+--- Test 18
No prompts or I would not get here1
No prompts or I would not get here2
%hello1%
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index 675256b..3c18356 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -2300,7 +2300,9 @@ void WCMD_free_commands(CMD_LIST *cmds) {
int wmain (int argc, WCHAR *argvW[])
{
int args;
- WCHAR *cmd;
+ WCHAR *cmdLine = NULL;
+ WCHAR *cmd = NULL;
+ WCHAR *argPos = NULL;
WCHAR string[1024];
WCHAR envvar[4];
BOOL opt_q;
@@ -2318,19 +2320,26 @@ int wmain (int argc, WCHAR *argvW[])
LocalFree(cmd);
cmd = NULL;
- args = argc;
+ /* Can't use argc/argv as it will have stripped quotes from parameters
+ * meaning cmd.exe /C echo "quoted string" is impossible
+ */
+ cmdLine = GetCommandLineW();
+ WINE_TRACE("Full commandline '%s'\n", wine_dbgstr_w(cmdLine));
+ args = 1; /* start at first arg, skipping cmd.exe itself */
+
opt_c = opt_k = opt_q = opt_s = FALSE;
- while (args > 0)
+ WCMD_parameter(cmdLine, args, &argPos, NULL, TRUE);
+ while (argPos && argPos[0] != 0x00)
{
WCHAR c;
- WINE_TRACE("Command line parm: '%s'\n", wine_dbgstr_w(*argvW));
- if ((*argvW)[0]!='/' || (*argvW)[1]=='\0') {
- argvW++;
- args--;
+ WINE_TRACE("Command line parm: '%s'\n", wine_dbgstr_w(argPos));
+ if (argPos[0]!='/' || argPos[1]=='\0') {
+ args++;
+ WCMD_parameter(cmdLine, args, &argPos, NULL, TRUE);
continue;
}
- c=(*argvW)[1];
+ c=argPos[1];
if (tolowerW(c)=='c') {
opt_c = TRUE;
} else if (tolowerW(c)=='q') {
@@ -2343,19 +2352,20 @@ int wmain (int argc, WCHAR *argvW[])
unicodeOutput = FALSE;
} else if (tolowerW(c)=='u') {
unicodeOutput = TRUE;
- } else if (tolowerW(c)=='t' && (*argvW)[2]==':') {
- opt_t=strtoulW(&(*argvW)[3], NULL, 16);
+ } else if (tolowerW(c)=='t' && argPos[2]==':') {
+ opt_t=strtoulW(&argPos[3], NULL, 16);
} else if (tolowerW(c)=='x' || tolowerW(c)=='y') {
/* Ignored for compatibility with Windows */
}
- if ((*argvW)[2]==0) {
- argvW++;
- args--;
+ if (argPos[2]==0 || argPos[2]==' ' || argPos[2]=='\t') {
+ args++;
+ WCMD_parameter(cmdLine, args, &argPos, NULL, TRUE);
}
else /* handle `cmd /cnotepad.exe` and `cmd /x/c ...` */
{
- *argvW+=2;
+ /* Do not step to next paramater, instead carry on parsing this one */
+ argPos+=2;
}
if (opt_c || opt_k) /* break out of parsing immediately after c or k */
@@ -2371,64 +2381,50 @@ int wmain (int argc, WCHAR *argvW[])
interactive = FALSE;
if (opt_c || opt_k) {
- int len,qcount;
- WCHAR** arg;
- int argsLeft;
- WCHAR* p;
+ int len;
+ WCHAR *q1 = NULL,*q2 = NULL,*p;
+
+ /* Handle very edge case error scenarion, "cmd.exe /c" ie when there are no
+ * parameters after the /C or /K by pretending there was a single space */
+ if (argPos == NULL) argPos = (WCHAR *)spaceW;
+
+ /* Build the command to execute - It is what is left in argPos */
+ len = strlenW(argPos);
+
+ /* Take a copy */
+ cmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ if (!cmd)
+ exit(1);
+ strcpyW(cmd, argPos);
/* opt_s left unflagged if the command starts with and contains exactly
* one quoted string (exactly two quote characters). The quoted string
* must be an executable name that has whitespace and must not have the
* following characters: &<>()@^| */
- /* Build the command to execute */
- len = 0;
- qcount = 0;
- argsLeft = args;
- for (arg = argvW; argsLeft>0; arg++,argsLeft--)
- {
- BOOL has_space = FALSE;
- int bcount;
- WCHAR* a;
-
- bcount=0;
- a=*arg;
- if( !*a ) has_space = TRUE;
- while (*a!='\0') {
- if (*a=='\\') {
- bcount++;
- } else {
- if (*a==' ' || *a=='\t') {
- has_space = TRUE;
- } else if (*a=='"') {
- /* doubling of '\' preceding a '"',
- * plus escaping of said '"'
- */
- len+=2*bcount+1;
- qcount++;
- }
- bcount=0;
- }
- a++;
- }
- len+=(a-*arg) + 1; /* for the separating space */
- if (has_space)
- {
- len+=2; /* for the quotes */
- qcount+=2;
- }
+ if (!opt_s) {
+ /* 1. Confirm there is at least one quote */
+ q1 = strchrW(argPos, '"');
+ if (!q1) opt_s=1;
}
- /* If there is not exactly 2 quote characters, then /S (old behaviour) is enabled */
- if (qcount!=2)
- opt_s = TRUE;
+ if (!opt_s) {
+ /* 2. Confirm there is a second quote */
+ q2 = strchrW(q1+1, '"');
+ if (!q2) opt_s=1;
+ }
- /* check argvW[0] for a space and invalid characters. There must not be any invalid
- * characters, but there must be one or more whitespace */
+ if (!opt_s) {
+ /* 3. Ensure there are no more quotes */
+ if (strchrW(q2+1, '"')) opt_s=1;
+ }
+
+ /* check first parameter for a space and invalid characters. There must not be any
+ * invalid characters, but there must be one or more whitespace */
if (!opt_s) {
opt_s = TRUE;
- p=*argvW;
- while (*p!='\0') {
+ p=q1;
+ while (p!=q2) {
if (*p=='&' || *p=='<' || *p=='>' || *p=='(' || *p==')'
|| *p=='@' || *p=='^' || *p=='|') {
opt_s = TRUE;
@@ -2440,73 +2436,6 @@ int wmain (int argc, WCHAR *argvW[])
}
}
- cmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
- if (!cmd)
- exit(1);
-
- p = cmd;
- argsLeft = args;
- for (arg = argvW; argsLeft>0; arg++,argsLeft--)
- {
- BOOL has_space = FALSE, has_quote = FALSE;
- WCHAR* a;
-
- /* Check for quotes and spaces in this argument */
- a=*arg;
- if( !*a ) has_space = TRUE;
- while (*a!='\0') {
- if (*a==' ' || *a=='\t') {
- has_space = TRUE;
- if (has_quote)
- break;
- } else if (*a=='"') {
- has_quote = TRUE;
- if (has_space)
- break;
- }
- a++;
- }
-
- /* Now transfer it to the command line */
- if (has_space)
- *p++='"';
- if (has_quote) {
- int bcount;
- WCHAR* a;
-
- bcount=0;
- a=*arg;
- while (*a!='\0') {
- if (*a=='\\') {
- *p++=*a;
- bcount++;
- } else {
- if (*a=='"') {
- int i;
-
- /* Double all the '\\' preceding this '"', plus one */
- for (i=0;i<=bcount;i++)
- *p++='\\';
- *p++='"';
- } else {
- *p++=*a;
- }
- bcount=0;
- }
- a++;
- }
- } else {
- strcpyW(p,*arg);
- p+=strlenW(*arg);
- }
- if (has_space)
- *p++='"';
- *p++=' ';
- }
- if (p > cmd)
- p--; /* remove last space */
- *p = '\0';
-
WINE_TRACE("/c command line: '%s'\n", wine_dbgstr_w(cmd));
/* Finally, we only stay in new mode IF the first parameter is quoted and