cmd: Add full for /R support.
This commit is contained in:
parent
8fbd65358e
commit
9dde62cb96
|
@ -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,13 +1197,58 @@ 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;
|
||||
|
||||
/* 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'};
|
||||
|
||||
/* Save away the starting position for the commands (and offset for the
|
||||
first one) */
|
||||
cmdStart = *cmdList;
|
||||
firstCmd = (*cmdList)->command + 3; /* Skip 'do ' */
|
||||
itemNum = 0;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
thisSet = setStart;
|
||||
/* Loop through all set entries */
|
||||
while (thisSet &&
|
||||
|
@ -1210,8 +1274,21 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
|
|||
WINE_TRACE("Processing for item %d '%s'\n", itemNum, wine_dbgstr_w(item));
|
||||
|
||||
if (!useNumbers && !doFileset) {
|
||||
if (strpbrkW (item, wildcards)) {
|
||||
hff = FindFirstFileW(item, &fd);
|
||||
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;
|
||||
|
@ -1225,17 +1302,26 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
|
|||
{
|
||||
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,
|
||||
fd.cFileName, FALSE, TRUE);
|
||||
fullitem, FALSE, TRUE);
|
||||
cmdEnd = thisCmdStart;
|
||||
}
|
||||
|
||||
} while (FindNextFileW(hff, &fd) != 0);
|
||||
FindClose (hff);
|
||||
}
|
||||
} else {
|
||||
doExecuted = TRUE;
|
||||
WCMD_part_execute(&thisCmdStart, firstCmd, variable, item, FALSE, TRUE);
|
||||
WCMD_part_execute(&thisCmdStart, firstCmd, variable, fullitem, FALSE, TRUE);
|
||||
cmdEnd = thisCmdStart;
|
||||
}
|
||||
|
||||
} else if (useNumbers) {
|
||||
|
@ -1290,7 +1376,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
|
|||
|
||||
} else {
|
||||
|
||||
WCHAR buffer[MAXSTRING] = {'\0'};
|
||||
WCHAR buffer[MAXSTRING];
|
||||
WCHAR *where, *parm;
|
||||
|
||||
while (WCMD_fgets(buffer, sizeof(buffer)/sizeof(WCHAR), input)) {
|
||||
|
@ -1309,7 +1395,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
|
|||
cmdEnd = thisCmdStart;
|
||||
}
|
||||
|
||||
buffer[0] = 0x00;
|
||||
buffer[0] = 0;
|
||||
|
||||
}
|
||||
CloseHandle (input);
|
||||
|
@ -1322,7 +1408,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
|
|||
|
||||
/* Filesets - A string literal */
|
||||
} else if (doFileset && *itemStart == '"') {
|
||||
WCHAR buffer[MAXSTRING] = {'\0'};
|
||||
WCHAR buffer[MAXSTRING];
|
||||
WCHAR *where, *parm;
|
||||
|
||||
/* Skip blank lines, and re-extract parameter now string has quotes removed */
|
||||
|
@ -1342,7 +1428,6 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
|
|||
}
|
||||
|
||||
WINE_TRACE("Post-command, cmdEnd = %p\n", cmdEnd);
|
||||
cmdEnd = thisCmdStart;
|
||||
i++;
|
||||
}
|
||||
|
||||
|
@ -1371,6 +1456,19 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
|
|||
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
|
||||
know this if something was run. If it has not been, we need to calculate
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue