cmd: Fix parameterization around delimiters.

This commit is contained in:
Jason Edmeades 2012-09-26 00:07:20 +01:00 committed by Alexandre Julliard
parent 1ed2bd14b7
commit e35b239b41
7 changed files with 154 additions and 60 deletions

View File

@ -117,9 +117,11 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA
* n [I] # of the parameter to return, counted from 0
* start [O] Optional. Pointer to the first char of param n in s
* end [O] Optional. Pointer to the last char of param n in s
* raw [I] True to return the parameter in raw format (quotes maintainted)
* False returns the parameter with quotes stripped
*
* RETURNS
* Success: The nth delimited parameter found in s, with any surrounding quotes removed
* Success: The nth delimited parameter found in s
* if start != NULL, *start points to the start of the param
* if end != NULL, *end points to the end of the param
* Failure: An empty string if the param is not found.
@ -128,40 +130,67 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA
* NOTES
* Return value is stored in static storage (i.e. overwritten after each call).
* Specify 'start' and/or 'end' to include delimiting double quotes as well, if any.
* By default, the parameter is returned with quotes removed, ready for use with
* other API calls, e.g. c:\"a b"\c is returned as c:\a b\c. However, some commands
* need to preserve the exact syntax (echo, for, etc) hence the raw option.
*/
WCHAR *WCMD_parameter (WCHAR *s, int n, WCHAR **start, WCHAR **end)
WCHAR *WCMD_parameter (WCHAR *s, int n, WCHAR **start, WCHAR **end, BOOL raw)
{
static const WCHAR defaultDelims[] = { ' ', '\t', ',', '=', ';', '\0' };
int curParamNb = 0;
static WCHAR param[MAX_PATH];
WCHAR *p = s, *q;
BOOL quotesDelimited;
WCHAR *p = s, *begin;
if (start != NULL) *start = NULL;
if (end != NULL) *end = NULL;
param[0] = '\0';
while (TRUE) {
while (*p && ((*p == ' ') || (*p == ',') || (*p == '=') || (*p == '\t')))
/* Absorb repeated word delimiters until we get to the next token (or the end!) */
while (*p && (strchrW(defaultDelims, *p) != NULL))
p++;
if (*p == '\0') return param;
quotesDelimited = (*p == '"');
/* If we have reached the token number we want, remember the beginning of it if it */
if (start != NULL && curParamNb == n) *start = p;
if (quotesDelimited) {
q = ++p;
while (*p && *p != '"') p++;
} else {
q = p;
while (*p && (*p != ' ') && (*p != ',') && (*p != '=') && (*p != '\t'))
/* Return the whole word up to the next delimiter, handling quotes in the middle
of it, e.g. a"\b c\"d is a single parameter. */
begin = p;
/* Loop character by character, but just need to special case quotes */
while (*p) {
/* Once we have found a delimiter, break */
if (strchrW(defaultDelims, *p) != NULL) break;
/* If we find a quote, copy until we get the end quote */
if (*p == '"') {
p++;
while (*p && *p != '"') p++;
}
/* Now skip the character / quote */
if (*p) p++;
}
if (curParamNb == n) {
memcpy(param, q, (p - q) * sizeof(WCHAR));
param[p-q] = '\0';
if (end) *end = p - 1 + quotesDelimited;
/* Return the parameter in static storage either as-is (raw) or
suitable for use with other win32 api calls (quotes stripped) */
if (raw) {
memcpy(param, begin, (p - begin) * sizeof(WCHAR));
param[p-begin] = '\0';
} else {
int i=0;
while (begin < p) {
if (*begin != '"') param[i++] = *begin;
begin++;
}
param[i] = '\0';
}
if (end) *end = p - 1;
return param;
}
if (quotesDelimited && *p == '"') p++;
curParamNb++;
}
}
@ -381,7 +410,7 @@ void WCMD_HandleTildaModifiers(WCHAR **start, const WCHAR *forVariable,
} else if ((*lastModifier >= '1' && *lastModifier <= '9')) {
strcpyW(outputparam,
WCMD_parameter (context -> command, *lastModifier-'0' + context -> shift_count[*lastModifier-'0'],
NULL, NULL));
NULL, NULL, FALSE));
} else {
strcpyW(outputparam, forValue);
}

View File

@ -564,7 +564,7 @@ void WCMD_create_dir (WCHAR *command) {
}
/* Loop through all args */
while (TRUE) {
WCHAR *thisArg = WCMD_parameter(command, argno++, &argN, NULL);
WCHAR *thisArg = WCMD_parameter(command, argno++, &argN, NULL, FALSE);
if (!argN) break;
if (!create_full_path(thisArg)) {
WCMD_print_error ();
@ -869,7 +869,7 @@ BOOL WCMD_delete (WCHAR *command) {
WCHAR *thisArg;
argN = NULL;
thisArg = WCMD_parameter (command, argno, &argN, NULL);
thisArg = WCMD_parameter (command, argno, &argN, NULL, FALSE);
if (!argN)
break; /* no more parameters */
if (argN[0] == '/')
@ -1206,7 +1206,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
WINE_TRACE("Processing for set %p\n", thisSet);
i = 0;
while (*(item = WCMD_parameter (thisSet->command, i, &itemStart, NULL))) {
while (*(item = WCMD_parameter (thisSet->command, i, &itemStart, NULL, TRUE))) {
/*
* If the parameter within the set has a wildcard then search for matching files
@ -1305,7 +1305,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
while (WCMD_fgets(buffer, sizeof(buffer)/sizeof(WCHAR), input)) {
/* Skip blank lines*/
parm = WCMD_parameter (buffer, 0, &where, NULL);
parm = WCMD_parameter (buffer, 0, &where, NULL, FALSE);
WINE_TRACE("Parsed parameter: %s from %s\n", wine_dbgstr_w(parm),
wine_dbgstr_w(buffer));
@ -1336,7 +1336,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
/* Skip blank lines, and re-extract parameter now string has quotes removed */
strcpyW(buffer, item);
parm = WCMD_parameter (buffer, 0, &where, NULL);
parm = WCMD_parameter (buffer, 0, &where, NULL, FALSE);
WINE_TRACE("Parsed parameter: %s from %s\n", wine_dbgstr_w(parm),
wine_dbgstr_w(buffer));
@ -1596,7 +1596,7 @@ void WCMD_if (WCHAR *p, CMD_LIST **cmdList) {
WINE_TRACE("Condition: %s\n", wine_dbgstr_w(condition));
if (!lstrcmpiW (condition, errlvlW)) {
WCHAR *param = WCMD_parameter(p, 1+negate, NULL, NULL);
WCHAR *param = WCMD_parameter(p, 1+negate, NULL, NULL, FALSE);
WCHAR *endptr;
long int param_int = strtolW(param, &endptr, 10);
if (*endptr) {
@ -1604,22 +1604,24 @@ void WCMD_if (WCHAR *p, CMD_LIST **cmdList) {
return;
}
test = ((long int)errorlevel >= param_int);
WCMD_parameter(p, 2+negate, &command, NULL);
WCMD_parameter(p, 2+negate, &command, NULL, FALSE);
}
else if (!lstrcmpiW (condition, existW)) {
test = (GetFileAttributesW(WCMD_parameter(p, 1+negate, NULL, NULL)) != INVALID_FILE_ATTRIBUTES);
WCMD_parameter(p, 2+negate, &command, NULL);
test = (GetFileAttributesW(WCMD_parameter(p, 1+negate, NULL, NULL, FALSE))
!= INVALID_FILE_ATTRIBUTES);
WCMD_parameter(p, 2+negate, &command, NULL, FALSE);
}
else if (!lstrcmpiW (condition, defdW)) {
test = (GetEnvironmentVariableW(WCMD_parameter(p, 1+negate, NULL, NULL), NULL, 0) > 0);
WCMD_parameter(p, 2+negate, &command, NULL);
test = (GetEnvironmentVariableW(WCMD_parameter(p, 1+negate, NULL, NULL, FALSE),
NULL, 0) > 0);
WCMD_parameter(p, 2+negate, &command, NULL, FALSE);
}
else if ((s = strstrW (p, eqeqW))) {
/* We need to get potential surrounding double quotes, so param1/2 can't be used */
WCHAR *leftPart, *leftPartEnd, *rightPart, *rightPartEnd;
s += 2;
WCMD_parameter(p, 0+negate+caseInsensitive, &leftPart, &leftPartEnd);
WCMD_parameter(p, 1+negate+caseInsensitive, &rightPart, &rightPartEnd);
WCMD_parameter(p, 0+negate+caseInsensitive, &leftPart, &leftPartEnd, FALSE);
WCMD_parameter(p, 1+negate+caseInsensitive, &rightPart, &rightPartEnd, FALSE);
test = caseInsensitive
? (CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
leftPart, leftPartEnd-leftPart+1,
@ -1627,7 +1629,7 @@ void WCMD_if (WCHAR *p, CMD_LIST **cmdList) {
: (CompareStringW(LOCALE_SYSTEM_DEFAULT, 0,
leftPart, leftPartEnd-leftPart+1,
rightPart, rightPartEnd-rightPart+1) == CSTR_EQUAL);
WCMD_parameter(s, 1, &command, NULL);
WCMD_parameter(s, 1, &command, NULL, FALSE);
}
else {
WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR));
@ -1802,7 +1804,7 @@ void WCMD_remove_dir (WCHAR *command) {
/* Loop through all args */
while (argN) {
WCHAR *thisArg = WCMD_parameter (command, argno++, &argN, NULL);
WCHAR *thisArg = WCMD_parameter (command, argno++, &argN, NULL, FALSE);
if (argN && argN[0] != '/') {
WINE_TRACE("rd: Processing arg %s (quals:%s)\n", wine_dbgstr_w(thisArg),
wine_dbgstr_w(quals));
@ -2564,7 +2566,7 @@ void WCMD_type (WCHAR *command) {
/* Loop through all args */
errorlevel = 0;
while (argN) {
WCHAR *thisArg = WCMD_parameter (command, argno++, &argN, NULL);
WCHAR *thisArg = WCMD_parameter (command, argno++, &argN, NULL, FALSE);
HANDLE h;
WCHAR buffer[512];
@ -2658,7 +2660,7 @@ void WCMD_more (WCHAR *command) {
WCMD_enter_paged_mode(moreStrPage);
while (argN) {
WCHAR *thisArg = WCMD_parameter (command, argno++, &argN, NULL);
WCHAR *thisArg = WCMD_parameter (command, argno++, &argN, NULL, FALSE);
HANDLE h;
if (!argN) break;

View File

@ -803,7 +803,7 @@ void WCMD_directory (WCHAR *cmd)
prevEntry = NULL;
while (argN) {
WCHAR fullname[MAXSTRING];
WCHAR *thisArg = WCMD_parameter(cmd, argno++, &argN, NULL);
WCHAR *thisArg = WCMD_parameter(cmd, argno++, &argN, NULL, FALSE);
if (argN && argN[0] != '/') {
WINE_TRACE("Found parm '%s'\n", wine_dbgstr_w(thisArg));

View File

@ -67,6 +67,26 @@ type mixedEchoModes.cmd
cmd /c mixedEchoModes.cmd
del mixedEchoModes.cmd
echo ------------ Testing parameterization ------------
call :TestParm a b c
call :TestParm "a b c"
call :TestParm "a b"\c
call :TestParm a=~`+,.{}!+b
call :TestParm a;b
call :TestParm "a;b"
call :TestParm a^;b
call :TestParm a[b]{c}(d)e
call :TestParm a&echo second line
call :TestParm a b,,,c
call :TestParm a==b;;c
call :TestParm a,,, b
goto :TestRem
:TestParm
echo '%1', '%2', '%3'
goto :eof
:TestRem
echo ------------ Testing rem ------------
rem Hello
rem Hello
@ -110,6 +130,12 @@ echo foo11> foo
type foo
echo foo12> foo
type foo
echo foo13>"foo"
type foo
echo foo14>."\foo"
type foo
echo foo15>."\f"oo
type foo
del foo
echo1>foo
type foo
@ -124,7 +150,7 @@ echo fooc 1>>foo
type foo
echo food1>>foo
type foo
echo food2>>foo
echo food2>>"foo"
type foo
del foo
echo food21>>foo
@ -502,6 +528,12 @@ mkdir "bar bak"
cd "bar bak"
cd
cd ..
cd ".\bar bak"
cd
cd ..
cd .\"bar bak"
cd
cd ..
cd bar bak
cd
cd "bar bak@space@"@tab@@space@
@ -522,7 +554,11 @@ type foobaz
echo ---
@echo off
type foobaz@tab@
echo ---
echo ---1
type ."\foobaz"
echo ---2
type ".\foobaz"
echo ---3
del foobaz
echo ------------ Testing NUL ------------
@ -1125,11 +1161,13 @@ if not exist foo (
)
echo --- multiple directories at once
mkdir foobaz & cd foobaz
mkdir foo bar\baz foobar
mkdir foo bar\baz foobar "bazbaz" .\"zabzab"
if exist foo (echo foo created) else echo foo not created!
if exist bar (echo bar created) else echo bar not created!
if exist foobar (echo foobar created) else echo foobar not created!
if exist bar\baz (echo bar\baz created) else echo bar\baz not created!
if exist bazbaz (echo bazbaz created) else echo bazbaz not created!
if exist zabzab (echo zabzab created) else echo zabzab not created!
cd .. & rd /s/q foobaz
call :setError 0
mkdir foo\*

View File

@ -116,6 +116,20 @@ foo
bar
foo2
bar2
------------ Testing parameterization ------------
'a', 'b', 'c'
'"a b c"', '', ''
'"a b"\c', '', ''
'a', '~`+', '.{}!+b'
'a', 'b', ''
'"a;b"', '', ''
@todo_wine@'a', 'b', ''
'a[b]{c}(d)e', '', ''
'a', '', ''
second line
'a', 'b', 'c'
'a', 'b', 'c'
'a', 'b', ''
------------ Testing rem ------------
@pwd@>rem Hello@space@
@ -145,6 +159,9 @@ foo9@space@@or_broken@foo@tab@
foo1
foo11
foo12
foo13
foo14
foo15
--- stdout appending
foo
foo@space@
@ -257,7 +274,7 @@ e@or_broken@qwerty
r@or_broken@qwerty
------------ Testing variable substitution ------------
--- in FOR variables
@todo_wine@"A B"
"A B"
C
'A B'@or_broken@''
'C'@or_broken@''
@ -291,19 +308,19 @@ N
''
'.eh'
--- in parameters
@todo_wine@"A B"
"A B"
C
@todo_wine@'A B'@or_broken@''
'A B'@or_broken@''
'C'@or_broken@''
@todo_wine@@pwd@\C D
@pwd@\C D
@pwd@\E
@drive@
@drive@
@path@
@path@
@todo_wine@L M
L M
N
@todo_wine@'.OOL'
'.OOL'
'.TABC'
''
@todo_wine@'@drive@@shortpath@R S'@or_broken@''
@ -364,6 +381,8 @@ Current dir: @pwd@\foobar@or_broken@Current dir:@space@
@pwd@\foobar\bar bak
@pwd@\foobar\bar bak
@pwd@\foobar\bar bak
@pwd@\foobar\bar bak
@pwd@\foobar\bar bak
@pwd@
@pwd@\foobar
------------ Testing type ------------
@ -374,7 +393,11 @@ bar
@pwd@>echo ---@space@
---
bar
---
---1
bar
---2
bar
---3
------------ Testing NUL ------------
bar
bar
@ -438,8 +461,8 @@ B`
'echo
A
B'
@todo_wine@"echo A B"
@todo_wine@"A B"
"echo A B"
"A B"
C
--- imbricated FORs
@todo_wine@X Y
@ -565,11 +588,11 @@ ErrorLevel 0
--- for /F
------ string argument
@todo_wine@a
@todo_wine@a
a
a
a
a
a
@todo_wine@a
@todo_wine@a
------ fileset argument
--------- basic blank handling
a
@ -682,6 +705,8 @@ foo created
bar created
foobar created
bar\baz created
bazbaz created
zabzab created
mkdir foo\* errorlevel 1
ok, foo created
------------ Testing rmdir ------------
@ -764,16 +789,16 @@ foo@space@
foo 8
foo@space@@space@
foo bar@space@
@todo_wine@foo ""@space@
@todo_wine@"" bar@space@
foo ""@space@
"" bar@space@
foo ''@space@
'' bar@space@
--- internal routines
bar :testRoutine
foo@space@
foo bar
@todo_wine@foo ""
@todo_wine@"" bar
foo ""
"" bar
foo ''
'' bar
--- with builtins

View File

@ -107,7 +107,7 @@ static inline BOOL WCMD_is_console_handle(HANDLE h)
return (((DWORD_PTR)h) & 3) == 3;
}
WCHAR *WCMD_fgets (WCHAR *buf, DWORD n, HANDLE stream);
WCHAR *WCMD_parameter (WCHAR *s, int n, WCHAR **start, WCHAR **end);
WCHAR *WCMD_parameter (WCHAR *s, int n, WCHAR **start, WCHAR **end, BOOL raw);
WCHAR *WCMD_skip_leading_spaces (WCHAR *string);
BOOL WCMD_keyword_ws_found(const WCHAR *keyword, int len, const WCHAR *ptr);
void WCMD_HandleTildaModifiers(WCHAR **start, const WCHAR *forVariable, const WCHAR *forValue, BOOL justFors);

View File

@ -831,13 +831,13 @@ static void handleExpansion(WCHAR *cmd, BOOL justFors,
/* Replace use of %0...%9 if in batch program*/
} else if (!justFors && context && (i >= 0) && (i <= 9)) {
t = WCMD_parameter(context -> command, i + context -> shift_count[i], NULL, NULL);
t = WCMD_parameter(context -> command, i + context -> shift_count[i], NULL, NULL, TRUE);
WCMD_strsubstW(p, p+2, t, -1);
/* Replace use of %* if in batch program*/
} else if (!justFors && context && *(p+1)=='*') {
WCHAR *startOfParms = NULL;
WCMD_parameter(context -> command, 1, &startOfParms, NULL);
WCMD_parameter(context -> command, 1, &startOfParms, NULL, TRUE);
if (startOfParms != NULL)
WCMD_strsubstW(p, p+2, startOfParms, -1);
else
@ -1356,7 +1356,7 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
/* Otherwise STDIN could come from a '<' redirect */
} else if ((p = strchrW(new_redir,'<')) != NULL) {
h = CreateFileW(WCMD_parameter(++p, 0, NULL, NULL), GENERIC_READ, FILE_SHARE_READ,
h = CreateFileW(WCMD_parameter(++p, 0, NULL, NULL, FALSE), GENERIC_READ, FILE_SHARE_READ,
&sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
WCMD_print_error ();
@ -1401,7 +1401,7 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
WINE_TRACE("Redirect %d (%p) to %d (%p)\n", handle, GetStdHandle(idx_stdhandles[idx]), idx, h);
} else {
WCHAR *param = WCMD_parameter(p, 0, NULL, NULL);
WCHAR *param = WCMD_parameter(p, 0, NULL, NULL, FALSE);
h = CreateFileW(param, GENERIC_WRITE, 0, &sa, creationDisposition,
FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {