cmd.exe: Fix FOR so it works as 'well' as before.
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index 0ec86e5..a764228 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -40,7 +40,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(cmd);
-void WCMD_execute (WCHAR *orig_command, WCHAR *parameter, WCHAR *substitution, CMD_LIST **cmdList);
+static void WCMD_part_execute(CMD_LIST **commands, WCHAR *firstcmd, WCHAR *variable,
+ WCHAR *value, BOOL isIF, BOOL conditionTRUE);
struct env_stack *saved_environment;
struct env_stack *pushd_directories;
@@ -571,6 +572,12 @@
* WCMD_for
*
* Batch file loop processing.
+ *
+ * On entry: cmdList contains the syntax up to the set
+ * next cmdList and all in that bracket contain the set data
+ * 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...
*/
@@ -579,45 +586,211 @@
WIN32_FIND_DATA fd;
HANDLE hff;
- WCHAR *cmd, *item;
- WCHAR set[MAX_PATH], param[MAX_PATH];
int i;
const WCHAR inW[] = {'i', 'n', '\0'};
- const WCHAR doW[] = {'d', 'o', '\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)
- || lstrcmpiW (WCMD_parameter (p, 3, NULL), doW)
- || (param1[0] != '%')) {
+ || (*cmdList) == NULL
+ || (*cmdList)->nextcommand == NULL
+ || (param1[0] != '%')
+ || (strlenW(param1) > 3)) {
WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
return;
}
- lstrcpynW (set, WCMD_parameter (p, 2, NULL), sizeof(set)/sizeof(WCHAR));
- WCMD_parameter (p, 4, &cmd);
- strcpyW (param, param1);
-/*
- * If the parameter within the set has a wildcard then search for matching files
- * otherwise do a literal substitution.
- */
+ /* Save away where the set of data starts and the variable */
+ strcpyW(variable, param1);
+ thisDepth = (*cmdList)->bracketDepth;
+ *cmdList = (*cmdList)->nextcommand;
+ setStart = (*cmdList);
- i = 0;
- while (*(item = WCMD_parameter (set, i, NULL))) {
- static const WCHAR wildcards[] = {'*','?','\0'};
- if (strpbrkW (item, wildcards)) {
- hff = FindFirstFile (item, &fd);
- if (hff == INVALID_HANDLE_VALUE) {
- return;
- }
- do {
- WCMD_execute (cmd, param, fd.cFileName, cmdList);
- } while (FindNextFile(hff, &fd) != 0);
- FindClose (hff);
- }
- else {
- WCMD_execute (cmd, param, item, cmdList);
- }
- i++;
+ /* Skip until the close bracket */
+ WINE_TRACE("Searching %p as the set\n", *cmdList);
+ while (*cmdList &&
+ (*cmdList)->command != NULL &&
+ (*cmdList)->bracketDepth > thisDepth) {
+ WINE_TRACE("Skipping %p which is part of the set\n", *cmdList);
+ *cmdList = (*cmdList)->nextcommand;
}
+
+ /* Skip the close bracket, if there is one */
+ if (*cmdList) *cmdList = (*cmdList)->nextcommand;
+
+ /* 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) ||
+ (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+ (*cmdList)->command, 3, doW, -1) != 2)) {
+ WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
+ 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 ' */
+
+ thisSet = setStart;
+ /* Loop through all set entries */
+ while (thisSet &&
+ thisSet->command != NULL &&
+ thisSet->bracketDepth >= thisDepth) {
+
+ /* Loop through all entries on the same line */
+ WCHAR *item;
+
+ WINE_TRACE("Processing for set %p\n", thisSet);
+ i = 0;
+ while (*(item = WCMD_parameter (thisSet->command, i, NULL))) {
+
+ /*
+ * If the parameter within the set has a wildcard then search for matching files
+ * otherwise do a literal substitution.
+ */
+ static const WCHAR wildcards[] = {'*','?','\0'};
+ CMD_LIST *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 {
+ 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);
+ }
+
+ WINE_TRACE("Post-command, cmdEnd = %p\n", cmdEnd);
+ cmdEnd = thisCmdStart;
+ i++;
+ }
+
+ /* Move onto the next set line */
+ thisSet = thisSet->nextcommand;
+ }
+
+ /* 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
+ value needs to be the NEXT command to execute, which it either is, or
+ we need to step over the closing bracket */
+ *cmdList = cmdEnd;
+ if (cmdEnd && cmdEnd->command == NULL) *cmdList = cmdEnd->nextcommand;
+}
+
+
+/*****************************************************************************
+ * WCMD_part_execute
+ *
+ * Execute a command, and any && or bracketed follow on to the command. The
+ * first command to be executed may not be at the front of the
+ * commands->thiscommand string (eg. it may point after a DO or ELSE
+ * Returns TRUE if something like exit or goto has aborted all processing
+ */
+void WCMD_part_execute(CMD_LIST **cmdList, WCHAR *firstcmd, WCHAR *variable,
+ WCHAR *value, BOOL isIF, BOOL conditionTRUE) {
+
+ CMD_LIST *curPosition = *cmdList;
+ int myDepth = (*cmdList)->bracketDepth;
+
+ WINE_TRACE("cmdList(%p), firstCmd(%p), with '%s'='%s', doIt(%d)\n",
+ cmdList, wine_dbgstr_w(firstcmd),
+ wine_dbgstr_w(variable), wine_dbgstr_w(value),
+ conditionTRUE);
+
+ /* Skip leading whitespace between condition and the command */
+ while (firstcmd && *firstcmd && (*firstcmd==' ' || *firstcmd=='\t')) firstcmd++;
+
+ /* Process the first command, if there is one */
+ if (conditionTRUE && firstcmd && *firstcmd) {
+ WCHAR *command = WCMD_strdupW(firstcmd);
+ WCMD_execute (firstcmd, variable, value, cmdList);
+ free (command);
+ }
+
+
+ /* If it didnt move the position, step to next command */
+ if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
+
+ /* Process any other parts of the command */
+ if (*cmdList) {
+ BOOL processThese = TRUE;
+
+ if (isIF) processThese = conditionTRUE;
+
+ while (*cmdList) {
+ const WCHAR ifElse[] = {'e','l','s','e',' ','\0'};
+
+ /* execute all appropriate commands */
+ curPosition = *cmdList;
+
+ WINE_TRACE("Processing cmdList(%p) - &(%d) bd(%d / %d)\n",
+ *cmdList,
+ (*cmdList)->isAmphersand,
+ (*cmdList)->bracketDepth, myDepth);
+
+ /* Execute any appended to the statement with &&'s */
+ if ((*cmdList)->isAmphersand) {
+ if (processThese) {
+ WCMD_execute ((*cmdList)->command, variable, value, cmdList);
+ }
+ if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
+
+ /* Execute any appended to the statement with (...) */
+ } else if ((*cmdList)->bracketDepth > myDepth) {
+ if (processThese) {
+ *cmdList = WCMD_process_commands(*cmdList, TRUE, variable, value);
+ WINE_TRACE("Back from processing commands, (next = %p)\n", *cmdList);
+ }
+ if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
+
+ /* End of the command - does 'ELSE ' follow as the next command? */
+ } else {
+ if (isIF && CompareString (LOCALE_USER_DEFAULT,
+ NORM_IGNORECASE | SORT_STRINGSORT,
+ (*cmdList)->command, 5, ifElse, -1) == 2) {
+
+ /* Swap between if and else processing */
+ processThese = !processThese;
+
+ /* Process the ELSE part */
+ if (processThese) {
+ WCHAR *cmd = ((*cmdList)->command) + strlenW(ifElse);
+
+ /* Skip leading whitespace between condition and the command */
+ while (*cmd && (*cmd==' ' || *cmd=='\t')) cmd++;
+ if (*cmd) {
+ WCMD_execute (cmd, variable, value, cmdList);
+ }
+ }
+ if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
+ } else {
+ WINE_TRACE("Found end of this IF statement (next = %p)\n", *cmdList);
+ break;
+ }
+ }
+ }
+ }
+ return;
}
/*****************************************************************************
@@ -631,22 +804,26 @@
WCHAR *new_cmd, *p, *s, *dup;
int size;
- size = strlenW (orig_cmd);
- new_cmd = (WCHAR *) LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, size);
- dup = s = WCMD_strdupW(orig_cmd);
+ if (param) {
+ size = (strlenW (orig_cmd) + 1) * sizeof(WCHAR);
+ new_cmd = (WCHAR *) LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, size);
+ dup = s = WCMD_strdupW(orig_cmd);
- while ((p = strstrW (s, param))) {
- *p = '\0';
- size += strlenW (subst);
- new_cmd = (WCHAR *) LocalReAlloc ((HANDLE)new_cmd, size, 0);
+ while ((p = strstrW (s, param))) {
+ *p = '\0';
+ size += strlenW (subst) * sizeof(WCHAR);
+ new_cmd = (WCHAR *) LocalReAlloc ((HANDLE)new_cmd, size, 0);
+ strcatW (new_cmd, s);
+ strcatW (new_cmd, subst);
+ s = p + strlenW (param);
+ }
strcatW (new_cmd, s);
- strcatW (new_cmd, subst);
- s = p + strlenW (param);
+ WCMD_process_command (new_cmd, cmdList);
+ free (dup);
+ LocalFree ((HANDLE)new_cmd);
+ } else {
+ WCMD_process_command (orig_cmd, cmdList);
}
- strcatW (new_cmd, s);
- WCMD_process_command (new_cmd, cmdList);
- free (dup);
- LocalFree ((HANDLE)new_cmd);
}
@@ -810,8 +987,6 @@
static const WCHAR existW[] = {'e','x','i','s','t','\0'};
static const WCHAR defdW[] = {'d','e','f','i','n','e','d','\0'};
static const WCHAR eqeqW[] = {'=','=','\0'};
- CMD_LIST *curPosition;
- int myDepth;
if (!lstrcmpiW (param1, notW)) {
negate = 1;
@@ -848,81 +1023,7 @@
/* Process rest of IF statement which is on the same line
Note: This may process all or some of the cmdList (eg a GOTO) */
- curPosition = *cmdList;
- myDepth = (*cmdList)->bracketDepth;
-
- if (test != negate) {
- WCHAR *cmd = command;
-
- /* Skip leading whitespace between condition and the command */
- while (cmd && *cmd && (*cmd==' ' || *cmd=='\t')) cmd++;
-
- if (cmd && *cmd) {
- command = WCMD_strdupW(cmd);
- WCMD_process_command (command, cmdList);
- free (command);
- }
- }
-
- /* If it didnt move the position, step to next command */
- if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
-
- /* Process any other parts of the IF */
- if (*cmdList) {
- BOOL processThese = (test != negate);
-
- while (*cmdList) {
- const WCHAR ifElse[] = {'e','l','s','e',' ','\0'};
-
- /* execute all appropriate commands */
- curPosition = *cmdList;
-
- WINE_TRACE("Processing cmdList(%p) - &(%d) bd(%d / %d)\n",
- *cmdList,
- (*cmdList)->isAmphersand,
- (*cmdList)->bracketDepth, myDepth);
-
- /* Execute any appended to the statement with &&'s */
- if ((*cmdList)->isAmphersand) {
- if (processThese) {
- WCMD_process_command((*cmdList)->command, cmdList);
- }
- if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
-
- /* Execute any appended to the statement with (...) */
- } else if ((*cmdList)->bracketDepth > myDepth) {
- if (processThese) {
- *cmdList = WCMD_process_commands(*cmdList, TRUE);
- WINE_TRACE("Back from processing commands, (next = %p)\n", *cmdList);
- }
- if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
-
- /* End of the command - does 'ELSE ' follow as the next command? */
- } else {
- if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
- (*cmdList)->command, 5, ifElse, -1) == 2) {
-
- /* Swap between if and else processing */
- processThese = !processThese;
-
- /* Process the ELSE part */
- if (processThese) {
- WCHAR *cmd = ((*cmdList)->command) + strlenW(ifElse);
-
- /* Skip leading whitespace between condition and the command */
- while (*cmd && (*cmd==' ' || *cmd=='\t')) cmd++;
- if (*cmd) {
- WCMD_process_command(cmd, cmdList);
- }
- }
- if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
- } else {
- WINE_TRACE("Found end of this IF statement (next = %p)\n", *cmdList);
- break;
- }
- }
- }
- }
+ WCMD_part_execute(cmdList, command, NULL, NULL, TRUE, (test != negate));
}
/****************************************************************************