cmd.exe: Enhance FOR support.
This commit is contained in:
parent
f825db6d0f
commit
dc372175d4
|
@ -639,8 +639,6 @@ void WCMD_echo (const WCHAR *command) {
|
|||
* next cmdlist contains the DO cmd
|
||||
* following that is either brackets or && entries (as per if)
|
||||
*
|
||||
* FIXME: We don't exhaustively check syntax. Any command which works in MessDOS
|
||||
* will probably work here, but the reverse is not necessarily the case...
|
||||
*/
|
||||
|
||||
void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
|
||||
|
@ -648,29 +646,89 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
|
|||
WIN32_FIND_DATA 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 doW[] = {'d', 'o', ' ', '\0'};
|
||||
CMD_LIST *setStart, *thisSet, *cmdStart, *cmdEnd;
|
||||
WCHAR variable[4];
|
||||
WCHAR *firstCmd;
|
||||
int thisDepth;
|
||||
|
||||
/* Check:
|
||||
the first line includes the % variable name as first parm
|
||||
we have been provided with more parts to the command
|
||||
and there is at least some set data
|
||||
and IN as the one after that */
|
||||
if (lstrcmpiW (WCMD_parameter (p, 1, NULL), inW)
|
||||
|| (*cmdList) == NULL
|
||||
|| (*cmdList)->nextcommand == NULL
|
||||
|| (param1[0] != '%')
|
||||
|| (strlenW(param1) > 3)) {
|
||||
WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
|
||||
return;
|
||||
WCHAR *curPos = p;
|
||||
BOOL expandDirs = FALSE;
|
||||
BOOL useNumbers = FALSE;
|
||||
BOOL doRecursive = FALSE;
|
||||
BOOL doFileset = FALSE;
|
||||
LONG numbers[3] = {0,0,0}; /* Defaults to 0 in native */
|
||||
int itemNum;
|
||||
CMD_LIST *thisCmdStart;
|
||||
|
||||
|
||||
/* Handle optional qualifiers (multiple are allowed) */
|
||||
while (*curPos && *curPos == '/') {
|
||||
WINE_TRACE("Processing qualifier at %s\n", wine_dbgstr_w(curPos));
|
||||
curPos++;
|
||||
switch (toupperW(*curPos)) {
|
||||
case 'D': curPos++; expandDirs = TRUE; break;
|
||||
case 'L': curPos++; useNumbers = TRUE; break;
|
||||
|
||||
/* Recursive is special case - /R can have an optional path following it */
|
||||
/* filenamesets are another special case - /F can have an optional options following it */
|
||||
case 'R':
|
||||
case 'F':
|
||||
{
|
||||
BOOL isRecursive = (*curPos == 'R');
|
||||
|
||||
if (isRecursive) doRecursive = TRUE;
|
||||
else doFileset = TRUE;
|
||||
|
||||
/* Skip whitespace */
|
||||
curPos++;
|
||||
while (*curPos && *curPos==' ') curPos++;
|
||||
|
||||
/* Next parm is either qualifier, path/options or variable -
|
||||
only care about it if it is the path/options */
|
||||
if (*curPos && *curPos != '/' && *curPos != '%') {
|
||||
if (isRecursive) WINE_FIXME("/R needs to handle supplied root\n");
|
||||
else WINE_FIXME("/F needs to handle options\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
WINE_FIXME("for qualifier '%c' unhandled\n", *curPos);
|
||||
curPos++;
|
||||
}
|
||||
|
||||
/* Skip whitespace between qualifiers */
|
||||
while (*curPos && *curPos==' ') curPos++;
|
||||
}
|
||||
|
||||
/* Skip whitespace before variable */
|
||||
while (*curPos && *curPos==' ') curPos++;
|
||||
|
||||
/* Ensure line continues with variable */
|
||||
if (!*curPos || *curPos != '%') {
|
||||
WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Variable should follow */
|
||||
i = 0;
|
||||
while (curPos[i] && curPos[i]!=' ') 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++;
|
||||
|
||||
/* Ensure line continues with IN */
|
||||
if (!*curPos || lstrcmpiW (curPos, inW)) {
|
||||
WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Save away where the set of data starts and the variable */
|
||||
strcpyW(variable, param1);
|
||||
thisDepth = (*cmdList)->bracketDepth;
|
||||
*cmdList = (*cmdList)->nextcommand;
|
||||
setStart = (*cmdList);
|
||||
|
@ -702,6 +760,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
|
|||
cmdStart = *cmdList;
|
||||
cmdEnd = *cmdList;
|
||||
firstCmd = (*cmdList)->command + 3; /* Skip 'do ' */
|
||||
itemNum = 0;
|
||||
|
||||
thisSet = setStart;
|
||||
/* Loop through all set entries */
|
||||
|
@ -721,27 +780,83 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
|
|||
* otherwise do a literal substitution.
|
||||
*/
|
||||
static const WCHAR wildcards[] = {'*','?','\0'};
|
||||
CMD_LIST *thisCmdStart = cmdStart;
|
||||
thisCmdStart = cmdStart;
|
||||
|
||||
WINE_TRACE("Processing for item '%s'\n", wine_dbgstr_w(item));
|
||||
if (strpbrkW (item, wildcards)) {
|
||||
hff = FindFirstFile (item, &fd);
|
||||
if (hff != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
BOOL isDirectory = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
if (!isDirectory)
|
||||
{
|
||||
thisCmdStart = cmdStart;
|
||||
WINE_TRACE("Processing FOR filename %s\n", wine_dbgstr_w(fd.cFileName));
|
||||
WCMD_part_execute (&thisCmdStart, firstCmd, variable,
|
||||
fd.cFileName, FALSE, TRUE);
|
||||
itemNum++;
|
||||
WINE_TRACE("Processing for item %d '%s'\n", itemNum, wine_dbgstr_w(item));
|
||||
|
||||
if (!useNumbers && !doFileset) {
|
||||
if (strpbrkW (item, wildcards)) {
|
||||
hff = FindFirstFile (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));
|
||||
WCMD_part_execute (&thisCmdStart, firstCmd, variable,
|
||||
fd.cFileName, FALSE, TRUE);
|
||||
}
|
||||
|
||||
} while (FindNextFile(hff, &fd) != 0);
|
||||
FindClose (hff);
|
||||
}
|
||||
} else {
|
||||
WCMD_part_execute(&thisCmdStart, firstCmd, variable, item, FALSE, TRUE);
|
||||
}
|
||||
|
||||
} while (FindNextFile(hff, &fd) != 0);
|
||||
FindClose (hff);
|
||||
}
|
||||
} else {
|
||||
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! */
|
||||
|
||||
} else if (doFileset) {
|
||||
|
||||
HANDLE input;
|
||||
|
||||
WINE_TRACE("Processing for filespec from item %d '%s'\n", itemNum,
|
||||
wine_dbgstr_w(item));
|
||||
|
||||
/* Open the file, read line by line and process */
|
||||
input = CreateFile (item, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (input == INVALID_HANDLE_VALUE) {
|
||||
WCMD_print_error ();
|
||||
WCMD_output (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);
|
||||
WINE_TRACE("Parsed parameter: %s from %s\n", wine_dbgstr_w(parm),
|
||||
wine_dbgstr_w(buffer));
|
||||
|
||||
if (where) {
|
||||
thisCmdStart = cmdStart;
|
||||
WCMD_part_execute(&thisCmdStart, firstCmd, variable, parm, FALSE, TRUE);
|
||||
cmdEnd = thisCmdStart;
|
||||
}
|
||||
|
||||
buffer[0] = 0x00;
|
||||
|
||||
}
|
||||
CloseHandle (input);
|
||||
}
|
||||
}
|
||||
|
||||
WINE_TRACE("Post-command, cmdEnd = %p\n", cmdEnd);
|
||||
|
@ -753,6 +868,26 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
|
|||
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;
|
||||
WCMD_part_execute(&thisCmdStart, firstCmd, variable, thisNum, FALSE, TRUE);
|
||||
cmdEnd = thisCmdStart;
|
||||
}
|
||||
}
|
||||
|
||||
/* When the loop ends, either something like a GOTO or EXIT /b has terminated
|
||||
all processing, OR it should be pointing to the end of && processing OR
|
||||
it should be pointing at the NULL end of bracket for the DO. The return
|
||||
|
|
Loading…
Reference in New Issue