cmd: Introduce helpers to save and restore FOR loop contexts.

Signed-off-by: Eric Pouech <epouech@codeweavers.com>
This commit is contained in:
Eric Pouech 2024-04-28 12:50:48 +02:00 committed by Alexandre Julliard
parent abcc2a1bc6
commit dc7e8da528
4 changed files with 84 additions and 49 deletions

View file

@ -398,7 +398,7 @@ void WCMD_HandleTildeModifiers(WCHAR **start, BOOL atExecute)
} else {
int foridx = for_var_char_to_index(*lastModifier);
/* Its a valid parameter identifier - OK */
if ((foridx >= 0) && (forloopcontext.variable[foridx] != NULL)) break;
if ((foridx >= 0) && (forloopcontext->variable[foridx] != NULL)) break;
/* Its not a valid parameter identifier - step backwards */
lastModifier--;
@ -425,7 +425,8 @@ void WCMD_HandleTildeModifiers(WCHAR **start, BOOL atExecute)
NULL, FALSE, TRUE));
} else {
int foridx = for_var_char_to_index(*lastModifier);
lstrcpyW(outputparam, forloopcontext.variable[foridx]);
if (foridx != -1)
lstrcpyW(outputparam, forloopcontext->variable[foridx]);
}
/* 1. Handle '~' : Strip surrounding quotes */
@ -663,12 +664,10 @@ void WCMD_call (WCHAR *command) {
if (context) {
LARGE_INTEGER li;
FOR_CONTEXT oldcontext;
/* Save the for variable context, then start with an empty context
as for loop variables do not survive a call */
oldcontext = forloopcontext;
memset(&forloopcontext, 0, sizeof(forloopcontext));
WCMD_save_for_loop_context(TRUE);
/* Save the current file position, call the same file,
restore position */
@ -680,7 +679,7 @@ void WCMD_call (WCHAR *command) {
&li.u.HighPart, FILE_BEGIN);
/* Restore the for loop context */
forloopcontext = oldcontext;
WCMD_restore_for_loop_context();
} else {
WCMD_output_asis_stderr(WCMD_LoadMessage(WCMD_CALLINSCRIPT));
}

View file

@ -1962,7 +1962,6 @@ static void WCMD_parse_line(CMD_NODE *cmdStart,
WCHAR *forf_tokens) {
WCHAR *parm;
FOR_CONTEXT oldcontext;
int varoffset;
int nexttoken, lasttoken = -1;
BOOL starfound = FALSE;
@ -1978,7 +1977,7 @@ static void WCMD_parse_line(CMD_NODE *cmdStart,
}
/* Save away any existing for variable context (e.g. nested for loops) */
oldcontext = forloopcontext;
WCMD_save_for_loop_context(FALSE);
/* Extract the parameters based on the tokens= value (There will always
be some value, as if it is not supplied, it defaults to tokens=1).
@ -1999,7 +1998,7 @@ static void WCMD_parse_line(CMD_NODE *cmdStart,
for (varoffset=0;
varidx >= 0 && varoffset<totalfound && for_var_index_in_range(varidx, varoffset);
varoffset++) {
forloopcontext.variable[varidx + varoffset] = emptyW;
WCMD_set_for_loop_variable(varidx + varoffset, emptyW);
}
/* Loop extracting the tokens
@ -2017,7 +2016,8 @@ static void WCMD_parse_line(CMD_NODE *cmdStart,
WINE_TRACE("Parsed token %d(%d) as parameter %s\n", nexttoken,
varidx + varoffset, wine_dbgstr_w(parm));
if (varidx >=0) {
if (parm) forloopcontext.variable[varidx + varoffset] = xstrdupW(parm);
if (parm)
WCMD_set_for_loop_variable(varidx + varoffset, parm);
varoffset++;
}
@ -2034,30 +2034,19 @@ static void WCMD_parse_line(CMD_NODE *cmdStart,
WCMD_parameter_with_delims(buffer, (nexttoken-1), &parm, FALSE, FALSE, forf_delims);
WINE_TRACE("Parsed allremaining tokens (%d) as parameter %s\n",
varidx + varoffset, wine_dbgstr_w(parm));
if (parm) forloopcontext.variable[varidx + varoffset] = xstrdupW(parm);
if (parm)
WCMD_set_for_loop_variable(varidx + varoffset, parm);
}
/* Execute the body of the foor loop with these values */
if (varidx >= 0 && forloopcontext.variable[varidx] && forloopcontext.variable[varidx][0] != forf_eol) {
if (varidx >= 0 && forloopcontext->variable[varidx] && forloopcontext->variable[varidx][0] != forf_eol) {
CMD_NODE *thisCmdStart = cmdStart;
*doExecuted = TRUE;
WCMD_part_execute(&thisCmdStart, firstCmd, FALSE, TRUE);
*cmdEnd = thisCmdStart;
}
/* Free the duplicated strings, and restore the context */
if (varidx >=0) {
int i;
for (i=varidx; i<MAX_FOR_VARIABLES; i++) {
if ((forloopcontext.variable[i] != oldcontext.variable[i]) &&
(forloopcontext.variable[i] != emptyW)) {
free(forloopcontext.variable[i]);
}
}
}
/* Restore the original for variable contextx */
forloopcontext = oldcontext;
WCMD_restore_for_loop_context();
}
/**************************************************************************
@ -2077,7 +2066,7 @@ static void WCMD_parse_line(CMD_NODE *cmdStart,
*
* Returns a file handle which can be used to read the input lines from.
*/
static FILE* WCMD_forf_getinput(BOOL usebackq, WCHAR *itemstr, BOOL iscmd)
static FILE *WCMD_forf_getinput(BOOL usebackq, WCHAR *itemstr, BOOL iscmd)
{
WCHAR *trimmed = NULL;
FILE* ret;
@ -2132,7 +2121,6 @@ void WCMD_for (WCHAR *p, CMD_NODE **cmdList) {
CMD_NODE *setStart, *thisSet, *cmdStart, *cmdEnd;
WCHAR variable[4];
int varidx = -1;
WCHAR *oldvariablevalue;
WCHAR *firstCmd;
int thisDepth;
WCHAR optionsRoot[MAX_PATH];
@ -2352,14 +2340,13 @@ void WCMD_for (WCHAR *p, CMD_NODE **cmdList) {
}
doExecuted = TRUE;
WCMD_save_for_loop_context(FALSE);
/* Save away any existing for variable context (e.g. nested for loops)
and restore it after executing the body of this for loop */
if (varidx >= 0) {
oldvariablevalue = forloopcontext.variable[varidx];
forloopcontext.variable[varidx] = fullitem;
}
if (varidx >= 0)
WCMD_set_for_loop_variable(varidx, fullitem);
WCMD_part_execute (&thisCmdStart, firstCmd, FALSE, TRUE);
if (varidx >= 0) forloopcontext.variable[varidx] = oldvariablevalue;
WCMD_restore_for_loop_context();
cmdEnd = thisCmdStart;
}
@ -2369,14 +2356,13 @@ void WCMD_for (WCHAR *p, CMD_NODE **cmdList) {
} else {
doExecuted = TRUE;
WCMD_save_for_loop_context(FALSE);
/* Save away any existing for variable context (e.g. nested for loops)
and restore it after executing the body of this for loop */
if (varidx >= 0) {
oldvariablevalue = forloopcontext.variable[varidx];
forloopcontext.variable[varidx] = fullitem;
}
if (varidx >= 0)
WCMD_set_for_loop_variable(varidx, fullitem);
WCMD_part_execute (&thisCmdStart, firstCmd, FALSE, TRUE);
if (varidx >= 0) forloopcontext.variable[varidx] = oldvariablevalue;
WCMD_restore_for_loop_context();
cmdEnd = thisCmdStart;
}
@ -2491,12 +2477,14 @@ void WCMD_for (WCHAR *p, CMD_NODE **cmdList) {
/* Save away any existing for variable context (e.g. nested for loops)
and restore it after executing the body of this for loop */
if (varidx >= 0) {
oldvariablevalue = forloopcontext.variable[varidx];
forloopcontext.variable[varidx] = thisNum;
if (varidx >= 0)
{
WCMD_save_for_loop_context(FALSE);
WCMD_set_for_loop_variable(varidx, thisNum);
}
WCMD_part_execute (&thisCmdStart, firstCmd, FALSE, TRUE);
if (varidx >= 0) forloopcontext.variable[varidx] = oldvariablevalue;
if (varidx >= 0)
WCMD_restore_for_loop_context();
}
cmdEnd = thisCmdStart;
}

View file

@ -288,10 +288,16 @@ static inline BOOL for_var_index_in_range(int var_idx, int var_offset)
return for_var_char_to_index(for_var_index_to_char(var_idx) + var_offset) == var_idx + var_offset;
}
typedef struct _FOR_CONTEXT {
WCHAR *variable[MAX_FOR_VARIABLES]; /* a-z then A-Z */
typedef struct _FOR_CONTEXT
{
struct _FOR_CONTEXT *previous;
WCHAR *variable[MAX_FOR_VARIABLES]; /* a-z then A-Z */
} FOR_CONTEXT;
void WCMD_save_for_loop_context(BOOL reset);
void WCMD_restore_for_loop_context(void);
void WCMD_set_for_loop_variable(int var_idx, const WCHAR *value);
/*
* Global variables quals, param1, param2 contain the current qualifiers
* (uppercased and concatenated) and parameters entered, with environment
@ -300,7 +306,7 @@ typedef struct _FOR_CONTEXT {
extern WCHAR quals[MAXSTRING], param1[MAXSTRING], param2[MAXSTRING];
extern int errorlevel;
extern BATCH_CONTEXT *context;
extern FOR_CONTEXT forloopcontext;
extern FOR_CONTEXT *forloopcontext;
extern BOOL delayedsubst;
#endif /* !RC_INVOKED */

View file

@ -39,7 +39,7 @@ BATCH_CONTEXT *context = NULL;
int errorlevel;
WCHAR quals[MAXSTRING], param1[MAXSTRING], param2[MAXSTRING];
BOOL interactive;
FOR_CONTEXT forloopcontext; /* The 'for' loop context */
FOR_CONTEXT *forloopcontext; /* The 'for' loop context */
BOOL delayedsubst = FALSE; /* The current delayed substitution setting */
int defaultColor = 7;
@ -808,10 +808,10 @@ static void handleExpansion(WCHAR *cmd, BOOL atExecute, BOOL delayed) {
/* Display the FOR variables in effect */
for (i=0;i<MAX_FOR_VARIABLES;i++) {
if (forloopcontext.variable[i]) {
if (forloopcontext->variable[i]) {
WINE_TRACE("FOR variable context: %c = '%s'\n",
for_var_index_to_char(i),
wine_dbgstr_w(forloopcontext.variable[i]));
wine_dbgstr_w(forloopcontext->variable[i]));
}
}
@ -860,9 +860,9 @@ static void handleExpansion(WCHAR *cmd, BOOL atExecute, BOOL delayed) {
} else {
int forvaridx = for_var_char_to_index(*(p+1));
if (startchar == '%' && forvaridx != -1 && forloopcontext.variable[forvaridx]) {
if (startchar == '%' && forvaridx != -1 && forloopcontext->variable[forvaridx]) {
/* Replace the 2 characters, % and for variable character */
WCMD_strsubstW(p, p + 2, forloopcontext.variable[forvaridx], -1);
WCMD_strsubstW(p, p + 2, forloopcontext->variable[forvaridx], -1);
} else if (!atExecute || startchar == '!') {
p = WCMD_expand_envvar(p, startchar);
@ -2710,6 +2710,44 @@ BOOL if_condition_evaluate(CMD_IF_CONDITION *cond, int *test)
return TRUE;
}
void WCMD_save_for_loop_context(BOOL reset)
{
FOR_CONTEXT *new = xalloc(sizeof(*new));
if (reset)
memset(new, 0, sizeof(*new));
else /* clone existing */
*new = *forloopcontext;
new->previous = forloopcontext;
forloopcontext = new;
}
void WCMD_restore_for_loop_context(void)
{
FOR_CONTEXT *old = forloopcontext->previous;
int varidx;
if (!old)
{
FIXME("Unexpected situation\n");
return;
}
for (varidx = 0; varidx < MAX_FOR_VARIABLES; varidx++)
{
if (forloopcontext->variable[varidx] != old->variable[varidx])
free(forloopcontext->variable[varidx]);
}
free(forloopcontext);
forloopcontext = old;
}
void WCMD_set_for_loop_variable(int var_idx, const WCHAR *value)
{
if (var_idx < 0 || var_idx >= MAX_FOR_VARIABLES) return;
if (forloopcontext->previous &&
forloopcontext->previous->variable[var_idx] != forloopcontext->variable[var_idx])
free(forloopcontext->variable[var_idx]);
forloopcontext->variable[var_idx] = xstrdupW(value);
}
/***************************************************************************
* WCMD_process_commands
*
@ -2799,6 +2837,10 @@ int __cdecl wmain (int argc, WCHAR *argvW[])
LocalFree(cmd);
cmd = NULL;
/* init for loop context */
forloopcontext = NULL;
WCMD_save_for_loop_context(TRUE);
/* Can't use argc/argv as it will have stripped quotes from parameters
* meaning cmd.exe /C echo "quoted string" is impossible
*/