diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index c4ad2310b8a..7aaa90019c4 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -902,8 +902,10 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { WIN32_FIND_DATAW fd; HANDLE hff; int i; - const WCHAR inW[] = {'i', 'n', ' ', '\0'}; - const WCHAR doW[] = {'d', 'o', ' ', '\0'}; + const WCHAR inW[] = {'i', 'n', ' ', '\0'}; + const WCHAR inTabW[] = {'i', 'n', '\t', '\0'}; + const WCHAR doW[] = {'d', 'o', ' ', '\0'}; + const WCHAR doTabW[] = {'d', 'o', '\t', '\0'}; CMD_LIST *setStart, *thisSet, *cmdStart, *cmdEnd; WCHAR variable[4]; WCHAR *firstCmd; @@ -938,7 +940,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { /* Skip whitespace */ curPos++; - while (*curPos && *curPos==' ') curPos++; + while (*curPos && (*curPos==' ' || *curPos=='\t')) curPos++; /* Next parm is either qualifier, path/options or variable - only care about it if it is the path/options */ @@ -954,11 +956,11 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { } /* Skip whitespace between qualifiers */ - while (*curPos && *curPos==' ') curPos++; + while (*curPos && (*curPos==' ' || *curPos=='\t')) curPos++; } /* Skip whitespace before variable */ - while (*curPos && *curPos==' ') curPos++; + while (*curPos && (*curPos==' ' || *curPos=='\t')) curPos++; /* Ensure line continues with variable */ if (!*curPos || *curPos != '%') { @@ -968,19 +970,21 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { /* Variable should follow */ i = 0; - while (curPos[i] && curPos[i]!=' ') i++; + while (curPos[i] && curPos[i]!=' ' && curPos[i]!='\t') i++; memcpy(&variable[0], curPos, i*sizeof(WCHAR)); variable[i] = 0x00; WINE_TRACE("Variable identified as %s\n", wine_dbgstr_w(variable)); curPos = &curPos[i]; /* Skip whitespace before IN */ - while (*curPos && *curPos==' ') curPos++; + while (*curPos && (*curPos==' ' || *curPos=='\t')) curPos++; /* Ensure line continues with IN */ if (!*curPos - || CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, - curPos, 3, inW, -1) != CSTR_EQUAL) { + || (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, + curPos, 3, inW, -1) != CSTR_EQUAL + && CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, + curPos, 3, inTabW, -1) != CSTR_EQUAL)) { WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR)); return; } @@ -1005,9 +1009,11 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { /* Syntax error if missing close bracket, or nothing following it and once we have the complete set, we expect a DO */ WINE_TRACE("Looking for 'do' in %p\n", *cmdList); - if ((*cmdList == NULL) || - (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, - (*cmdList)->command, 3, doW, -1) != CSTR_EQUAL)) { + if ((*cmdList == NULL) + || (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, + (*cmdList)->command, 3, doW, -1) != CSTR_EQUAL + && CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, + (*cmdList)->command, 3, doTabW, -1) != CSTR_EQUAL)) { WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR)); return; } diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index b815e77588e..0d2f9833a6e 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -386,6 +386,7 @@ for %%i in@tab@(X3) do echo %%i for %%i in (@tab@ foo@tab@) do echo %%i for@tab@ %%i in@tab@(@tab@M) do echo %%i for %%i@tab@in (X)@tab@do@tab@echo %%i +for@tab@ %%j in@tab@(@tab@M, N, O@tab@) do echo %%j goto :endForTestFun1 :forTestFun1 echo %1 diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index b86c012844a..11384858607 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -275,11 +275,14 @@ A B C X -@todo_wine@X2 -@todo_wine@X3 +X2 +X3 foo -@todo_wine@M -@todo_wine@X +M +X +M +N +O ...imbricated FORs @todo_wine@X Y @todo_wine@X Y diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index d7588576d80..67b6ef4a20c 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1742,7 +1742,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE CMD_DELIMITERS prevDelim = CMD_NONE; static WCHAR *extraSpace = NULL; /* Deliberately never freed */ const WCHAR remCmd[] = {'r','e','m',' ','\0'}; - const WCHAR forCmd[] = {'f','o','r',' ','\0'}; + const WCHAR forCmd[] = {'f','o','r',' ' ,'\0'}; + const WCHAR forTabCmd[] = {'f','o','r','\t','\0'}; const WCHAR ifCmd[] = {'i','f',' ','\0'}; const WCHAR ifElse[] = {'e','l','s','e',' ','\0'}; BOOL inRem = FALSE; @@ -1826,7 +1827,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE /* Certain commands need special handling */ if (curStringLen == 0 && curCopyTo == curString) { - const WCHAR forDO[] = {'d','o',' ','\0'}; + const WCHAR forDO[] = {'d','o',' ' ,'\0'}; + const WCHAR forDOTab[] = {'d','o','\t','\0'}; /* If command starts with 'rem', ignore any &&, ( etc */ if (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, @@ -1835,7 +1837,9 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE /* If command starts with 'for', handle ('s mid line after IN or DO */ } else if (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, - curPos, 4, forCmd, -1) == CSTR_EQUAL) { + curPos, 4, forCmd, -1) == CSTR_EQUAL + || CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, + curPos, 4, forTabCmd, -1) == CSTR_EQUAL) { inFor = TRUE; /* If command starts with 'if' or 'else', handle ('s mid line. We should ensure this @@ -1863,7 +1867,9 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE is then 0, and all whitespace is skipped */ } else if (inFor && (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, - curPos, 3, forDO, -1) == CSTR_EQUAL)) { + curPos, 3, forDO, -1) == CSTR_EQUAL + || CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, + curPos, 3, forDOTab, -1) == CSTR_EQUAL)) { WINE_TRACE("Found DO\n"); lastWasDo = TRUE; onlyWhiteSpace = TRUE; @@ -1876,12 +1882,15 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE /* Special handling for the 'FOR' command */ if (inFor && lastWasWhiteSpace) { - const WCHAR forIN[] = {'i','n',' ','\0'}; + const WCHAR forIN[] = {'i','n',' ' ,'\0'}; + const WCHAR forINTab[] = {'i','n','\t','\0'}; WINE_TRACE("Found 'FOR', comparing next parm: '%s'\n", wine_dbgstr_w(curPos)); if (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, - curPos, 3, forIN, -1) == CSTR_EQUAL) { + curPos, 3, forIN, -1) == CSTR_EQUAL + || CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, + curPos, 3, forINTab, -1) == CSTR_EQUAL) { WINE_TRACE("Found IN\n"); lastWasIn = TRUE; onlyWhiteSpace = TRUE; @@ -2106,7 +2115,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE /* At various times we need to know if we have only skipped whitespace, so reset this variable and then it will remain true until a non whitespace is found */ - if ((thisChar != ' ') && (thisChar != '\n')) onlyWhiteSpace = FALSE; + if ((thisChar != ' ') && (thisChar != '\t') && (thisChar != '\n')) + onlyWhiteSpace = FALSE; /* Flag end of interest in FOR DO and IN parms once something has been processed */ if (!lastWasWhiteSpace) {