https://git.reactos.org/?p=reactos.git;a=commitdiff;h=5d5a1a455c9e8b158e4c6…
commit 5d5a1a455c9e8b158e4c6f4c51a1ee20e0da95f0
Author:     Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Sat Jul 4 22:22:07 2020 +0200
Commit:     Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Sat Sep 19 19:44:56 2020 +0200
    [CMD] FOR: Fix a bug when parsing the "delims=" option in FOR loops.
    Suppose the following FOR-loop command, to be run from the command-line
    (if using a batch file, double each percent '%' sign):
      FOR %l IN ("a,b,c,d,e" "f,g,h,i,j") DO (
        FOR /F "delims=, tokens=1-3*" %a IN (%l) DO @echo %a-%b-%c-%d
      )
    The outermost FOR-loop enumerates the two strings "a,b,c,d,e" and
    "f,g,h,i,j" (placed in %l), and parse each of these in turn, splitting
    them at each specified delimiter (here only one character) ',' and storing
    the results in consecutive tokens %a, %b, %c, %d, with the last token %d
    containing all the remaining string (non-split).
    The expected result is:
      a-b-c-d,e
      f-g-h-i,j
    However, due to the way the delimiters string specified by the "delims="
    option is stored (no stack/heap duplication of the FOR-option substring,
    but reading from it directly), during the first run of the innermost
    FOR-loop, the option string "delims=, tokens=1-3*" was truncated to just
    after the ',' due to the erroneous "delims=" parsing, so that when
this
    FOR-loop ran for a second time (to deal with the second string), the option
    string was already erroneously truncated, without the "tokens=..." part,
    so that the parsing results were not stored in the tokens and resulting in:
      a-b-c-d,e
      f-%b-%c-%d
    instead. The solution is to save where the "delims=" string needs to be
    cut, but wait until running the actual FOR-loop to terminate it (and
    saving the original character too), run the FOR-loop body, and then
    restore the original character where termination took place. This allows
    having the FOR-loop option string valid for the next execution of the
    FOR-loop.
---
 base/shell/cmd/for.c | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)
diff --git a/base/shell/cmd/for.c b/base/shell/cmd/for.c
index 67a9240c787..300588bed31 100644
--- a/base/shell/cmd/for.c
+++ b/base/shell/cmd/for.c
@@ -121,6 +121,8 @@ static LPTSTR ReadFileContents(FILE *InputFile, TCHAR *Buffer)
 static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
 {
     LPTSTR Delims = _T(" \t");
+    LPTSTR DelimsEndPtr = NULL;
+    TCHAR  DelimsEndChr = _T('\0');
     TCHAR Eol = _T(';');
     INT SkipLines = 0;
     DWORD Tokens = (1 << 1);
@@ -148,8 +150,13 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
             else if (_tcsnicmp(Param, _T("delims="), 7) == 0)
             {
                 Param += 7;
-                /* delims=xxx: Specifies the list of characters that separate tokens */
+                /*
+                 * delims=xxx: Specifies the list of characters that separate tokens.
+                 * This option does not cumulate: only the latest 'delims='
specification
+                 * is taken into account.
+                 */
                 Delims = Param;
+                DelimsEndPtr = NULL;
                 while (*Param && *Param != Quote)
                 {
                     if (*Param == _T(' '))
@@ -158,13 +165,19 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
                         Param += _tcsspn(Param, _T(" "));
                         /* Exclude trailing spaces if this is not the last parameter */
                         if (*Param && *Param != Quote)
-                            *FirstSpace = _T('\0');
+                        {
+                            /* Save where the delimiters specification string ends */
+                            DelimsEndPtr = FirstSpace;
+                        }
                         break;
                     }
                     Param++;
                 }
                 if (*Param == Quote)
-                    *Param++ = _T('\0');
+                {
+                    /* Save where the delimiters specification string ends */
+                    DelimsEndPtr = Param++;
+                }
             }
             else if (_tcsnicmp(Param, _T("eol="), 4) == 0)
             {
@@ -301,6 +314,13 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
             return 1;
         }
+        /* Patch the delimiters string */
+        if (DelimsEndPtr)
+        {
+            DelimsEndChr = *DelimsEndPtr;
+            *DelimsEndPtr = _T('\0');
+        }
+
         /* Loop over the input line by line */
         for (In = FullInput, Skip = SkipLines;
              !ExitingOrGoto(Cmd) && (In != NULL);
@@ -343,6 +363,10 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
                 Ret = RunInstance(Cmd);
         }
+        /* Restore the delimiters string */
+        if (DelimsEndPtr)
+            *DelimsEndPtr = DelimsEndChr;
+
         cmd_free(FullInput);
     }