mirror of
https://github.com/reactos/reactos.git
synced 2024-11-02 04:37:32 +00:00
37bda06eed
- Fail if no parameter is provided. - The "CALL :label args..." syntax is available only when command extensions are enabled. Fail if this syntax is used outside of a batch context. - Reparse the CALL command parameter with the command parser, in order to accurately parse and interpret it as a possible command (including escape carets, etc...) and not duplicate the logic. ** CURRENT Windows' CMD-compatibility LIMITATION ** (may be lifted in a "ROS-specific" running mode of CMD): only allow standard commands to be specified as parameter of the CALL command. This reparsing behaviour can be observed in Windows' CMD, by dumping the interpreted commands after enabling the cmd!fDumpParse flag from a debugger (using public symbols). - When reparsing, we should tell the parser to NOT ignore lines that start with a colon, because in this situation these are to be considered as valid "commands" (for parsing "CALL :label"). * For Windows' CMD-compatibility, the remaining escape carets need to be doubled again so that, after the new parser step, they are escaped back to their original form. But then we also need to do it the "buggy" way à la Windows, where carets in quotes are doubled either! However when being re-parsed, since they are in quotes they remain doubled!! (see "Phase 6" in https://stackoverflow.com/a/4095133/13530036 ). * A MSCMD_CALL_QUIRKS define allows to disable this buggy behaviour, and instead tell the parser to not not interpret the escape carets. - When initializing a new batch context when the "CALL :label" syntax is used, ensure that we reuse the same batch file position pointer as its parent, so as to have correct call label ordering behaviour. That is, :label ECHO hi CALL :label :label ECHO bye should display: hi bye bye i.e., the CALL calls the second label instead of the first one (and thus entering into an infinite loop). Finally, the "CALL :label" syntax strips the first ':' away, so, as a side-effect, the command "CALL :EOF" fails (otherwise it would perform a "GOTO :EOF" and succeeds), while "CALL ::EOF" succeeds. Fixes some cmd_winetests.
197 lines
5 KiB
C
197 lines
5 KiB
C
/*
|
|
* GOTO.C - goto internal batch command.
|
|
*
|
|
* History:
|
|
*
|
|
* 16 Jul 1998 (Hans B Pufal)
|
|
* started.
|
|
*
|
|
* 16 Jul 1998 (John P Price)
|
|
* Separated commands into individual files.
|
|
*
|
|
* 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
|
|
* added config.h include
|
|
*
|
|
* 28 Jul 1998 (Hans B Pufal) [HBP_003]
|
|
* Terminate label on first space character, use only first 8 chars of
|
|
* label string
|
|
*
|
|
* 24-Jan-1999 (Eric Kohl)
|
|
* Unicode and redirection safe!
|
|
*
|
|
* 27-Jan-1999 (Eric Kohl)
|
|
* Added help text ("/?").
|
|
*
|
|
* 28-Apr-2005 (Magnus Olsen <magnus@greatlord.com>)
|
|
* Remove all hardcoded strings in En.rc
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
/*
|
|
* Perform GOTO command.
|
|
*
|
|
* Only valid when a batch context is active.
|
|
*/
|
|
INT cmd_goto(LPTSTR param)
|
|
{
|
|
LPTSTR label, tmp;
|
|
DWORD dwCurrPos;
|
|
BOOL bRetry;
|
|
|
|
TRACE("cmd_goto(\'%s\')\n", debugstr_aw(param));
|
|
|
|
/*
|
|
* Keep the help message handling here too.
|
|
* This allows us to reproduce the Windows' CMD "bug"
|
|
* (from a batch script):
|
|
*
|
|
* SET label=/?
|
|
* CALL :%%label%%
|
|
*
|
|
* calls GOTO help, due to how CALL :label functionality
|
|
* is internally implemented.
|
|
*
|
|
* See https://stackoverflow.com/q/31987023/13530036
|
|
* for more details.
|
|
*
|
|
* Note that the choice of help parsing forbids
|
|
* any label containing '/?' in it.
|
|
*/
|
|
if (_tcsstr(param, _T("/?")))
|
|
{
|
|
ConOutResPaging(TRUE, STRING_GOTO_HELP1);
|
|
return 0;
|
|
}
|
|
|
|
/* If not in batch, fail */
|
|
if (bc == NULL)
|
|
return 1;
|
|
|
|
/* Fail if no label has been provided */
|
|
if (*param == _T('\0'))
|
|
{
|
|
ConErrResPrintf(STRING_GOTO_ERROR1);
|
|
ExitBatch();
|
|
return 1;
|
|
}
|
|
|
|
/* Strip leading whitespace */
|
|
while (_istspace(*param))
|
|
++param;
|
|
|
|
/* Support jumping to the end of the file, only if extensions are enabled */
|
|
if (bEnableExtensions &&
|
|
(_tcsnicmp(param, _T(":EOF"), 4) == 0) &&
|
|
(!param[4] || _istspace(param[4])))
|
|
{
|
|
/* Position at the end of the batch file */
|
|
bc->mempos = bc->memsize;
|
|
|
|
/* Do not process any more parts of a compound command */
|
|
bc->current = NULL;
|
|
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
|
|
* until reaching back our old current position.
|
|
*/
|
|
bRetry = FALSE;
|
|
dwCurrPos = bc->mempos;
|
|
|
|
retry:
|
|
while (BatchGetString(textline, ARRAYSIZE(textline)))
|
|
{
|
|
if (bRetry && (bc->mempos >= dwCurrPos))
|
|
break;
|
|
|
|
#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 */
|
|
}
|
|
|
|
++tmp;
|
|
}
|
|
*tmp = _T('\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;
|
|
return 0;
|
|
}
|
|
}
|
|
if (!bRetry && (bc->mempos >= bc->memsize))
|
|
{
|
|
bRetry = TRUE;
|
|
bc->mempos = 0;
|
|
goto retry;
|
|
}
|
|
|
|
NotFound:
|
|
ConErrResPrintf(STRING_GOTO_ERROR2, param);
|
|
ExitBatch();
|
|
return 1;
|
|
}
|
|
|
|
/* EOF */
|