From 9dde62cb96b8f423f8cfe4f095eb10ec65ea7d72 Mon Sep 17 00:00:00 2001 From: Jason Edmeades Date: Thu, 27 Sep 2012 19:58:56 +0100 Subject: [PATCH] cmd: Add full for /R support. --- programs/cmd/builtins.c | 468 ++++++++++++++--------- programs/cmd/tests/test_builtins.cmd | 123 ++++++ programs/cmd/tests/test_builtins.cmd.exp | 10 + 3 files changed, 416 insertions(+), 185 deletions(-) diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index e54c444bbc5..f9e51e563e4 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -1079,10 +1079,13 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { WCHAR variable[4]; WCHAR *firstCmd; int thisDepth; + WCHAR optionsRoot[MAX_PATH]; + DIRECTORY_STACK *dirsToWalk = NULL; BOOL expandDirs = FALSE; BOOL useNumbers = FALSE; BOOL doFileset = FALSE; + BOOL doRecurse = FALSE; BOOL doExecuted = FALSE; /* Has the 'do' part been executed */ LONG numbers[3] = {0,0,0}; /* Defaults to 0 in native */ int itemNum; @@ -1091,6 +1094,8 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { /* Handle optional qualifiers (multiple are allowed) */ WCHAR *thisArg = WCMD_parameter(p, parameterNo++, NULL, NULL, FALSE); + + optionsRoot[0] = 0; while (thisArg && *thisArg == '/') { WINE_TRACE("Processing qualifier at %s\n", wine_dbgstr_w(thisArg)); thisArg++; @@ -1103,20 +1108,23 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { case 'R': case 'F': { - BOOL isRecursive = (*thisArg == 'R'); + /* When recursing directories, use current directory as the starting point unless + subsequently overridden */ + doRecurse = (toupperW(*thisArg) == 'R'); + if (doRecurse) GetCurrentDirectoryW(sizeof(optionsRoot)/sizeof(WCHAR), optionsRoot); - if (!isRecursive) - doFileset = TRUE; + doFileset = (toupperW(*thisArg) == 'F'); - /* Retrieve next parameter to see if is path/options */ - thisArg = WCMD_parameter(p, parameterNo, NULL, NULL, !isRecursive); + /* Retrieve next parameter to see if is root/options (raw form required + with for /f, or unquoted in for /r) */ + thisArg = WCMD_parameter(p, parameterNo, NULL, NULL, doFileset); /* Next parm is either qualifier, path/options or variable - only care about it if it is the path/options */ if (thisArg && *thisArg != '/' && *thisArg != '%') { parameterNo++; - if (isRecursive) WINE_FIXME("/R needs to handle supplied root\n"); - else { + strcpyW(optionsRoot, thisArg); + if (!doRecurse) { static unsigned int once; if (!once++) WINE_FIXME("/F needs to handle options\n"); } @@ -1137,6 +1145,17 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { return; } + /* Set up the list of directories to recurse if we are going to */ + if (doRecurse) { + /* Allocate memory, add to list */ + dirsToWalk = HeapAlloc(GetProcessHeap(), 0, sizeof(DIRECTORY_STACK)); + dirsToWalk->next = NULL; + dirsToWalk->dirName = HeapAlloc(GetProcessHeap(),0, + (strlenW(optionsRoot) + 1) * sizeof(WCHAR)); + strcpyW(dirsToWalk->dirName, optionsRoot); + WINE_TRACE("Starting with root directory %s\n", wine_dbgstr_w(dirsToWalk->dirName)); + } + /* Variable should follow */ strcpyW(variable, thisArg); WINE_TRACE("Variable identified as %s\n", wine_dbgstr_w(variable)); @@ -1178,198 +1197,277 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { return; } - /* Save away the starting position for the commands (and offset for the - first one */ - cmdStart = *cmdList; cmdEnd = *cmdList; - firstCmd = (*cmdList)->command + 3; /* Skip 'do ' */ - itemNum = 0; - thisSet = setStart; - /* Loop through all set entries */ - while (thisSet && - thisSet->command != NULL && - thisSet->bracketDepth >= thisDepth) { + /* Loop repeatedly per-directory we are potentially walking, when in for /r + mode, or once for the rest of the time. */ + do { + WCHAR fullitem[MAX_PATH]; + static const WCHAR slashstarW[] = {'\\','*','\0'}; - /* Loop through all entries on the same line */ - WCHAR *item; - WCHAR *itemStart; + /* Save away the starting position for the commands (and offset for the + first one) */ + cmdStart = *cmdList; + firstCmd = (*cmdList)->command + 3; /* Skip 'do ' */ + itemNum = 0; - WINE_TRACE("Processing for set %p\n", thisSet); - i = 0; - while (*(item = WCMD_parameter (thisSet->command, i, &itemStart, NULL, TRUE))) { + /* If we are recursing directories (ie /R), add all sub directories now, then + prefix the root when searching for the item */ + if (dirsToWalk) { + DIRECTORY_STACK *remainingDirs = dirsToWalk; - /* - * If the parameter within the set has a wildcard then search for matching files - * otherwise do a literal substitution. - */ - static const WCHAR wildcards[] = {'*','?','\0'}; - thisCmdStart = cmdStart; - - itemNum++; - WINE_TRACE("Processing for item %d '%s'\n", itemNum, wine_dbgstr_w(item)); - - if (!useNumbers && !doFileset) { - if (strpbrkW (item, wildcards)) { - hff = FindFirstFileW(item, &fd); - if (hff != INVALID_HANDLE_VALUE) { - do { - BOOL isDirectory = FALSE; - - if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) isDirectory = TRUE; - - /* Handle as files or dirs appropriately, but ignore . and .. */ - if (isDirectory == expandDirs && - (strcmpW(fd.cFileName, dotdotW) != 0) && - (strcmpW(fd.cFileName, dotW) != 0)) - { - thisCmdStart = cmdStart; - WINE_TRACE("Processing FOR filename %s\n", wine_dbgstr_w(fd.cFileName)); - doExecuted = TRUE; - WCMD_part_execute (&thisCmdStart, firstCmd, variable, - fd.cFileName, FALSE, TRUE); - } - - } while (FindNextFileW(hff, &fd) != 0); - FindClose (hff); - } - } else { - doExecuted = TRUE; - WCMD_part_execute(&thisCmdStart, firstCmd, variable, item, FALSE, TRUE); - } - - } else if (useNumbers) { - /* Convert the first 3 numbers to signed longs and save */ - if (itemNum <=3) numbers[itemNum-1] = atolW(item); - /* else ignore them! */ - - /* Filesets - either a list of files, or a command to run and parse the output */ - } else if (doFileset && *itemStart != '"') { - - HANDLE input; - WCHAR temp_file[MAX_PATH]; - - WINE_TRACE("Processing for filespec from item %d '%s'\n", itemNum, - wine_dbgstr_w(item)); - - /* If backquote or single quote, we need to launch that command - and parse the results - use a temporary file */ - if (*itemStart == '`' || *itemStart == '\'') { - - WCHAR temp_path[MAX_PATH], temp_cmd[MAXSTRING]; - static const WCHAR redirOut[] = {'>','%','s','\0'}; - static const WCHAR cmdW[] = {'C','M','D','\0'}; - - /* Remove trailing character */ - itemStart[strlenW(itemStart)-1] = 0x00; - - /* Get temp filename */ - GetTempPathW(sizeof(temp_path)/sizeof(WCHAR), temp_path); - GetTempFileNameW(temp_path, cmdW, 0, temp_file); - - /* Execute program and redirect output */ - wsprintfW(temp_cmd, redirOut, (itemStart+1), temp_file); - WCMD_execute (itemStart, temp_cmd, NULL, NULL, NULL); - - /* Open the file, read line by line and process */ - input = CreateFileW(temp_file, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - } else { - - /* Open the file, read line by line and process */ - input = CreateFileW(item, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - } - - /* Process the input file */ - if (input == INVALID_HANDLE_VALUE) { - WCMD_print_error (); - WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), item); - errorlevel = 1; - return; /* FOR loop aborts at first failure here */ - - } else { - - WCHAR buffer[MAXSTRING] = {'\0'}; - WCHAR *where, *parm; - - while (WCMD_fgets(buffer, sizeof(buffer)/sizeof(WCHAR), input)) { - - /* Skip blank lines*/ - parm = WCMD_parameter (buffer, 0, &where, NULL, FALSE); - WINE_TRACE("Parsed parameter: %s from %s\n", wine_dbgstr_w(parm), - wine_dbgstr_w(buffer)); - - if (where) { - /* FIXME: The following should be moved into its own routine and - reused for the string literal parsing below */ - thisCmdStart = cmdStart; - doExecuted = TRUE; - WCMD_part_execute(&thisCmdStart, firstCmd, variable, parm, FALSE, TRUE); - cmdEnd = thisCmdStart; - } - - buffer[0] = 0x00; - - } - CloseHandle (input); - } - - /* Delete the temporary file */ - if (*itemStart == '`' || *itemStart == '\'') { - DeleteFileW(temp_file); - } - - /* Filesets - A string literal */ - } else if (doFileset && *itemStart == '"') { - WCHAR buffer[MAXSTRING] = {'\0'}; - WCHAR *where, *parm; - - /* Skip blank lines, and re-extract parameter now string has quotes removed */ - strcpyW(buffer, item); - parm = WCMD_parameter (buffer, 0, &where, NULL, FALSE); - WINE_TRACE("Parsed parameter: %s from %s\n", wine_dbgstr_w(parm), - wine_dbgstr_w(buffer)); - - if (where) { - /* FIXME: The following should be moved into its own routine and - reused for the string literal parsing below */ - thisCmdStart = cmdStart; - doExecuted = TRUE; - WCMD_part_execute(&thisCmdStart, firstCmd, variable, parm, FALSE, TRUE); - cmdEnd = thisCmdStart; + /* Build a generic search and add all directories on the list of directories + still to walk */ + strcpyW(fullitem, dirsToWalk->dirName); + strcatW(fullitem, slashstarW); + hff = FindFirstFileW(fullitem, &fd); + if (hff != INVALID_HANDLE_VALUE) { + do { + WINE_TRACE("Looking for subdirectories\n"); + if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + (strcmpW(fd.cFileName, dotdotW) != 0) && + (strcmpW(fd.cFileName, dotW) != 0)) + { + /* Allocate memory, add to list */ + DIRECTORY_STACK *toWalk = HeapAlloc(GetProcessHeap(), 0, sizeof(DIRECTORY_STACK)); + WINE_TRACE("(%p->%p)\n", remainingDirs, remainingDirs->next); + toWalk->next = remainingDirs->next; + remainingDirs->next = toWalk; + remainingDirs = toWalk; + toWalk->dirName = HeapAlloc(GetProcessHeap(), 0, + sizeof(WCHAR) * + (strlenW(dirsToWalk->dirName) + 2 + strlenW(fd.cFileName))); + strcpyW(toWalk->dirName, dirsToWalk->dirName); + strcatW(toWalk->dirName, slashW); + strcatW(toWalk->dirName, fd.cFileName); + WINE_TRACE("Added to stack %s (%p->%p)\n", wine_dbgstr_w(toWalk->dirName), + toWalk, toWalk->next); } + } while (FindNextFileW(hff, &fd) != 0); + WINE_TRACE("Finished adding all subdirectories\n"); + FindClose (hff); } - - WINE_TRACE("Post-command, cmdEnd = %p\n", cmdEnd); - cmdEnd = thisCmdStart; - i++; } - /* Move onto the next set line */ - thisSet = thisSet->nextcommand; - } + thisSet = setStart; + /* Loop through all set entries */ + while (thisSet && + thisSet->command != NULL && + thisSet->bracketDepth >= thisDepth) { - /* If /L is provided, now run the for loop */ - if (useNumbers) { - WCHAR thisNum[20]; - static const WCHAR fmt[] = {'%','d','\0'}; + /* Loop through all entries on the same line */ + WCHAR *item; + WCHAR *itemStart; - WINE_TRACE("FOR /L provided range from %d to %d step %d\n", - numbers[0], numbers[2], numbers[1]); - for (i=numbers[0]; - (numbers[1]<0)? i>=numbers[2] : i<=numbers[2]; - i=i + numbers[1]) { + WINE_TRACE("Processing for set %p\n", thisSet); + i = 0; + while (*(item = WCMD_parameter (thisSet->command, i, &itemStart, NULL, TRUE))) { - sprintfW(thisNum, fmt, i); - WINE_TRACE("Processing FOR number %s\n", wine_dbgstr_w(thisNum)); + /* + * If the parameter within the set has a wildcard then search for matching files + * otherwise do a literal substitution. + */ + static const WCHAR wildcards[] = {'*','?','\0'}; + thisCmdStart = cmdStart; - thisCmdStart = cmdStart; - doExecuted = TRUE; - WCMD_part_execute(&thisCmdStart, firstCmd, variable, thisNum, FALSE, TRUE); + itemNum++; + WINE_TRACE("Processing for item %d '%s'\n", itemNum, wine_dbgstr_w(item)); + + if (!useNumbers && !doFileset) { + WCHAR fullitem[MAX_PATH]; + + /* Now build the item to use / search for in the specified directory, + as it is fully qualified in the /R case */ + if (dirsToWalk) { + strcpyW(fullitem, dirsToWalk->dirName); + strcatW(fullitem, slashW); + strcatW(fullitem, item); + } else { + strcpyW(fullitem, item); + } + + if (strpbrkW (fullitem, wildcards)) { + + hff = FindFirstFileW(fullitem, &fd); + if (hff != INVALID_HANDLE_VALUE) { + do { + BOOL isDirectory = FALSE; + + if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) isDirectory = TRUE; + + /* Handle as files or dirs appropriately, but ignore . and .. */ + if (isDirectory == expandDirs && + (strcmpW(fd.cFileName, dotdotW) != 0) && + (strcmpW(fd.cFileName, dotW) != 0)) + { + thisCmdStart = cmdStart; + WINE_TRACE("Processing FOR filename %s\n", wine_dbgstr_w(fd.cFileName)); + + if (doRecurse) { + strcpyW(fullitem, dirsToWalk->dirName); + strcatW(fullitem, slashW); + strcatW(fullitem, fd.cFileName); + } else { + strcpyW(fullitem, fd.cFileName); + } + doExecuted = TRUE; + WCMD_part_execute (&thisCmdStart, firstCmd, variable, + fullitem, FALSE, TRUE); + cmdEnd = thisCmdStart; + } + } while (FindNextFileW(hff, &fd) != 0); + FindClose (hff); + } + } else { + doExecuted = TRUE; + WCMD_part_execute(&thisCmdStart, firstCmd, variable, fullitem, FALSE, TRUE); + cmdEnd = thisCmdStart; + } + + } else if (useNumbers) { + /* Convert the first 3 numbers to signed longs and save */ + if (itemNum <=3) numbers[itemNum-1] = atolW(item); + /* else ignore them! */ + + /* Filesets - either a list of files, or a command to run and parse the output */ + } else if (doFileset && *itemStart != '"') { + + HANDLE input; + WCHAR temp_file[MAX_PATH]; + + WINE_TRACE("Processing for filespec from item %d '%s'\n", itemNum, + wine_dbgstr_w(item)); + + /* If backquote or single quote, we need to launch that command + and parse the results - use a temporary file */ + if (*itemStart == '`' || *itemStart == '\'') { + + WCHAR temp_path[MAX_PATH], temp_cmd[MAXSTRING]; + static const WCHAR redirOut[] = {'>','%','s','\0'}; + static const WCHAR cmdW[] = {'C','M','D','\0'}; + + /* Remove trailing character */ + itemStart[strlenW(itemStart)-1] = 0x00; + + /* Get temp filename */ + GetTempPathW(sizeof(temp_path)/sizeof(WCHAR), temp_path); + GetTempFileNameW(temp_path, cmdW, 0, temp_file); + + /* Execute program and redirect output */ + wsprintfW(temp_cmd, redirOut, (itemStart+1), temp_file); + WCMD_execute (itemStart, temp_cmd, NULL, NULL, NULL); + + /* Open the file, read line by line and process */ + input = CreateFileW(temp_file, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } else { + + /* Open the file, read line by line and process */ + input = CreateFileW(item, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } + + /* Process the input file */ + if (input == INVALID_HANDLE_VALUE) { + WCMD_print_error (); + WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), item); + errorlevel = 1; + return; /* FOR loop aborts at first failure here */ + + } else { + + WCHAR buffer[MAXSTRING]; + WCHAR *where, *parm; + + while (WCMD_fgets(buffer, sizeof(buffer)/sizeof(WCHAR), input)) { + + /* Skip blank lines*/ + parm = WCMD_parameter (buffer, 0, &where, NULL, FALSE); + WINE_TRACE("Parsed parameter: %s from %s\n", wine_dbgstr_w(parm), + wine_dbgstr_w(buffer)); + + if (where) { + /* FIXME: The following should be moved into its own routine and + reused for the string literal parsing below */ + thisCmdStart = cmdStart; + doExecuted = TRUE; + WCMD_part_execute(&thisCmdStart, firstCmd, variable, parm, FALSE, TRUE); + cmdEnd = thisCmdStart; + } + + buffer[0] = 0; + + } + CloseHandle (input); + } + + /* Delete the temporary file */ + if (*itemStart == '`' || *itemStart == '\'') { + DeleteFileW(temp_file); + } + + /* Filesets - A string literal */ + } else if (doFileset && *itemStart == '"') { + WCHAR buffer[MAXSTRING]; + WCHAR *where, *parm; + + /* Skip blank lines, and re-extract parameter now string has quotes removed */ + strcpyW(buffer, item); + parm = WCMD_parameter (buffer, 0, &where, NULL, FALSE); + WINE_TRACE("Parsed parameter: %s from %s\n", wine_dbgstr_w(parm), + wine_dbgstr_w(buffer)); + + if (where) { + /* FIXME: The following should be moved into its own routine and + reused for the string literal parsing below */ + thisCmdStart = cmdStart; + doExecuted = TRUE; + WCMD_part_execute(&thisCmdStart, firstCmd, variable, parm, FALSE, TRUE); + cmdEnd = thisCmdStart; + } + } + + WINE_TRACE("Post-command, cmdEnd = %p\n", cmdEnd); + i++; } - cmdEnd = thisCmdStart; - } + + /* Move onto the next set line */ + thisSet = thisSet->nextcommand; + } + + /* If /L is provided, now run the for loop */ + if (useNumbers) { + WCHAR thisNum[20]; + static const WCHAR fmt[] = {'%','d','\0'}; + + WINE_TRACE("FOR /L provided range from %d to %d step %d\n", + numbers[0], numbers[2], numbers[1]); + for (i=numbers[0]; + (numbers[1]<0)? i>=numbers[2] : i<=numbers[2]; + i=i + numbers[1]) { + + sprintfW(thisNum, fmt, i); + WINE_TRACE("Processing FOR number %s\n", wine_dbgstr_w(thisNum)); + + thisCmdStart = cmdStart; + doExecuted = TRUE; + WCMD_part_execute(&thisCmdStart, firstCmd, variable, thisNum, FALSE, TRUE); + } + cmdEnd = thisCmdStart; + } + + /* If we are walking directories, move on to any which remain */ + if (dirsToWalk != NULL) { + DIRECTORY_STACK *nextDir = dirsToWalk->next; + HeapFree(GetProcessHeap(), 0, dirsToWalk->dirName); + HeapFree(GetProcessHeap(), 0, dirsToWalk); + dirsToWalk = nextDir; + if (dirsToWalk) WINE_TRACE("Moving to next directorty to iterate: %s\n", + wine_dbgstr_w(dirsToWalk->dirName)); + else WINE_TRACE("Finished all directories.\n"); + } + + } while (dirsToWalk != NULL); /* Now skip over the do part if we did not perform the for loop so far. We store in cmdEnd the next command after the do block, but we only diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index d3bdf043c1a..f564aedc62b 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -726,6 +726,129 @@ rem del tmp rem for /d %%i in (*) do echo %%i>> tmp rem sort < tmp rem del tmp +echo > baz\bazbaz +goto :TestForR + +:SetExpected +del temp.bat 2>nul +call :WriteLine set found=N +for /l %%i in (1,1,%expectedresults%) do ( + call :WriteLine if "%%%%expectedresults.%%i%%%%"=="%%%%1" set found=Y + call :WriteLine if "%%%%found%%%%"=="Y" set expectedresults.%%i= + call :WriteLine if "%%%%found%%%%"=="Y" goto :eof +) +call :WriteLine echo Got unexpected result: "%%%%1" +goto :eof + +:WriteLine +echo %*>> temp.bat +goto :EOF + +:ValidateExpected +del temp.bat 2>nul +for /l %%i in (1,1,%expectedresults%) do call :WriteLine if not "%%%%expectedresults.%%i%%%%"=="" echo Found missing result: "%%%%expectedresults.%%i%%%%" +call temp.bat +del temp.bat 2>nul +goto :eof + +:TestForR +rem %CD% does not tork on NT4 so use the following workaround +for /d %%i in (.) do set CURDIR=%%~dpnxi + +echo --- for /R +echo Plain directory enumeration +set expectedresults=4 +set expectedresults.1=%CURDIR%\. +set expectedresults.2=%CURDIR%\bar\. +set expectedresults.3=%CURDIR%\baz\. +set expectedresults.4=%CURDIR%\foo\. +call :SetExpected +for /R %%i in (.) do call temp.bat %%i +call :ValidateExpected + +echo Plain directory enumeration from provided root +set expectedresults=4 +set expectedresults.1=%CURDIR%\. +set expectedresults.2=%CURDIR%\bar\. +set expectedresults.3=%CURDIR%\baz\. +set expectedresults.4=%CURDIR%\foo\. +if "%CD%"=="" goto :SkipBrokenNT4 +call :SetExpected +for /R "%CURDIR%" %%i in (.) do call temp.bat %%i +call :ValidateExpected +:SkipBrokenNT4 + +echo File enumeration +set expectedresults=2 +set expectedresults.1=%CURDIR%\baz\bazbaz +set expectedresults.2=%CURDIR%\bazbaz +call :SetExpected +for /R %%i in (baz*) do call temp.bat %%i +call :ValidateExpected + +echo File enumeration from provided root +set expectedresults=2 +set expectedresults.1=%CURDIR%\baz\bazbaz +set expectedresults.2=%CURDIR%\bazbaz +call :SetExpected +for /R %%i in (baz*) do call temp.bat %%i +call :ValidateExpected + +echo Mixed enumeration +set expectedresults=6 +set expectedresults.1=%CURDIR%\. +set expectedresults.2=%CURDIR%\bar\. +set expectedresults.3=%CURDIR%\baz\. +set expectedresults.4=%CURDIR%\baz\bazbaz +set expectedresults.5=%CURDIR%\bazbaz +set expectedresults.6=%CURDIR%\foo\. +call :SetExpected +for /R %%i in (. baz*) do call temp.bat %%i +call :ValidateExpected + +echo Mixed enumeration from provided root +set expectedresults=6 +set expectedresults.1=%CURDIR%\. +set expectedresults.2=%CURDIR%\bar\. +set expectedresults.3=%CURDIR%\baz\. +set expectedresults.4=%CURDIR%\baz\bazbaz +set expectedresults.5=%CURDIR%\bazbaz +set expectedresults.6=%CURDIR%\foo\. +call :SetExpected +for /R %%i in (. baz*) do call temp.bat %%i +call :ValidateExpected + +echo With duplicates enumeration +set expectedresults=12 +set expectedresults.1=%CURDIR%\bar\bazbaz +set expectedresults.2=%CURDIR%\bar\fred +set expectedresults.3=%CURDIR%\baz\bazbaz +set expectedresults.4=%CURDIR%\baz\bazbaz +set expectedresults.5=%CURDIR%\baz\bazbaz +set expectedresults.6=%CURDIR%\baz\fred +set expectedresults.7=%CURDIR%\bazbaz +set expectedresults.8=%CURDIR%\bazbaz +set expectedresults.9=%CURDIR%\bazbaz +set expectedresults.10=%CURDIR%\foo\bazbaz +set expectedresults.11=%CURDIR%\foo\fred +set expectedresults.12=%CURDIR%\fred +call :SetExpected +for /R %%i in (baz* bazbaz fred ba*) do call temp.bat %%i +call :ValidateExpected + +echo Strip missing wildcards, keep unwildcarded names +set expectedresults=6 +set expectedresults.1=%CURDIR%\bar\jim +set expectedresults.2=%CURDIR%\baz\bazbaz +set expectedresults.3=%CURDIR%\baz\jim +set expectedresults.4=%CURDIR%\bazbaz +set expectedresults.5=%CURDIR%\foo\jim +set expectedresults.6=%CURDIR%\jim +call :SetExpected +for /R %%i in (baz* fred* jim) do call temp.bat %%i +call :ValidateExpected + +echo for /R passed cd .. & rd /s/Q foobar echo --- for /L rem Some cases loop forever writing 0s, like e.g. (1,0,1), (1,a,3) or (a,b,c); those can't be tested here diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 21440b59875..435d0e63a0d 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -495,6 +495,16 @@ bar@space@ PASSED xxx - Should be xxx Expected second line +--- for /R +Plain directory enumeration +Plain directory enumeration from provided root +File enumeration +File enumeration from provided root +Mixed enumeration +Mixed enumeration from provided root +With duplicates enumeration +Strip missing wildcards, keep unwildcarded names +for /R passed --- for /L 1 3