[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.
This commit is contained in:
Hermès Bélusca-Maïto 2020-07-22 01:16:53 +02:00
parent 71cd64d66a
commit 2e4c8c019e
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
4 changed files with 74 additions and 34 deletions

View file

@ -131,7 +131,7 @@ static LPTSTR BatchParams(LPTSTR s1, LPTSTR s2)
BOOL inquotes = FALSE; BOOL inquotes = FALSE;
/* Find next parameter */ /* Find next parameter */
while (_istspace(*s2) || (*s2 && _tcschr(_T(",;="), *s2))) while (_istspace(*s2) || (*s2 && _tcschr(STANDARD_SEPS, *s2)))
s2++; s2++;
if (!*s2) if (!*s2)
break; break;
@ -139,7 +139,7 @@ static LPTSTR BatchParams(LPTSTR s1, LPTSTR s2)
/* Copy it */ /* Copy it */
do do
{ {
if (!inquotes && (_istspace(*s2) || _tcschr(_T(",;="), *s2))) if (!inquotes && (_istspace(*s2) || _tcschr(STANDARD_SEPS, *s2)))
break; break;
inquotes ^= (*s2 == _T('"')); inquotes ^= (*s2 == _T('"'));
*s1++ = *s2++; *s1++ = *s2++;

View file

@ -301,6 +301,10 @@ INT cmd_move (LPTSTR);
INT CommandMsgbox (LPTSTR); INT CommandMsgbox (LPTSTR);
/* Prototypes from PARSER.C */ /* 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 }; enum { C_COMMAND, C_QUIET, C_BLOCK, C_MULTI, C_OR, C_AND, C_PIPE, C_IF, C_FOR };
typedef struct _PARSED_COMMAND typedef struct _PARSED_COMMAND
{ {

View file

@ -35,7 +35,7 @@
*/ */
INT cmd_goto(LPTSTR param) INT cmd_goto(LPTSTR param)
{ {
LPTSTR tmp, tmp2; LPTSTR label, tmp;
DWORD dwCurrPos; DWORD dwCurrPos;
BOOL bRetry; BOOL bRetry;
@ -59,11 +59,9 @@ INT cmd_goto(LPTSTR param)
return 1; return 1;
} }
/* Terminate label at first space char */ /* Strip leading whitespace */
tmp = param + 1; while (_istspace(*param))
while (!_istcntrl(*tmp) && !_istspace(*tmp) && (*tmp != _T(':'))) ++param;
++tmp;
*tmp = _T('\0');
/* Support jumping to the end of the file, only if extensions are enabled */ /* Support jumping to the end of the file, only if extensions are enabled */
if (bEnableExtensions && if (bEnableExtensions &&
@ -78,6 +76,23 @@ INT cmd_goto(LPTSTR param)
return 0; 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. * 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 * If none has been found, restart at the beginning of the file, and continue
@ -89,36 +104,59 @@ INT cmd_goto(LPTSTR param)
retry: retry:
while (BatchGetString(textline, ARRAYSIZE(textline))) while (BatchGetString(textline, ARRAYSIZE(textline)))
{ {
INT pos;
INT_PTR size;
if (bRetry && (bc->mempos >= dwCurrPos)) if (bRetry && (bc->mempos >= dwCurrPos))
break; break;
/* Strip out any trailing spaces or control chars */ #if 0
tmp = textline + _tcslen(textline) - 1; /* If this is not a label, continue searching */
while (tmp > textline && (_istcntrl(*tmp) || _istspace(*tmp) || (*tmp == _T(':')))) if (!_tcschr(textline, _T(':')))
--tmp; continue;
*(tmp + 1) = _T('\0'); #endif
/* Then leading spaces... */ label = textline;
tmp = textline;
while (_istspace(*tmp))
++tmp;
/* All space after leading space terminate the string */ /* A bug in Windows' CMD makes it always ignore the
size = _tcslen(tmp) - 1; * first character of the line, unless it's a colon. */
pos = 0; if (*label != _T(':'))
while (tmp + pos < tmp + size) ++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))
{ {
if (_istspace(tmp[pos])) /* Support the escape caret */
tmp[pos]=_T('\0'); if (*tmp == _T('^'))
++pos; {
/* Move the buffer back one character */
memmove(tmp, tmp + 1, (_tcslen(tmp + 1) + 1) * sizeof(TCHAR));
/* We will ignore the new character */
} }
tmp2 = param; ++tmp;
/* Use whole label name */ }
if ((*tmp == _T(':')) && ((_tcsicmp(++tmp, param) == 0) || (_tcsicmp(tmp, ++tmp2) == 0))) *tmp = _T('\0');
/* Jump if the labels are identical */
if (_tcsicmp(label, param) == 0)
{ {
/* Do not process any more parts of a compound command */ /* Do not process any more parts of a compound command */
bc->current = NULL; bc->current = NULL;
@ -132,6 +170,7 @@ retry:
goto retry; goto retry;
} }
NotFound:
ConErrResPrintf(STRING_GOTO_ERROR2, param); ConErrResPrintf(STRING_GOTO_ERROR2, param);
ExitBatch(); ExitBatch();
return 1; return 1;

View file

@ -39,9 +39,6 @@ static const TCHAR *const IfOperatorString[] =
#define IF_MAX_COMPARISON IF_NEQ #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) static BOOL IsSeparator(TCHAR Char)
{ {
return _istspace(Char) || (Char && _tcschr(STANDARD_SEPS, Char)); return _istspace(Char) || (Char && _tcschr(STANDARD_SEPS, Char));