[PATCH 5/7] [cmd] Fix for loops within for loops

Ann and Jason Edmeades jason at edmeades.me.uk
Mon Nov 5 16:25:12 CST 2012


Add support for multiple 'for' variables being in effect at the same
time, in preparation for tokens= support.

[Fixes bug 27741 and a few todos]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-patches/attachments/20121105/1e455b48/attachment-0001.html>
-------------- next part --------------
From 108f683f1af3b4bad28f515f6ab4d306b7efc80a Mon Sep 17 00:00:00 2001
From: Jason Edmeades <jason at edmeades.me.uk>
Date: Mon, 5 Nov 2012 21:43:08 +0000
Subject: [PATCH 5/7] [cmd] Fix for loops within for loops

Add support for multiple 'for' variables being in effect at the same
time, in preparation for tokens= support.

[Fixes bug 27741 and a few todos]
---
 programs/cmd/batch.c                     |   35 ++++++++-----
 programs/cmd/builtins.c                  |   77 ++++++++++++++++++++-------
 programs/cmd/tests/test_builtins.cmd.exp |   20 +++----
 programs/cmd/wcmd.h                      |   17 ++++--
 programs/cmd/wcmdmain.c                  |   84 ++++++++++++++----------------
 5 files changed, 141 insertions(+), 92 deletions(-)

diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c
index 62f726c..5bf7d1b 100644
--- a/programs/cmd/batch.c
+++ b/programs/cmd/batch.c
@@ -92,7 +92,7 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA
       /* Note: although this batch program itself may be called, we are not retrying
          the command as a result of a call failing to find a program, hence the
          retryCall parameter below is FALSE                                           */
-      WCMD_process_commands(toExecute, FALSE, NULL, NULL, FALSE);
+      WCMD_process_commands(toExecute, FALSE, FALSE);
       WCMD_free_commands(toExecute);
       toExecute = NULL;
   }
@@ -358,8 +358,8 @@ void WCMD_splitpath(const WCHAR* path, WCHAR* drv, WCHAR* dir, WCHAR* name, WCHA
 /****************************************************************************
  * WCMD_HandleTildaModifiers
  *
- * Handle the ~ modifiers when expanding %0-9 or (%a-z in for command)
- *    %~xxxxxV  (V=0-9 or A-Z)
+ * Handle the ~ modifiers when expanding %0-9 or (%a-z/A-Z in for command)
+ *    %~xxxxxV  (V=0-9 or A-Z, a-z)
  * Where xxxx is any combination of:
  *    ~ - Removes quotes
  *    f - Fully qualified path (assumes current dir if not drive\dir)
@@ -387,8 +387,8 @@ void WCMD_splitpath(const WCHAR* path, WCHAR* drv, WCHAR* dir, WCHAR* name, WCHA
  *  Hence search forwards until find an invalid modifier, and then
  *  backwards until find for variable or 0-9
  */
-void WCMD_HandleTildaModifiers(WCHAR **start, const WCHAR *forVariable,
-                               const WCHAR *forValue, BOOL justFors) {
+void WCMD_HandleTildaModifiers(WCHAR **start, BOOL justFors)
+{
 
 #define NUMMODIFIERS 11
   static const WCHAR validmodifiers[NUMMODIFIERS] = {
@@ -442,18 +442,19 @@ void WCMD_HandleTildaModifiers(WCHAR **start, const WCHAR *forVariable,
   }
 
   while (lastModifier > firstModifier) {
-    WINE_TRACE("Looking backwards for parameter id: %s / %s\n",
-               wine_dbgstr_w(lastModifier), wine_dbgstr_w(forVariable));
+    WINE_TRACE("Looking backwards for parameter id: %s\n",
+               wine_dbgstr_w(lastModifier));
 
     if (!justFors && context && (*lastModifier >= '0' && *lastModifier <= '9')) {
       /* Its a valid parameter identifier - OK */
       break;
 
-    } else if (forVariable && *lastModifier == *(forVariable+1)) {
+    } else {
+      int foridx = FOR_VAR_IDX(*lastModifier);
       /* Its a valid parameter identifier - OK */
-      break;
+      if ((foridx >= 0) && (forloopcontext.variable[foridx] != NULL)) break;
 
-    } else {
+      /* Its not a valid parameter identifier - step backwards */
       lastModifier--;
     }
   }
@@ -468,7 +469,8 @@ void WCMD_HandleTildaModifiers(WCHAR **start, const WCHAR *forVariable,
                             *lastModifier-'0' + context -> shift_count[*lastModifier-'0'],
                             NULL, FALSE, TRUE));
   } else {
-    strcpyW(outputparam, forValue);
+    int foridx = FOR_VAR_IDX(*lastModifier);
+    strcpyW(outputparam, forloopcontext.variable[foridx]);
   }
 
   /* So now, firstModifier points to beginning of modifiers, lastModifier
@@ -696,17 +698,24 @@ void WCMD_call (WCHAR *command) {
     if (context) {
 
       LARGE_INTEGER li;
+      FOR_CONTEXT oldcontext;
+
+      /* Save the for variable context, then start with an empty context
+         as for loop variables do not survive a call                    */
+      oldcontext = forloopcontext;
+      memset(&forloopcontext, 0, sizeof(forloopcontext));
 
       /* Save the current file position, call the same file,
          restore position                                    */
       li.QuadPart = 0;
       li.u.LowPart = SetFilePointer(context -> h, li.u.LowPart,
                      &li.u.HighPart, FILE_CURRENT);
-
       WCMD_batch (param1, command, TRUE, gotoLabel, context->h);
-
       SetFilePointer(context -> h, li.u.LowPart,
                      &li.u.HighPart, FILE_BEGIN);
+
+      /* Restore the for loop context */
+      forloopcontext = oldcontext;
     } else {
       WCMD_output_asis_stderr(WCMD_LoadMessage(WCMD_CALLINSCRIPT));
     }
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index 72d4f43..bcf19a3 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -1426,15 +1426,13 @@ void WCMD_echo (const WCHAR *args)
  * commands->thiscommand string (eg. it may point after a DO or ELSE)
  */
 static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
-                              const WCHAR *variable, const WCHAR *value,
                               BOOL isIF, BOOL executecmds)
 {
   CMD_LIST *curPosition = *cmdList;
   int myDepth = (*cmdList)->bracketDepth;
 
-  WINE_TRACE("cmdList(%p), firstCmd(%p), with variable '%s'='%s', doIt(%d)\n",
+  WINE_TRACE("cmdList(%p), firstCmd(%p), doIt(%d)\n",
              cmdList, wine_dbgstr_w(firstcmd),
-             wine_dbgstr_w(variable), wine_dbgstr_w(value),
              executecmds);
 
   /* Skip leading whitespace between condition and the command */
@@ -1443,7 +1441,7 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
   /* Process the first command, if there is one */
   if (executecmds && firstcmd && *firstcmd) {
     WCHAR *command = WCMD_strdupW(firstcmd);
-    WCMD_execute (firstcmd, (*cmdList)->redirects, variable, value, cmdList, FALSE);
+    WCMD_execute (firstcmd, (*cmdList)->redirects, cmdList, FALSE);
     HeapFree(GetProcessHeap(), 0, command);
   }
 
@@ -1471,15 +1469,15 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
       if ((*cmdList)->prevDelim == CMD_ONFAILURE ||
           (*cmdList)->prevDelim == CMD_ONSUCCESS) {
         if (processThese && (*cmdList)->command) {
-          WCMD_execute ((*cmdList)->command, (*cmdList)->redirects, variable,
-                        value, cmdList, FALSE);
+          WCMD_execute ((*cmdList)->command, (*cmdList)->redirects,
+                        cmdList, FALSE);
         }
         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, FALSE);
+          *cmdList = WCMD_process_commands(*cmdList, TRUE, FALSE);
           WINE_TRACE("Back from processing commands, (next = %p)\n", *cmdList);
         }
         if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
@@ -1501,7 +1499,7 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
             /* Skip leading whitespace between condition and the command */
             while (*cmd && (*cmd==' ' || *cmd=='\t')) cmd++;
             if (*cmd) {
-              WCMD_execute (cmd, (*cmdList)->redirects, variable, value, cmdList, FALSE);
+              WCMD_execute (cmd, (*cmdList)->redirects, cmdList, FALSE);
             }
           }
           if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
@@ -1703,7 +1701,7 @@ static void WCMD_add_dirstowalk(DIRECTORY_STACK *dirsToWalk) {
 static void WCMD_parse_line(CMD_LIST    *cmdStart,
                             const WCHAR *firstCmd,
                             CMD_LIST   **cmdEnd,
-                            const WCHAR *variable,
+                            const WCHAR  variable,
                             WCHAR       *buffer,
                             BOOL        *doExecuted,
                             int         *forf_skip,
@@ -1711,6 +1709,8 @@ static void WCMD_parse_line(CMD_LIST    *cmdStart,
                             WCHAR       *forf_delims) {
 
   WCHAR *parm, *where;
+  FOR_CONTEXT oldcontext;
+  int varidx;
 
   /* Skip lines if requested */
   if (*forf_skip) {
@@ -1718,18 +1718,29 @@ static void WCMD_parse_line(CMD_LIST    *cmdStart,
     return;
   }
 
+  /* Save away any existing for variable context (e.g. nested for loops) */
+  oldcontext = forloopcontext;
+
   /* Extract the parameter */
   parm = WCMD_parameter_with_delims(buffer, 0, &where, FALSE, FALSE, forf_delims);
   WINE_TRACE("Parsed parameter: %s from %s\n", wine_dbgstr_w(parm),
              wine_dbgstr_w(buffer));
 
+  /* FIXME: Use tokens= line to populate forloopcontext */
+  varidx = FOR_VAR_IDX(variable);
+  if (varidx >=0) forloopcontext.variable[varidx] = WCMD_strdupW(parm);
+
   if (where && where[0] != forf_eol) {
     CMD_LIST *thisCmdStart = cmdStart;
     *doExecuted = TRUE;
-    WCMD_part_execute(&thisCmdStart, firstCmd, variable, parm, FALSE, TRUE);
+    WCMD_part_execute(&thisCmdStart, firstCmd, FALSE, TRUE);
     *cmdEnd = thisCmdStart;
   }
 
+  if (varidx >=0) HeapFree(GetProcessHeap(), 0, forloopcontext.variable[varidx]);
+
+  /* Restore the original for variable contextx */
+  forloopcontext = oldcontext;
 }
 
 /**************************************************************************
@@ -1778,7 +1789,7 @@ static HANDLE WCMD_forf_getinputhandle(BOOL usebackq, WCHAR *itemstr, BOOL iscmd
     wsprintfW(temp_cmd, cmdslashcW, itemstr);
     WINE_TRACE("Issuing '%s' with redirs '%s'\n",
                wine_dbgstr_w(temp_cmd), wine_dbgstr_w(temp_str));
-    WCMD_execute (temp_cmd, temp_str, NULL, NULL, NULL, FALSE);
+    WCMD_execute (temp_cmd, temp_str, NULL, FALSE);
 
     /* Open the file, read line by line and process */
     hinput = CreateFileW(temp_file, GENERIC_READ, FILE_SHARE_READ,
@@ -1814,6 +1825,8 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
   static const WCHAR doW[] = {'d','o'};
   CMD_LIST *setStart, *thisSet, *cmdStart, *cmdEnd;
   WCHAR variable[4];
+  int   varidx = -1;
+  WCHAR *oldvariablevalue;
   WCHAR *firstCmd;
   int thisDepth;
   WCHAR optionsRoot[MAX_PATH];
@@ -1905,6 +1918,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
   /* Variable should follow */
   strcpyW(variable, thisArg);
   WINE_TRACE("Variable identified as %s\n", wine_dbgstr_w(variable));
+  varidx = FOR_VAR_IDX(variable[1]);
 
   /* Ensure line continues with IN */
   thisArg = WCMD_parameter(p, parameterNo++, NULL, FALSE, FALSE);
@@ -2022,8 +2036,16 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
                           strcpyW(fullitem, fd.cFileName);
                       }
                       doExecuted = TRUE;
-                      WCMD_part_execute (&thisCmdStart, firstCmd, variable,
-                                                   fullitem, FALSE, TRUE);
+
+                      /* Save away any existing for variable context (e.g. nested for loops)
+                         and restore it after executing the body of this for loop           */
+                      if (varidx >= 0) {
+                        oldvariablevalue = forloopcontext.variable[varidx];
+                        forloopcontext.variable[varidx] = fullitem;
+                      }
+                      WCMD_part_execute (&thisCmdStart, firstCmd, FALSE, TRUE);
+                      if (varidx >= 0) forloopcontext.variable[varidx] = oldvariablevalue;
+
                       cmdEnd = thisCmdStart;
                   }
                 } while (FindNextFileW(hff, &fd) != 0);
@@ -2031,7 +2053,16 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
               }
             } else {
               doExecuted = TRUE;
-              WCMD_part_execute(&thisCmdStart, firstCmd, variable, fullitem, FALSE, TRUE);
+
+              /* Save away any existing for variable context (e.g. nested for loops)
+                 and restore it after executing the body of this for loop           */
+              if (varidx >= 0) {
+                oldvariablevalue = forloopcontext.variable[varidx];
+                forloopcontext.variable[varidx] = fullitem;
+              }
+              WCMD_part_execute (&thisCmdStart, firstCmd, FALSE, TRUE);
+              if (varidx >= 0) forloopcontext.variable[varidx] = oldvariablevalue;
+
               cmdEnd = thisCmdStart;
             }
 
@@ -2075,7 +2106,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
 
               /* Read line by line until end of file */
               while (WCMD_fgets(buffer, sizeof(buffer)/sizeof(WCHAR), input)) {
-                WCMD_parse_line(cmdStart, firstCmd, &cmdEnd, variable, buffer, &doExecuted,
+                WCMD_parse_line(cmdStart, firstCmd, &cmdEnd, variable[1], buffer, &doExecuted,
                                 &forf_skip, forf_eol, forf_delims);
                 buffer[0] = 0;
               }
@@ -2103,7 +2134,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
 
           /* Copy the item away from the global buffer used by WCMD_parameter */
           strcpyW(buffer, itemStart);
-          WCMD_parse_line(cmdStart, firstCmd, &cmdEnd, variable, buffer, &doExecuted,
+          WCMD_parse_line(cmdStart, firstCmd, &cmdEnd, variable[1], buffer, &doExecuted,
                             &forf_skip, forf_eol, forf_delims);
 
           /* Only one string can be supplied in the whole set, abort future set processing */
@@ -2135,7 +2166,15 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
 
             thisCmdStart = cmdStart;
             doExecuted = TRUE;
-            WCMD_part_execute(&thisCmdStart, firstCmd, variable, thisNum, FALSE, TRUE);
+
+            /* Save away any existing for variable context (e.g. nested for loops)
+               and restore it after executing the body of this for loop           */
+            if (varidx >= 0) {
+              oldvariablevalue = forloopcontext.variable[varidx];
+              forloopcontext.variable[varidx] = thisNum;
+            }
+            WCMD_part_execute (&thisCmdStart, firstCmd, FALSE, TRUE);
+            if (varidx >= 0) forloopcontext.variable[varidx] = oldvariablevalue;
         }
         cmdEnd = thisCmdStart;
     }
@@ -2160,7 +2199,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
   if (!doExecuted) {
     thisCmdStart = cmdStart;
     WINE_TRACE("Skipping for loop commands due to no valid iterations\n");
-    WCMD_part_execute(&thisCmdStart, firstCmd, NULL, NULL, FALSE, FALSE);
+    WCMD_part_execute(&thisCmdStart, firstCmd, FALSE, FALSE);
     cmdEnd = thisCmdStart;
   }
 
@@ -2510,7 +2549,7 @@ void WCMD_if (WCHAR *p, CMD_LIST **cmdList)
 
   /* Process rest of IF statement which is on the same line
      Note: This may process all or some of the cmdList (eg a GOTO) */
-  WCMD_part_execute(cmdList, command, NULL, NULL, TRUE, (test != negate));
+  WCMD_part_execute(cmdList, command, TRUE, (test != negate));
   return;
 
 syntax_err:
diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp
index 6e05558..d3612b6 100644
--- a/programs/cmd/tests/test_builtins.cmd.exp
+++ b/programs/cmd/tests/test_builtins.cmd.exp
@@ -648,16 +648,16 @@ B'
 "A B"
 C
 --- imbricated FORs
- at todo_wine@X Y
- at todo_wine@X Y
- at todo_wine@A C
- at todo_wine@A D
- at todo_wine@B C
- at todo_wine@B D
- at todo_wine@A C
- at todo_wine@A D
- at todo_wine@B C
- at todo_wine@B D
+X Y
+X Y
+A C
+A D
+B C
+B D
+A C
+A D
+B C
+B D
 --- basic wildcards
 bazbaz
 --- for /d
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h
index 9922a8f..fb17122 100644
--- a/programs/cmd/wcmd.h
+++ b/programs/cmd/wcmd.h
@@ -112,7 +112,7 @@ WCHAR *WCMD_parameter_with_delims (WCHAR *s, int n, WCHAR **start, BOOL raw,
                                    BOOL wholecmdline, const WCHAR *delims);
 WCHAR *WCMD_skip_leading_spaces (WCHAR *string);
 BOOL WCMD_keyword_ws_found(const WCHAR *keyword, int len, const WCHAR *ptr);
-void WCMD_HandleTildaModifiers(WCHAR **start, const WCHAR *forVariable, const WCHAR *forValue, BOOL justFors);
+void WCMD_HandleTildaModifiers(WCHAR **start, BOOL justFors);
 
 void WCMD_splitpath(const WCHAR* path, WCHAR* drv, WCHAR* dir, WCHAR* name, WCHAR* ext);
 void WCMD_strip_quotes(WCHAR *cmd);
@@ -122,11 +122,9 @@ void WCMD_strsubstW(WCHAR *start, const WCHAR* next, const WCHAR* insert, int le
 BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars, LPDWORD charsRead);
 
 WCHAR    *WCMD_ReadAndParseLine(const WCHAR *initialcmd, CMD_LIST **output, HANDLE readFrom);
-CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket,
-                                const WCHAR *var, const WCHAR *val, BOOL retrycall);
+CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket, BOOL retrycall);
 void      WCMD_free_commands(CMD_LIST *cmds);
 void      WCMD_execute (const WCHAR *orig_command, const WCHAR *redirects,
-                        const WCHAR *parameter, const WCHAR *substitution,
                         CMD_LIST **cmdList, BOOL retrycall);
 
 /* Data structure to hold context when executing batch files */
@@ -163,6 +161,16 @@ typedef struct _DIRECTORY_STACK
   WCHAR  *fileName;
 } DIRECTORY_STACK;
 
+/* Data structure to for loop variables during for body execution, bearing
+   in mind that for loops can be nested                                    */
+#define MAX_FOR_VARIABLES 52
+#define FOR_VAR_IDX(c) (((c)>='a'&&(c)<='z')?((c)-'a'):\
+                        ((c)>='A'&&(c)<='Z')?(26+(c)-'A'):-1)
+
+typedef struct _FOR_CONTEXT {
+  WCHAR *variable[MAX_FOR_VARIABLES];	/* a-z then A-Z */
+} FOR_CONTEXT;
+
 /*
  * Global variables quals, param1, param2 contain the current qualifiers
  * (uppercased and concatenated) and parameters entered, with environment
@@ -171,6 +179,7 @@ typedef struct _DIRECTORY_STACK
 extern WCHAR quals[MAX_PATH], param1[MAXSTRING], param2[MAXSTRING];
 extern DWORD errorlevel;
 extern BATCH_CONTEXT *context;
+extern FOR_CONTEXT forloopcontext;
 
 #endif /* !RC_INVOKED */
 
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index 534e5a6..c59f686 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -40,6 +40,7 @@ BATCH_CONTEXT *context = NULL;
 DWORD errorlevel;
 WCHAR quals[MAX_PATH], param1[MAXSTRING], param2[MAXSTRING];
 BOOL  interactive;
+FOR_CONTEXT forloopcontext; /* The 'for' loop context */
 
 int defaultColor = 7;
 BOOL echo_mode = TRUE;
@@ -549,8 +550,8 @@ static inline BOOL WCMD_is_magic_envvar(const WCHAR *s, const WCHAR *magicvar)
  *
  *	Expands environment variables, allowing for WCHARacter substitution
  */
-static WCHAR *WCMD_expand_envvar(WCHAR *start,
-                                 const WCHAR *forVar, const WCHAR *forVal) {
+static WCHAR *WCMD_expand_envvar(WCHAR *start)
+{
     WCHAR *endOfVar = NULL, *s;
     WCHAR *colonpos = NULL;
     WCHAR thisVar[MAXSTRING];
@@ -565,8 +566,7 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start,
     static const WCHAR Random[]    = {'R','A','N','D','O','M','\0'};
     static const WCHAR Delims[]    = {'%',':','\0'};
 
-    WINE_TRACE("Expanding: %s (%s,%s)\n", wine_dbgstr_w(start),
-               wine_dbgstr_w(forVal), wine_dbgstr_w(forVar));
+    WINE_TRACE("Expanding: %s\n", wine_dbgstr_w(start));
 
     /* Find the end of the environment variable, and extract name */
     endOfVar = strpbrkW(start+1, Delims);
@@ -629,17 +629,6 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start,
       static const WCHAR fmt[] = {'%','d','\0'};
       wsprintfW(thisVarContents, fmt, rand() % 32768);
       len = strlenW(thisVarContents);
-
-    /* Look for a matching 'for' variable */
-    } else if (forVar &&
-               (CompareStringW(LOCALE_USER_DEFAULT,
-                               SORT_STRINGSORT,
-                               thisVar,
-                               (colonpos - thisVar) - 1,
-                               forVar, -1) == CSTR_EQUAL)) {
-      strcpyW(thisVarContents, forVal);
-      len = strlenW(thisVarContents);
-
     } else {
 
       len = ExpandEnvironmentStringsW(thisVar, thisVarContents,
@@ -802,8 +791,7 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start,
  * read in and not again, except for 'for' variable substitution.
  * eg. As evidence, "echo %1 && shift && echo %1" or "echo %%path%%"
  */
-static void handleExpansion(WCHAR *cmd, BOOL justFors,
-                            const WCHAR *forVariable, const WCHAR *forValue) {
+static void handleExpansion(WCHAR *cmd, BOOL justFors) {
 
   /* For commands in a context (batch program):                  */
   /*   Expand environment variables in a batch file %{0-9} first */
@@ -818,6 +806,15 @@ static void handleExpansion(WCHAR *cmd, BOOL justFors,
   WCHAR *t;
   int   i;
 
+  /* Display the FOR variables in effect */
+  for (i=0;i<52;i++) {
+    if (forloopcontext.variable[i]) {
+      WINE_TRACE("FOR variable context: %c = '%s'\n",
+                 i<26?i+'a':(i-26)+'A',
+                 wine_dbgstr_w(forloopcontext.variable[i]));
+    }
+  }
+
   while ((p = strchrW(p, '%'))) {
 
     WINE_TRACE("Translate command:%s %d (at: %s)\n",
@@ -833,7 +830,7 @@ static void handleExpansion(WCHAR *cmd, BOOL justFors,
 
     /* Replace %~ modifications if in batch program */
     } else if (*(p+1) == '~') {
-      WCMD_HandleTildaModifiers(&p, forVariable, forValue, justFors);
+      WCMD_HandleTildaModifiers(&p, justFors);
       p++;
 
     /* Replace use of %0...%9 if in batch program*/
@@ -853,20 +850,18 @@ static void handleExpansion(WCHAR *cmd, BOOL justFors,
       } else
         WCMD_strsubstW(p, p+2, NULL, 0);
 
-    } else if (forVariable &&
-               (CompareStringW(LOCALE_USER_DEFAULT,
-                               SORT_STRINGSORT,
-                               p,
-                               strlenW(forVariable),
-                               forVariable, -1) == CSTR_EQUAL)) {
-      WCMD_strsubstW(p, p + strlenW(forVariable), forValue, -1);
-
-    } else if (!justFors) {
-      p = WCMD_expand_envvar(p, forVariable, forValue);
-
-    /* In a FOR loop, see if this is the variable to replace */
-    } else { /* Ignore %'s on second pass of batch program */
-      p++;
+    } else {
+      int forvaridx = FOR_VAR_IDX(*(p+1));
+      if (forvaridx != -1 && forloopcontext.variable[forvaridx]) {
+        /* Replace the 2 characters, % and for variable character */
+        WCMD_strsubstW(p, p + 2, forloopcontext.variable[forvaridx], -1);
+      } else if (!justFors) {
+        p = WCMD_expand_envvar(p);
+
+      /* In a FOR loop, see if this is the variable to replace */
+      } else { /* Ignore %'s on second pass of batch program */
+        p++;
+      }
     }
   }
 
@@ -1224,7 +1219,7 @@ void WCMD_run_program (WCHAR *command, BOOL called)
 
     /* Parse the command string, without reading any more input */
     WCMD_ReadAndParseLine(command, &toExecute, INVALID_HANDLE_VALUE);
-    WCMD_process_commands(toExecute, FALSE, NULL, NULL, called);
+    WCMD_process_commands(toExecute, FALSE, called);
     WCMD_free_commands(toExecute);
     toExecute = NULL;
     return;
@@ -1248,7 +1243,6 @@ void WCMD_run_program (WCHAR *command, BOOL called)
  *       we are attempting this retry.
  */
 void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
-                   const WCHAR *forVariable, const WCHAR *forValue,
                    CMD_LIST **cmdList, BOOL retrycall)
 {
     WCHAR *cmd, *p, *redir;
@@ -1267,9 +1261,8 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
                                 STD_ERROR_HANDLE};
     BOOL prev_echo_mode, piped = FALSE;
 
-    WINE_TRACE("command on entry:%s (%p), with forVariable '%s'='%s'\n",
-               wine_dbgstr_w(command), cmdList,
-               wine_dbgstr_w(forVariable), wine_dbgstr_w(forValue));
+    WINE_TRACE("command on entry:%s (%p)\n",
+               wine_dbgstr_w(command), cmdList);
 
     /* If the next command is a pipe then we implement pipes by redirecting
        the output from this command to a temp file and input into the
@@ -1323,8 +1316,8 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
 
     /* Expand variables in command line mode only (batch mode will
        be expanded as the line is read in, except for 'for' loops) */
-    handleExpansion(new_cmd, (context != NULL), forVariable, forValue);
-    handleExpansion(new_redir, (context != NULL), forVariable, forValue);
+    handleExpansion(new_cmd, (context != NULL));
+    handleExpansion(new_redir, (context != NULL));
     cmd = new_cmd;
 
 /*
@@ -1847,7 +1840,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE
     }
 
     /* Replace env vars if in a batch context */
-    if (context) handleExpansion(extraSpace, FALSE, NULL, NULL);
+    if (context) handleExpansion(extraSpace, FALSE);
 
     /* Skip preceding whitespace */
     while (*curPos == ' ' || *curPos == '\t') curPos++;
@@ -2244,7 +2237,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE
 
         } while (*extraData == 0x00);
         curPos = extraSpace;
-        if (context) handleExpansion(extraSpace, FALSE, NULL, NULL);
+        if (context) handleExpansion(extraSpace, FALSE);
         /* Continue to echo commands IF echo is on and in batch program */
         if (context && echo_mode && extraSpace[0] && (extraSpace[0] != '@')) {
           WCMD_output_asis(extraSpace);
@@ -2265,7 +2258,6 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE
  * Process all the commands read in so far
  */
 CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket,
-                                const WCHAR *var, const WCHAR *val,
                                 BOOL retrycall) {
 
     int bdepth = -1;
@@ -2291,7 +2283,7 @@ CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket,
          Also, skip over any batch labels (eg. :fred)          */
       if (thisCmd->command && thisCmd->command[0] != ':') {
         WINE_TRACE("Executing command: '%s'\n", wine_dbgstr_w(thisCmd->command));
-        WCMD_execute (thisCmd->command, thisCmd->redirects, var, val, &thisCmd, retrycall);
+        WCMD_execute (thisCmd->command, thisCmd->redirects, &thisCmd, retrycall);
       }
 
       /* Step on unless the command itself already stepped on */
@@ -2581,7 +2573,7 @@ int wmain (int argc, WCHAR *argvW[])
 
       /* Parse the command string, without reading any more input */
       WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE);
-      WCMD_process_commands(toExecute, FALSE, NULL, NULL, FALSE);
+      WCMD_process_commands(toExecute, FALSE, FALSE);
       WCMD_free_commands(toExecute);
       toExecute = NULL;
 
@@ -2668,7 +2660,7 @@ int wmain (int argc, WCHAR *argvW[])
   if (opt_k) {
       /* Parse the command string, without reading any more input */
       WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE);
-      WCMD_process_commands(toExecute, FALSE, NULL, NULL, FALSE);
+      WCMD_process_commands(toExecute, FALSE, FALSE);
       WCMD_free_commands(toExecute);
       toExecute = NULL;
       HeapFree(GetProcessHeap(), 0, cmd);
@@ -2688,7 +2680,7 @@ int wmain (int argc, WCHAR *argvW[])
     if (echo_mode) WCMD_show_prompt();
     if (!WCMD_ReadAndParseLine(NULL, &toExecute, GetStdHandle(STD_INPUT_HANDLE)))
       break;
-    WCMD_process_commands(toExecute, FALSE, NULL, NULL, FALSE);
+    WCMD_process_commands(toExecute, FALSE, FALSE);
     WCMD_free_commands(toExecute);
     toExecute = NULL;
   }
-- 
1.7.9.5


More information about the wine-patches mailing list