cmd: Split parsing from executing FOR loops for numbers (/L).

Introducing CMD_FOR_CONTROL structure to store parsing output
for consumption in execution.

Signed-off-by: Eric Pouech <epouech@codeweavers.com>
This commit is contained in:
Eric Pouech 2024-06-14 17:04:58 +02:00 committed by Alexandre Julliard
parent 4a3365daff
commit 9ba05f5e5b
3 changed files with 231 additions and 7 deletions

View file

@ -1546,8 +1546,8 @@ void WCMD_echo (const WCHAR *args)
* first command to be executed may not be at the front of the
* commands->thiscommand string (eg. it may point after a DO or ELSE)
*/
static void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd,
BOOL isIF, BOOL executecmds)
void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd,
BOOL isIF, BOOL executecmds)
{
CMD_NODE *curPosition = *cmdList;
int myDepth = CMD_node_get_depth(*cmdList);
@ -1850,11 +1850,11 @@ static void WCMD_add_dirstowalk(DIRECTORY_STACK *dirsToWalk)
* are recursively passed. If any have duplicates, then the * token should
* not be honoured.
*/
static int WCMD_for_nexttoken(int lasttoken, WCHAR *tokenstr,
int *totalfound, BOOL *doall,
BOOL *duplicates)
int WCMD_for_nexttoken(int lasttoken, const WCHAR *tokenstr,
int *totalfound, BOOL *doall,
BOOL *duplicates)
{
WCHAR *pos = tokenstr;
const WCHAR *pos = tokenstr;
int nexttoken = -1;
if (totalfound) *totalfound = 0;
@ -2136,7 +2136,7 @@ static FILE *WCMD_forf_getinput(BOOL usebackq, WCHAR *itemstr, BOOL iscmd)
*
*/
void WCMD_for (WCHAR *p, CMD_NODE **cmdList) {
static void WCMD_for_OLD (WCHAR *p, CMD_NODE **cmdList) {
WIN32_FIND_DATAW fd;
HANDLE hff;
@ -2541,6 +2541,32 @@ void WCMD_for (WCHAR *p, CMD_NODE **cmdList) {
if (cmdEnd && CMD_node_get_command(cmdEnd)->command == NULL) *cmdList = CMD_node_next(cmdEnd);
}
void WCMD_for(WCHAR *p, CMD_NODE **cmdList)
{
CMD_FOR_CONTROL *for_ctrl;
for_ctrl = for_control_parse(p);
if (!for_ctrl)
{
/* temporary code: use OLD code for non migrated FOR constructs */
WCMD_for_OLD(p, cmdList);
return;
}
for (*cmdList = CMD_node_next(*cmdList); /* swallow options */
*cmdList && CMD_node_get_command(*cmdList)->command != NULL;
*cmdList = CMD_node_next(*cmdList))
{
for_control_append_set(for_ctrl, CMD_node_get_command(*cmdList)->command);
}
/* step over terminating NULL CMD_NODE of set */
*cmdList = CMD_node_next(*cmdList);
for_control_execute(for_ctrl, cmdList);
for_control_dispose(for_ctrl);
}
/**************************************************************************
* WCMD_give_help
*

View file

@ -83,6 +83,14 @@ typedef struct _CMD_IF_CONDITION
};
} CMD_IF_CONDITION;
typedef struct _CMD_FOR_CONTROL
{
enum for_control_operator {CMD_FOR_NUMBERS /* /L */} operator;
unsigned flags; /* |-ed CMD_FOR_FLAG_* */
int variable_index;
const WCHAR *set;
} CMD_FOR_CONTROL;
typedef struct _CMD_COMMAND
{
WCHAR *command; /* Command string to execute */
@ -129,6 +137,18 @@ void if_condition_dispose(CMD_IF_CONDITION *);
BOOL if_condition_evaluate(CMD_IF_CONDITION *cond, int *test);
const char *debugstr_if_condition(const CMD_IF_CONDITION *cond);
void for_control_create(enum for_control_operator for_op, unsigned flags, const WCHAR *options, int var_idx, CMD_FOR_CONTROL *for_ctrl);
CMD_FOR_CONTROL *for_control_parse(WCHAR *opts_var);
void for_control_append_set(CMD_FOR_CONTROL *for_ctrl, const WCHAR *string);
void for_control_dump(const CMD_FOR_CONTROL *for_ctrl);
void for_control_dispose(CMD_FOR_CONTROL *for_ctrl);
void for_control_execute(CMD_FOR_CONTROL *for_ctrl, CMD_NODE **cmdList);
int WCMD_for_nexttoken(int lasttoken, const WCHAR *tokenstr,
int *totalfound, BOOL *doall,
BOOL *duplicates);
void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd,
BOOL isIF, BOOL executecmds);
void WCMD_assoc (const WCHAR *, BOOL);
void WCMD_batch (WCHAR *, WCHAR *, BOOL, WCHAR *, HANDLE);
void WCMD_call (WCHAR *command);

View file

@ -991,6 +991,65 @@ static void command_dispose(CMD_COMMAND *cmd)
}
}
void for_control_dispose(CMD_FOR_CONTROL *for_ctrl)
{
free((void*)for_ctrl->set);
switch (for_ctrl->operator)
{
default:
break;
}
}
const char *debugstr_for_control(const CMD_FOR_CONTROL *for_ctrl)
{
static const char* for_ctrl_strings[] = {"numbers"};
const char *flags, *options;
if (for_ctrl->operator >= ARRAY_SIZE(for_ctrl_strings))
{
FIXME("Unexpected operator\n");
return wine_dbg_sprintf("<<%u>>", for_ctrl->operator);
}
flags = "";
switch (for_ctrl->operator)
{
default:
options = "";
break;
}
return wine_dbg_sprintf("[FOR] %s %s%s%%%c (%ls)",
for_ctrl_strings[for_ctrl->operator], flags, options,
for_var_index_to_char(for_ctrl->variable_index), for_ctrl->set);
}
void for_control_create(enum for_control_operator for_op, unsigned flags, const WCHAR *options, int var_idx, CMD_FOR_CONTROL *for_ctrl)
{
for_ctrl->operator = for_op;
for_ctrl->flags = flags;
for_ctrl->variable_index = var_idx;
for_ctrl->set = NULL;
switch (for_ctrl->operator)
{
default:
break;
}
}
void for_control_append_set(CMD_FOR_CONTROL *for_ctrl, const WCHAR *set)
{
if (for_ctrl->set)
{
for_ctrl->set = xrealloc((void*)for_ctrl->set,
(wcslen(for_ctrl->set) + 1 + wcslen(set) + 1) * sizeof(WCHAR));
wcscat((WCHAR*)for_ctrl->set, L" ");
wcscat((WCHAR*)for_ctrl->set, set);
}
else
for_ctrl->set = xstrdupW(set);
}
/***************************************************************************
* node_dispose_tree
*
@ -2093,6 +2152,70 @@ static BOOL WCMD_IsEndQuote(const WCHAR *quote, int quoteIndex)
return FALSE;
}
CMD_FOR_CONTROL *for_control_parse(WCHAR *opts_var)
{
CMD_FOR_CONTROL *for_ctrl;
enum for_control_operator for_op;
WCHAR mode = L' ', option;
WCHAR options[MAXSTRING];
WCHAR *arg;
unsigned flags = 0;
int arg_index;
int var_idx;
options[0] = L'\0';
/* native allows two options only in the /D /R case, a repetition of the option
* and prints an error otherwise
*/
for (arg_index = 0; ; arg_index++)
{
arg = WCMD_parameter(opts_var, arg_index, NULL, FALSE, FALSE);
if (!arg || *arg != L'/') break;
option = towupper(arg[1]);
if (mode != L' ' && (mode != L'D' || option != 'R') && mode != option)
break;
switch (option)
{
case L'R':
if (mode == L'D')
{
mode = L'X';
break;
}
/* fall thru */
case L'D':
case L'L':
case L'F':
mode = option;
break;
default:
/* error unexpected 'arg' at this time */
WARN("for qualifier '%c' unhandled\n", *arg);
goto syntax_error;
}
}
switch (mode)
{
case L'L':
for_op = CMD_FOR_NUMBERS;
break;
default:
return NULL;
}
/* Ensure line continues with variable */
arg = WCMD_parameter(opts_var, arg_index++, NULL, FALSE, FALSE);
if (!arg || *arg != L'%' || (var_idx = for_var_char_to_index(arg[1])) == -1)
goto syntax_error; /* FIXME native prints the offending token "%<whatever>" was unexpected at this time */
for_ctrl = xalloc(sizeof(*for_ctrl));
for_control_create(for_op, flags, options, var_idx, for_ctrl);
return for_ctrl;
syntax_error:
WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR));
return NULL;
}
/***************************************************************************
* WCMD_ReadAndParseLine
*
@ -2748,6 +2871,61 @@ void WCMD_set_for_loop_variable(int var_idx, const WCHAR *value)
forloopcontext->variable[var_idx] = xstrdupW(value);
}
static CMD_NODE *for_control_execute_numbers(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *cmdList)
{
WCHAR set[MAXSTRING];
CMD_NODE *body = NULL;
int numbers[3] = {0, 0, 0}, var;
int i;
wcscpy(set, for_ctrl->set);
/* Note: native doesn't check the actual number of parameters, and set
* them by default to 0.
* so (-10 1) is interpreted as (-10 1 0)
* and (10) loops for ever !!!
*/
for (i = 0; i < ARRAY_SIZE(numbers); i++)
{
WCHAR *element = WCMD_parameter(set, i, NULL, FALSE, FALSE);
if (!element || !*element) break;
/* native doesn't no error handling */
numbers[i] = wcstol(element, NULL, 0);
}
for (var = numbers[0];
(numbers[1] < 0) ? var >= numbers[2] : var <= numbers[2];
var += numbers[1])
{
WCHAR tmp[32];
body = cmdList;
swprintf(tmp, ARRAY_SIZE(tmp), L"%d", var);
WCMD_set_for_loop_variable(for_ctrl->variable_index, tmp);
TRACE("Processing FOR number %s\n", wine_dbgstr_w(tmp));
WCMD_part_execute(&body, CMD_node_get_command(cmdList)->command + 3, FALSE, TRUE);
}
return body;
}
void for_control_execute(CMD_FOR_CONTROL *for_ctrl, CMD_NODE **cmdList)
{
CMD_NODE *last;
WCMD_save_for_loop_context(FALSE);
switch (for_ctrl->operator)
{
case CMD_FOR_NUMBERS:
last = for_control_execute_numbers(for_ctrl, *cmdList);
break;
default:
last = NULL;
break;
}
WCMD_restore_for_loop_context();
*cmdList = last;
}
/***************************************************************************
* WCMD_process_commands
*