Module: wine Branch: master Commit: f252e9dfc85303d645ec545f238fd8ee11ff2497 URL: http://source.winehq.org/git/wine.git/?a=commit;h=f252e9dfc85303d645ec545f23...
Author: Jason Edmeades jason@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