cmd: Ignore quotes when parsing command line parameters.

This fixes a hang in the WinTV 8.5 installer.

Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
feature/deterministic
Zebediah Figura 2020-04-25 22:53:38 -05:00 committed by Alexandre Julliard
parent 16c938cb66
commit 2e45fdb194
3 changed files with 68 additions and 60 deletions

View File

@ -183,12 +183,19 @@ echo ------- Testing CMD /C qualifier treatment ------------
rem no need for space after /c rem no need for space after /c
cmd /csay one cmd /csay one
cmd /c"say one" cmd /c"say one"
rem ignore quote before qualifier cmd /c"say one
rem FIXME the next command in wine starts a sub-CMD cmd /c=say one
echo THIS FAILS: cmd "/c"say one cmd /c,say one
rem ignore anything before /c cmd /c;say one
rem FIXME the next command in wine starts a sub-CMD rem non-options are ignored before /c; quotes are not treated specially
echo THIS FAILS: cmd ignoreme/c say one cmd "/c"say one
cmd ignoreme/c say one
cmd abc "def ghi/c say one"
cmd -\@$*'"/c say one
echo echo bar > foo.bat
cmd /qq/c foo
cmd /q "xyz /c foo"
del foo.bat
echo --------- Testing special characters -------------- echo --------- Testing special characters --------------
echo @echo amp > "say&.bat" echo @echo amp > "say&.bat"

View File

@ -90,8 +90,16 @@ Passed
------- Testing CMD /C qualifier treatment ------------ ------- Testing CMD /C qualifier treatment ------------
0@space@ 0@space@
1@space@ 1@space@
THIS FAILS: cmd "/c"say one 0@space@
THIS FAILS: cmd ignoreme/c say one 0@space@
0@space@
0@space@
0@space@
0@space@
0@space@
0@space@
bar@space@
bar@space@
--------- Testing special characters -------------- --------- Testing special characters --------------
0@space@ 0@space@
0@space@ 0@space@

View File

@ -2421,10 +2421,8 @@ void WCMD_free_commands(CMD_LIST *cmds) {
int __cdecl wmain (int argc, WCHAR *argvW[]) int __cdecl wmain (int argc, WCHAR *argvW[])
{ {
int args;
WCHAR *cmdLine = NULL; WCHAR *cmdLine = NULL;
WCHAR *cmd = NULL; WCHAR *cmd = NULL;
WCHAR *argPos = NULL;
WCHAR string[1024]; WCHAR string[1024];
WCHAR envvar[4]; WCHAR envvar[4];
BOOL promptNewLine = TRUE; BOOL promptNewLine = TRUE;
@ -2440,6 +2438,7 @@ int __cdecl wmain (int argc, WCHAR *argvW[])
RTL_OSVERSIONINFOEXW osv; RTL_OSVERSIONINFOEXW osv;
char osver[50]; char osver[50];
STARTUPINFOW startupInfo; STARTUPINFOW startupInfo;
const WCHAR *arg;
if (!GetEnvironmentVariableW(comspecW, comspec, ARRAY_SIZE(comspec))) if (!GetEnvironmentVariableW(comspecW, comspec, ARRAY_SIZE(comspec)))
{ {
@ -2467,57 +2466,55 @@ int __cdecl wmain (int argc, WCHAR *argvW[])
*/ */
cmdLine = GetCommandLineW(); cmdLine = GetCommandLineW();
WINE_TRACE("Full commandline '%s'\n", wine_dbgstr_w(cmdLine)); WINE_TRACE("Full commandline '%s'\n", wine_dbgstr_w(cmdLine));
args = 0;
while (*cmdLine && *cmdLine != '/') ++cmdLine;
opt_c = opt_k = opt_q = opt_s = FALSE; opt_c = opt_k = opt_q = opt_s = FALSE;
WCMD_parameter(cmdLine, args, &argPos, TRUE, TRUE);
while (argPos && argPos[0] != 0x00) for (arg = cmdLine; *arg; ++arg)
{ {
WCHAR c; if (arg[0] != '/')
WINE_TRACE("Command line parm: '%s'\n", wine_dbgstr_w(argPos));
if (argPos[0]!='/' || argPos[1]=='\0') {
args++;
WCMD_parameter(cmdLine, args, &argPos, TRUE, TRUE);
continue; continue;
}
c=argPos[1]; switch (towlower(arg[1]))
if (towlower(c)=='c') {
opt_c = TRUE;
} else if (towlower(c)=='q') {
opt_q = TRUE;
} else if (towlower(c)=='k') {
opt_k = TRUE;
} else if (towlower(c)=='s') {
opt_s = TRUE;
} else if (towlower(c)=='a') {
unicodeOutput = FALSE;
} else if (towlower(c)=='u') {
unicodeOutput = TRUE;
} else if (towlower(c)=='v' && argPos[2]==':') {
delayedsubst = wcsnicmp(&argPos[3], offW, 3);
if (delayedsubst) WINE_TRACE("Delayed substitution is on\n");
} else if (towlower(c)=='t' && argPos[2]==':') {
opt_t=wcstoul(&argPos[3], NULL, 16);
} else if (towlower(c)=='x' || towlower(c)=='y') {
/* Ignored for compatibility with Windows */
}
if (argPos[2]==0 || argPos[2]==' ' || argPos[2]=='\t' ||
towlower(c)=='v') {
args++;
WCMD_parameter(cmdLine, args, &argPos, TRUE, TRUE);
}
else /* handle `cmd /cnotepad.exe` and `cmd /x/c ...` */
{ {
/* Do not step to next parameter, instead carry on parsing this one */ case 'a':
argPos+=2; unicodeOutput = FALSE;
} break;
case 'c':
if (opt_c || opt_k) /* break out of parsing immediately after c or k */ opt_c = TRUE;
break;
case 'k':
opt_k = TRUE;
break;
case 'q':
opt_q = TRUE;
break;
case 's':
opt_s = TRUE;
break;
case 't':
if (arg[2] == ':')
opt_t = wcstoul(&arg[3], NULL, 16);
break;
case 'u':
unicodeOutput = TRUE;
break;
case 'v':
if (arg[2] == ':')
delayedsubst = wcsnicmp(&arg[3], L"OFF", 3);
break; break;
} }
if (opt_c || opt_k)
{
arg += 2;
break;
}
}
while (*arg && wcschr(L" \t,=;", *arg)) arg++;
if (opt_q) { if (opt_q) {
WCMD_echo(offW); WCMD_echo(offW);
} }
@ -2531,12 +2528,8 @@ int __cdecl wmain (int argc, WCHAR *argvW[])
int len; int len;
WCHAR *q1 = NULL,*q2 = NULL,*p; WCHAR *q1 = NULL,*q2 = NULL,*p;
/* Handle very edge case error scenario, "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;
/* Take a copy */ /* Take a copy */
cmd = heap_strdupW(argPos); cmd = heap_strdupW(arg);
/* opt_s left unflagged if the command starts with and contains exactly /* opt_s left unflagged if the command starts with and contains exactly
* one quoted string (exactly two quote characters). The quoted string * one quoted string (exactly two quote characters). The quoted string
@ -2545,7 +2538,7 @@ int __cdecl wmain (int argc, WCHAR *argvW[])
if (!opt_s) { if (!opt_s) {
/* 1. Confirm there is at least one quote */ /* 1. Confirm there is at least one quote */
q1 = wcschr(argPos, '"'); q1 = wcschr(arg, '"');
if (!q1) opt_s=1; if (!q1) opt_s=1;
} }