https://git.reactos.org/?p=reactos.git;a=commitdiff;h=2e4c8c019ebe5d78dbcf4…
commit 2e4c8c019ebe5d78dbcf4397ffa0caa4eabb5d0d
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Wed Jul 22 01:16:53 2020 +0200
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)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));