https://git.reactos.org/?p=reactos.git;a=commitdiff;h=2e4c8c019ebe5d78dbcf43...
commit 2e4c8c019ebe5d78dbcf4397ffa0caa4eabb5d0d Author: Hermès Bélusca-Maïto hermes.belusca-maito@reactos.org AuthorDate: Wed Jul 22 01:16:53 2020 +0200 Commit: Hermès Bélusca-Maïto hermes.belusca-maito@reactos.org CommitDate: Wed Aug 19 20:36:01 2020 +0200
[CMD] GOTO: Fix label parsing.
We note two things, when CMD searches for the corresponding label in the batch file: - the first character of the line is always ignored, unless it's a colon; - the escape caret ^ is supported and interpreted.
Fixes some cmd_winetests. --- base/shell/cmd/batch.c | 4 +- base/shell/cmd/cmd.h | 4 ++ base/shell/cmd/goto.c | 97 ++++++++++++++++++++++++++++++++++--------------- base/shell/cmd/parser.c | 3 -- 4 files changed, 74 insertions(+), 34 deletions(-)
diff --git a/base/shell/cmd/batch.c b/base/shell/cmd/batch.c index a5c31aece09..184911c60a6 100644 --- a/base/shell/cmd/batch.c +++ b/base/shell/cmd/batch.c @@ -131,7 +131,7 @@ static LPTSTR BatchParams(LPTSTR s1, LPTSTR s2) BOOL inquotes = FALSE;
/* Find next parameter */ - while (_istspace(*s2) || (*s2 && _tcschr(_T(",;="), *s2))) + while (_istspace(*s2) || (*s2 && _tcschr(STANDARD_SEPS, *s2))) s2++; if (!*s2) break; @@ -139,7 +139,7 @@ static LPTSTR BatchParams(LPTSTR s1, LPTSTR s2) /* Copy it */ do { - if (!inquotes && (_istspace(*s2) || _tcschr(_T(",;="), *s2))) + if (!inquotes && (_istspace(*s2) || _tcschr(STANDARD_SEPS, *s2))) break; inquotes ^= (*s2 == _T('"')); *s1++ = *s2++; diff --git a/base/shell/cmd/cmd.h b/base/shell/cmd/cmd.h index a6e780bdc6e..f547389024e 100644 --- a/base/shell/cmd/cmd.h +++ b/base/shell/cmd/cmd.h @@ -301,6 +301,10 @@ INT cmd_move (LPTSTR); INT CommandMsgbox (LPTSTR);
/* Prototypes from PARSER.C */ + +/* These three characters act like spaces to the parser in most contexts */ +#define STANDARD_SEPS _T(",;=") + enum { C_COMMAND, C_QUIET, C_BLOCK, C_MULTI, C_OR, C_AND, C_PIPE, C_IF, C_FOR }; typedef struct _PARSED_COMMAND { diff --git a/base/shell/cmd/goto.c b/base/shell/cmd/goto.c index 5831b25e22f..5174b62417f 100644 --- a/base/shell/cmd/goto.c +++ b/base/shell/cmd/goto.c @@ -35,7 +35,7 @@ */ INT cmd_goto(LPTSTR param) { - LPTSTR tmp, tmp2; + LPTSTR label, tmp; DWORD dwCurrPos; BOOL bRetry;
@@ -59,11 +59,9 @@ INT cmd_goto(LPTSTR param) return 1; }
- /* Terminate label at first space char */ - tmp = param + 1; - while (!_istcntrl(*tmp) && !_istspace(*tmp) && (*tmp != _T(':'))) - ++tmp; - *tmp = _T('\0'); + /* Strip leading whitespace */ + while (_istspace(*param)) + ++param;
/* Support jumping to the end of the file, only if extensions are enabled */ if (bEnableExtensions && @@ -78,6 +76,23 @@ INT cmd_goto(LPTSTR param) return 0; }
+ /* Skip the first colon or plus sign */ + if (*param == _T(':') || *param == _T('+')) + ++param; + /* Terminate the label at the first delimiter character */ + tmp = param; + while (!_istcntrl(*tmp) && !_istspace(*tmp) && + !_tcschr(_T(":+"), *tmp) && !_tcschr(STANDARD_SEPS, *tmp) /* && + !_tcschr(_T("&|<>"), *tmp) */) + { + ++tmp; + } + *tmp = _T('\0'); + + /* If we don't have any label, bail out */ + if (!*param) + goto NotFound; + /* * Search the next label starting our position, until the end of the file. * If none has been found, restart at the beginning of the file, and continue @@ -89,36 +104,59 @@ INT cmd_goto(LPTSTR param) retry: while (BatchGetString(textline, ARRAYSIZE(textline))) { - INT pos; - INT_PTR size; - if (bRetry && (bc->mempos >= dwCurrPos)) break;
- /* Strip out any trailing spaces or control chars */ - tmp = textline + _tcslen(textline) - 1; - while (tmp > textline && (_istcntrl(*tmp) || _istspace(*tmp) || (*tmp == _T(':')))) - --tmp; - *(tmp + 1) = _T('\0'); +#if 0 + /* If this is not a label, continue searching */ + if (!_tcschr(textline, _T(':'))) + continue; +#endif + + label = textline; + + /* A bug in Windows' CMD makes it always ignore the + * first character of the line, unless it's a colon. */ + if (*label != _T(':')) + ++label; + + /* Strip any leading whitespace */ + while (_istspace(*label)) + ++label; + + /* If this is not a label, continue searching */ + if (*label != _T(':')) + continue; + + /* Skip the first colon or plus sign */ +#if 0 + if (*label == _T(':') || *label == _T('+')) + ++label; +#endif + ++label; + /* Strip any whitespace between the colon and the label */ + while (_istspace(*label)) + ++label; + /* Terminate the label at the first delimiter character */ + tmp = label; + while (!_istcntrl(*tmp) && !_istspace(*tmp) && + !_tcschr(_T(":+"), *tmp) && !_tcschr(STANDARD_SEPS, *tmp) && + !_tcschr(_T("&|<>"), *tmp)) + { + /* Support the escape caret */ + if (*tmp == _T('^')) + { + /* Move the buffer back one character */ + memmove(tmp, tmp + 1, (_tcslen(tmp + 1) + 1) * sizeof(TCHAR)); + /* We will ignore the new character */ + }
- /* Then leading spaces... */ - tmp = textline; - while (_istspace(*tmp)) ++tmp; - - /* All space after leading space terminate the string */ - size = _tcslen(tmp) - 1; - pos = 0; - while (tmp + pos < tmp + size) - { - if (_istspace(tmp[pos])) - tmp[pos]=_T('\0'); - ++pos; } + *tmp = _T('\0');
- tmp2 = param; - /* Use whole label name */ - if ((*tmp == _T(':')) && ((_tcsicmp(++tmp, param) == 0) || (_tcsicmp(tmp, ++tmp2) == 0))) + /* Jump if the labels are identical */ + if (_tcsicmp(label, param) == 0) { /* Do not process any more parts of a compound command */ bc->current = NULL; @@ -132,6 +170,7 @@ retry: goto retry; }
+NotFound: ConErrResPrintf(STRING_GOTO_ERROR2, param); ExitBatch(); return 1; diff --git a/base/shell/cmd/parser.c b/base/shell/cmd/parser.c index e25b63aa91b..20310c77830 100644 --- a/base/shell/cmd/parser.c +++ b/base/shell/cmd/parser.c @@ -39,9 +39,6 @@ static const TCHAR *const IfOperatorString[] = #define IF_MAX_COMPARISON IF_NEQ };
-/* These three characters act like spaces to the parser in most contexts */ -#define STANDARD_SEPS _T(",;=") - static BOOL IsSeparator(TCHAR Char) { return _istspace(Char) || (Char && _tcschr(STANDARD_SEPS, Char));