[CMD] Reduce stack memory usage when parsing or echoing commands recursively.

- Use a common large string buffer for temporary command substitutions/expansions,
  instead of having such a buffer in the local stack of the parsing and
  echoing functions that may be called recursively. Since CMD executes
  synchronously we know that this common buffer can only be used once at
  a time.

- Also do a small code cleanup in ParseIf(), ParseFor() and ParseCommandPart().
This commit is contained in:
Hermès Bélusca-Maïto 2020-06-06 22:00:13 +02:00
parent 26cfadc352
commit c93f511241
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0

View file

@ -57,6 +57,9 @@ enum
TOK_END_BLOCK TOK_END_BLOCK
}; };
/* Scratch buffer for temporary command substitutions / expansions */
static TCHAR TempBuf[CMDLINE_LENGTH];
static BOOL bParseError; static BOOL bParseError;
static BOOL bLineContinuations; static BOOL bLineContinuations;
static TCHAR ParseLine[CMDLINE_LENGTH]; static TCHAR ParseLine[CMDLINE_LENGTH];
@ -370,7 +373,6 @@ static PARSED_COMMAND *ParseBlock(REDIRECTION *RedirList)
/* Parse an IF statement */ /* Parse an IF statement */
static PARSED_COMMAND *ParseIf(void) static PARSED_COMMAND *ParseIf(void)
{ {
int Type;
PARSED_COMMAND *Cmd; PARSED_COMMAND *Cmd;
Cmd = cmd_alloc(sizeof(PARSED_COMMAND)); Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
@ -383,24 +385,19 @@ static PARSED_COMMAND *ParseIf(void)
memset(Cmd, 0, sizeof(PARSED_COMMAND)); memset(Cmd, 0, sizeof(PARSED_COMMAND));
Cmd->Type = C_IF; Cmd->Type = C_IF;
Type = CurrentTokenType;
if (_tcsicmp(CurrentToken, _T("/I")) == 0) if (_tcsicmp(CurrentToken, _T("/I")) == 0)
{ {
Cmd->If.Flags |= IFFLAG_IGNORECASE; Cmd->If.Flags |= IFFLAG_IGNORECASE;
Type = ParseToken(0, STANDARD_SEPS); ParseToken(0, STANDARD_SEPS);
} }
if (_tcsicmp(CurrentToken, _T("not")) == 0) if (_tcsicmp(CurrentToken, _T("not")) == 0)
{ {
Cmd->If.Flags |= IFFLAG_NEGATE; Cmd->If.Flags |= IFFLAG_NEGATE;
Type = ParseToken(0, STANDARD_SEPS); ParseToken(0, STANDARD_SEPS);
} }
if (Type != TOK_NORMAL) if (CurrentTokenType != TOK_NORMAL)
{ goto error;
FreeCommand(Cmd);
ParseError();
return NULL;
}
/* Check for unary operators */ /* Check for unary operators */
for (; Cmd->If.Operator <= IF_MAX_UNARY; Cmd->If.Operator++) for (; Cmd->If.Operator <= IF_MAX_UNARY; Cmd->If.Operator++)
@ -408,11 +405,7 @@ static PARSED_COMMAND *ParseIf(void)
if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0) if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0)
{ {
if (ParseToken(0, STANDARD_SEPS) != TOK_NORMAL) if (ParseToken(0, STANDARD_SEPS) != TOK_NORMAL)
{ goto error;
FreeCommand(Cmd);
ParseError();
return NULL;
}
Cmd->If.RightArg = cmd_dup(CurrentToken); Cmd->If.RightArg = cmd_dup(CurrentToken);
goto condition_done; goto condition_done;
} }
@ -435,33 +428,30 @@ static PARSED_COMMAND *ParseIf(void)
if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0) if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0)
{ {
if (ParseToken(0, STANDARD_SEPS) != TOK_NORMAL) if (ParseToken(0, STANDARD_SEPS) != TOK_NORMAL)
break; goto error;
Cmd->If.RightArg = cmd_dup(CurrentToken); Cmd->If.RightArg = cmd_dup(CurrentToken);
goto condition_done; goto condition_done;
} }
} }
FreeCommand(Cmd); goto error;
ParseError();
return NULL;
condition_done: condition_done:
Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST); Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
if (Cmd->Subcommands == NULL) if (Cmd->Subcommands == NULL)
{ goto error;
FreeCommand(Cmd);
return NULL;
}
if (_tcsicmp(CurrentToken, _T("else")) == 0) if (_tcsicmp(CurrentToken, _T("else")) == 0)
{ {
Cmd->Subcommands->Next = ParseCommandOp(C_OP_LOWEST); Cmd->Subcommands->Next = ParseCommandOp(C_OP_LOWEST);
if (Cmd->Subcommands->Next == NULL) if (Cmd->Subcommands->Next == NULL)
{ goto error;
FreeCommand(Cmd);
return NULL;
}
} }
return Cmd; return Cmd;
error:
FreeCommand(Cmd);
ParseError();
return NULL;
} }
/* /*
@ -471,7 +461,7 @@ condition_done:
static PARSED_COMMAND *ParseFor(void) static PARSED_COMMAND *ParseFor(void)
{ {
PARSED_COMMAND *Cmd; PARSED_COMMAND *Cmd;
TCHAR List[CMDLINE_LENGTH]; TCHAR* List = TempBuf;
TCHAR *Pos = List; TCHAR *Pos = List;
Cmd = cmd_alloc(sizeof(PARSED_COMMAND)); Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
@ -487,7 +477,9 @@ static PARSED_COMMAND *ParseFor(void)
while (1) while (1)
{ {
if (_tcsicmp(CurrentToken, _T("/D")) == 0) if (_tcsicmp(CurrentToken, _T("/D")) == 0)
{
Cmd->For.Switches |= FOR_DIRS; Cmd->For.Switches |= FOR_DIRS;
}
else if (_tcsicmp(CurrentToken, _T("/F")) == 0) else if (_tcsicmp(CurrentToken, _T("/F")) == 0)
{ {
Cmd->For.Switches |= FOR_F; Cmd->For.Switches |= FOR_F;
@ -500,7 +492,9 @@ static PARSED_COMMAND *ParseFor(void)
} }
} }
else if (_tcsicmp(CurrentToken, _T("/L")) == 0) else if (_tcsicmp(CurrentToken, _T("/L")) == 0)
{
Cmd->For.Switches |= FOR_LOOP; Cmd->For.Switches |= FOR_LOOP;
}
else if (_tcsicmp(CurrentToken, _T("/R")) == 0) else if (_tcsicmp(CurrentToken, _T("/R")) == 0)
{ {
Cmd->For.Switches |= FOR_RECURSIVE; Cmd->For.Switches |= FOR_RECURSIVE;
@ -514,7 +508,10 @@ static PARSED_COMMAND *ParseFor(void)
} }
} }
else else
{
break; break;
}
ParseToken(0, STANDARD_SEPS); ParseToken(0, STANDARD_SEPS);
} }
@ -540,24 +537,22 @@ static PARSED_COMMAND *ParseFor(void)
while (1) while (1)
{ {
int Type;
/* Pretend we're inside a block so the tokenizer will stop on ')' */ /* Pretend we're inside a block so the tokenizer will stop on ')' */
InsideBlock++; InsideBlock++;
Type = ParseToken(0, STANDARD_SEPS); ParseToken(0, STANDARD_SEPS);
InsideBlock--; InsideBlock--;
if (Type == TOK_END_BLOCK) if (CurrentTokenType == TOK_END_BLOCK)
break; break;
if (Type == TOK_END) if (CurrentTokenType == TOK_END)
{ {
/* Skip past the \n */ /* Skip past the \n */
ParseChar(); ParseChar();
continue; continue;
} }
if (Type != TOK_NORMAL) if (CurrentTokenType != TOK_NORMAL)
goto error; goto error;
if (Pos != List) if (Pos != List)
@ -576,10 +571,7 @@ static PARSED_COMMAND *ParseFor(void)
Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST); Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
if (Cmd->Subcommands == NULL) if (Cmd->Subcommands == NULL)
{ goto error;
FreeCommand(Cmd);
return NULL;
}
return Cmd; return Cmd;
@ -631,8 +623,8 @@ static DECLSPEC_NOINLINE PARSED_COMMAND *ParseCommandPart(REDIRECTION *RedirList
/* Now get the tail */ /* Now get the tail */
while (1) while (1)
{ {
int Type = ParseToken(0, NULL); ParseToken(0, NULL);
if (Type == TOK_NORMAL) if (CurrentTokenType == TOK_NORMAL)
{ {
if (Pos + _tcslen(CurrentToken) >= &ParsedLine[CMDLINE_LENGTH]) if (Pos + _tcslen(CurrentToken) >= &ParsedLine[CMDLINE_LENGTH])
{ {
@ -642,7 +634,7 @@ static DECLSPEC_NOINLINE PARSED_COMMAND *ParseCommandPart(REDIRECTION *RedirList
} }
Pos = _stpcpy(Pos, CurrentToken); Pos = _stpcpy(Pos, CurrentToken);
} }
else if (Type == TOK_REDIRECTION) else if (CurrentTokenType == TOK_REDIRECTION)
{ {
if (!ParseRedirection(&RedirList)) if (!ParseRedirection(&RedirList))
return NULL; return NULL;
@ -1123,7 +1115,6 @@ dump:
VOID VOID
EchoCommand(PARSED_COMMAND *Cmd) EchoCommand(PARSED_COMMAND *Cmd)
{ {
TCHAR Buf[CMDLINE_LENGTH];
PARSED_COMMAND *Sub; PARSED_COMMAND *Sub;
REDIRECTION *Redir; REDIRECTION *Redir;
@ -1131,14 +1122,14 @@ EchoCommand(PARSED_COMMAND *Cmd)
{ {
case C_COMMAND: case C_COMMAND:
{ {
if (SubstituteForVars(Cmd->Command.First, Buf)) if (SubstituteForVars(Cmd->Command.First, TempBuf))
ConOutPrintf(_T("%s"), Buf); ConOutPrintf(_T("%s"), TempBuf);
if (SubstituteForVars(Cmd->Command.Rest, Buf)) if (SubstituteForVars(Cmd->Command.Rest, TempBuf))
{ {
ConOutPrintf(_T("%s"), Buf); ConOutPrintf(_T("%s"), TempBuf);
#ifdef MSCMD_ECHO_COMMAND_COMPAT #ifdef MSCMD_ECHO_COMMAND_COMPAT
/* NOTE: For Windows compatibility, add a trailing space after printing the command parameter, if present */ /* NOTE: For Windows compatibility, add a trailing space after printing the command parameter, if present */
if (*Buf) ConOutChar(_T(' ')); if (*TempBuf) ConOutChar(_T(' '));
#endif #endif
} }
break; break;
@ -1214,11 +1205,11 @@ EchoCommand(PARSED_COMMAND *Cmd)
ConOutPuts(_T(" /I")); ConOutPuts(_T(" /I"));
if (Cmd->If.Flags & IFFLAG_NEGATE) if (Cmd->If.Flags & IFFLAG_NEGATE)
ConOutPuts(_T(" not")); ConOutPuts(_T(" not"));
if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, Buf)) if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, TempBuf))
ConOutPrintf(_T(" %s"), Buf); ConOutPrintf(_T(" %s"), TempBuf);
ConOutPrintf(_T(" %s"), IfOperatorString[Cmd->If.Operator]); ConOutPrintf(_T(" %s"), IfOperatorString[Cmd->If.Operator]);
if (SubstituteForVars(Cmd->If.RightArg, Buf)) if (SubstituteForVars(Cmd->If.RightArg, TempBuf))
ConOutPrintf(_T(" %s "), Buf); ConOutPrintf(_T(" %s "), TempBuf);
Sub = Cmd->Subcommands; Sub = Cmd->Subcommands;
EchoCommand(Sub); EchoCommand(Sub);
if (Sub->Next) if (Sub->Next)
@ -1238,8 +1229,8 @@ EchoCommand(PARSED_COMMAND *Cmd)
if (Cmd->For.Switches & FOR_RECURSIVE) ConOutPuts(_T(" /R")); if (Cmd->For.Switches & FOR_RECURSIVE) ConOutPuts(_T(" /R"));
if (Cmd->For.Params) if (Cmd->For.Params)
ConOutPrintf(_T(" %s"), Cmd->For.Params); ConOutPrintf(_T(" %s"), Cmd->For.Params);
if (Cmd->For.List && SubstituteForVars(Cmd->For.List, Buf)) if (Cmd->For.List && SubstituteForVars(Cmd->For.List, TempBuf))
ConOutPrintf(_T(" %%%c in (%s) do "), Cmd->For.Variable, Buf); ConOutPrintf(_T(" %%%c in (%s) do "), Cmd->For.Variable, TempBuf);
else else
ConOutPrintf(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List); ConOutPrintf(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List);
EchoCommand(Cmd->Subcommands); EchoCommand(Cmd->Subcommands);
@ -1253,16 +1244,16 @@ EchoCommand(PARSED_COMMAND *Cmd)
for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next) for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
{ {
if (SubstituteForVars(Redir->Filename, Buf)) if (SubstituteForVars(Redir->Filename, TempBuf))
{ {
#ifdef MSCMD_ECHO_COMMAND_COMPAT #ifdef MSCMD_ECHO_COMMAND_COMPAT
ConOutPrintf(_T("%c%s%s "), ConOutPrintf(_T("%c%s%s "),
_T('0') + Redir->Number, _T('0') + Redir->Number,
RedirString[Redir->Mode], Buf); RedirString[Redir->Mode], TempBuf);
#else #else
ConOutPrintf(_T(" %c%s%s"), ConOutPrintf(_T(" %c%s%s"),
_T('0') + Redir->Number, _T('0') + Redir->Number,
RedirString[Redir->Mode], Buf); RedirString[Redir->Mode], TempBuf);
#endif #endif
} }
} }
@ -1276,7 +1267,6 @@ EchoCommand(PARSED_COMMAND *Cmd)
TCHAR * TCHAR *
Unparse(PARSED_COMMAND *Cmd, TCHAR *Out, TCHAR *OutEnd) Unparse(PARSED_COMMAND *Cmd, TCHAR *Out, TCHAR *OutEnd)
{ {
TCHAR Buf[CMDLINE_LENGTH];
PARSED_COMMAND *Sub; PARSED_COMMAND *Sub;
REDIRECTION *Redir; REDIRECTION *Redir;
@ -1313,10 +1303,10 @@ do { \
/* This is fragile since there could be special characters, but /* This is fragile since there could be special characters, but
* Windows doesn't bother escaping them, so for compatibility * Windows doesn't bother escaping them, so for compatibility
* we probably shouldn't do it either */ * we probably shouldn't do it either */
if (!SubstituteForVars(Cmd->Command.First, Buf)) return NULL; if (!SubstituteForVars(Cmd->Command.First, TempBuf)) return NULL;
STRING(Buf); STRING(TempBuf);
if (!SubstituteForVars(Cmd->Command.Rest, Buf)) return NULL; if (!SubstituteForVars(Cmd->Command.Rest, TempBuf)) return NULL;
STRING(Buf); STRING(TempBuf);
break; break;
case C_QUIET: case C_QUIET:
@ -1351,11 +1341,11 @@ do { \
STRING(_T(" /I")); STRING(_T(" /I"));
if (Cmd->If.Flags & IFFLAG_NEGATE) if (Cmd->If.Flags & IFFLAG_NEGATE)
STRING(_T(" not")); STRING(_T(" not"));
if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, Buf)) if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, TempBuf))
PRINTF(_T(" %s"), Buf); PRINTF(_T(" %s"), TempBuf);
PRINTF(_T(" %s"), IfOperatorString[Cmd->If.Operator]); PRINTF(_T(" %s"), IfOperatorString[Cmd->If.Operator]);
if (!SubstituteForVars(Cmd->If.RightArg, Buf)) return NULL; if (!SubstituteForVars(Cmd->If.RightArg, TempBuf)) return NULL;
PRINTF(_T(" %s "), Buf); PRINTF(_T(" %s "), TempBuf);
Sub = Cmd->Subcommands; Sub = Cmd->Subcommands;
RECURSE(Sub); RECURSE(Sub);
if (Sub->Next) if (Sub->Next)
@ -1373,8 +1363,8 @@ do { \
if (Cmd->For.Switches & FOR_RECURSIVE) STRING(_T(" /R")); if (Cmd->For.Switches & FOR_RECURSIVE) STRING(_T(" /R"));
if (Cmd->For.Params) if (Cmd->For.Params)
PRINTF(_T(" %s"), Cmd->For.Params); PRINTF(_T(" %s"), Cmd->For.Params);
if (Cmd->For.List && SubstituteForVars(Cmd->For.List, Buf)) if (Cmd->For.List && SubstituteForVars(Cmd->For.List, TempBuf))
PRINTF(_T(" %%%c in (%s) do "), Cmd->For.Variable, Buf); PRINTF(_T(" %%%c in (%s) do "), Cmd->For.Variable, TempBuf);
else else
PRINTF(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List); PRINTF(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List);
RECURSE(Cmd->Subcommands); RECURSE(Cmd->Subcommands);
@ -1387,10 +1377,10 @@ do { \
for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next) for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
{ {
if (!SubstituteForVars(Redir->Filename, Buf)) if (!SubstituteForVars(Redir->Filename, TempBuf))
return NULL; return NULL;
PRINTF(_T(" %c%s%s"), _T('0') + Redir->Number, PRINTF(_T(" %c%s%s"), _T('0') + Redir->Number,
RedirString[Redir->Mode], Buf); RedirString[Redir->Mode], TempBuf);
} }
return Out; return Out;