From: Eric Pouech epouech@codeweavers.com
Introducing CMD_FOR_CONTROL structure to store parsing output for consumption in execution.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/builtins.c | 40 +++++++-- programs/cmd/wcmd.h | 20 +++++ programs/cmd/wcmdmain.c | 178 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 231 insertions(+), 7 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index b28638ce70d..86248231363 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -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 * diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index dde5d1e12ec..3eb9731c45d 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -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); diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index da9d93f4dde..e86ad8042f9 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -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 *