[CMD] Fixes for Batch error execution control flow.

CORE-13713 CORE-13736

- In case execution of all batch contexts is stopped (by selecting "All"
  at the Ctrl-C/Ctrl-Break prompt), notify as well the CheckCtrlBreak()
  signal handler once there are no more batch contexts (this in effect
  resets the internal 'bLeaveAll' static flag in CheckCtrlBreak).
  This is an adaptation of the fix present in FreeCOM 1.5, first
  described in https://gcfl.net/FreeDOS/command.com/bugs074g.html .

- Introduce a ParseErrorEx() helper that sets the 'bParseError' flag and
  displays a customized syntax-error message, only for the first syntax
  error encountered. Implement ParseError() around the *Ex function.

- In batch mode, echo the original pre-parsed batch file line if a parse
  error has been encountered.

- When running a compound command - including IF, FOR, command blocks -,
  and that control flow is modified by any CALL/GOTO/EXIT command,
  detect this while running the compound command so as to stop it and go
  back to the main batch execution loop, that will then set up the actual
  new command to run.

- In GOTO, do not process any more parts of a compound command only when
  we have found a valid label.
This commit is contained in:
Hermès Bélusca-Maïto 2020-05-18 02:05:53 +02:00
parent 7f8792e005
commit 6eb1cae348
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
9 changed files with 119 additions and 33 deletions

View file

@ -74,11 +74,12 @@ static BOOL GetNextElement(TCHAR **pStart, TCHAR **pEnd)
ExecuteCommandWithEcho((Cmd)->Subcommands)
/* Check if this FOR should be terminated early */
static BOOL Exiting(PARSED_COMMAND *Cmd)
{
/* Someone might have removed our context */
return bCtrlBreak || fc != Cmd->For.Context;
}
#define Exiting(Cmd) \
/* Someone might have removed our context */ \
(bCtrlBreak || (fc != (Cmd)->For.Context))
/* Take also GOTO jumps into account */
#define ExitingOrGoto(Cmd) \
(Exiting(Cmd) || (bc && bc->current == NULL))
/* Read the contents of a text file into memory,
* dynamically allocating enough space to hold it all */
@ -251,7 +252,7 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
/* Loop over each file */
End = List;
while (GetNextElement(&Start, &End))
while (!ExitingOrGoto(Cmd) && GetNextElement(&Start, &End))
{
FILE *InputFile;
LPTSTR FullInput, In, NextLine;
@ -301,9 +302,9 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
}
/* Loop over the input line by line */
In = FullInput;
Skip = SkipLines;
do
for (In = FullInput, Skip = SkipLines;
!ExitingOrGoto(Cmd) && (In != NULL);
In = NextLine)
{
DWORD RemainingTokens = Tokens;
LPTSTR *CurVar = Variables;
@ -340,7 +341,8 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
/* Don't run unless the line had enough tokens to fill at least one variable */
if (*Variables[0])
Ret = RunInstance(Cmd);
} while (!Exiting(Cmd) && (In = NextLine) != NULL);
}
cmd_free(FullInput);
}
@ -360,6 +362,11 @@ static INT ForLoop(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
params[i] = _tcstol(Start, NULL, 0);
i = params[START];
/*
* Windows' CMD compatibility:
* Contrary to the other FOR-loops, FOR /L does not check
* whether a GOTO has been done, and will continue to loop.
*/
while (!Exiting(Cmd) &&
(params[STEP] >= 0 ? (i <= params[END]) : (i >= params[END])))
{
@ -379,7 +386,7 @@ static INT ForDir(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *BufPos
INT Ret = 0;
TCHAR *Start, *End = List;
while (!Exiting(Cmd) && GetNextElement(&Start, &End))
while (!ExitingOrGoto(Cmd) && GetNextElement(&Start, &End))
{
if (BufPos + (End - Start) > &Buffer[CMDLINE_LENGTH])
continue;
@ -410,7 +417,7 @@ static INT ForDir(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *BufPos
continue;
_tcscpy(FilePart, w32fd.cFileName);
Ret = RunInstance(Cmd);
} while (!Exiting(Cmd) && FindNextFile(hFind, &w32fd));
} while (!ExitingOrGoto(Cmd) && FindNextFile(hFind, &w32fd));
FindClose(hFind);
}
else
@ -436,6 +443,12 @@ static INT ForRecursive(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *
Ret = ForDir(Cmd, List, Buffer, BufPos);
/* NOTE (We don't apply Windows' CMD compatibility here):
* Windows' CMD does not check whether a GOTO has been done,
* and will continue to loop. */
if (ExitingOrGoto(Cmd))
return Ret;
_tcscpy(BufPos, _T("*"));
hFind = FindFirstFile(Buffer, &w32fd);
if (hFind == INVALID_HANDLE_VALUE)
@ -448,7 +461,11 @@ static INT ForRecursive(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *
_tcscmp(w32fd.cFileName, _T("..")) == 0)
continue;
Ret = ForRecursive(Cmd, List, Buffer, _stpcpy(BufPos, w32fd.cFileName));
} while (!Exiting(Cmd) && FindNextFile(hFind, &w32fd));
/* NOTE (We don't apply Windows' CMD compatibility here):
* Windows' CMD does not check whether a GOTO has been done,
* and will continue to loop. */
} while (!ExitingOrGoto(Cmd) && FindNextFile(hFind, &w32fd));
FindClose(hFind);
return Ret;