https://git.reactos.org/?p=reactos.git;a=commitdiff;h=5d5a1a455c9e8b158e4c6f...
commit 5d5a1a455c9e8b158e4c6f4c51a1ee20e0da95f0 Author: Hermès Bélusca-Maïto hermes.belusca-maito@reactos.org AuthorDate: Sat Jul 4 22:22:07 2020 +0200 Commit: Hermès Bélusca-Maïto hermes.belusca-maito@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); }