This cmd engine rewrite part XXXIX.
It mainly finishes the pipe support by implementing the rewrite of complex commands. It also fixes a bunch of todo:s in the tests (linked to some extraneous space handling for explicit blocks).
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 65 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index c16f1ebe32e..a44157eb84d 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -3346,6 +3346,54 @@ static BOOL rebuild_append_all_redirections(struct command_rebuild *rb, const CM return ret; }
+static BOOL rebuild_append_command(struct command_rebuild *rb, const CMD_NODE *node, struct rebuild_flags rbflags); + +static BOOL rebuild_command_binary(struct command_rebuild *rb, const CMD_NODE *node, struct rebuild_flags rbflags) +{ + const WCHAR *op_string; + struct rebuild_flags new_rbflags = {.depth = rbflags.depth + 1}; + + switch (node->op) + { + case CMD_PIPE: op_string = L"|"; new_rbflags.precedence = 4; break; + case CMD_CONCAT: op_string = L"&"; new_rbflags.precedence = 3; break; + case CMD_ONFAILURE: op_string = L"||"; new_rbflags.precedence = 2; break; + case CMD_ONSUCCESS: op_string = L"&&"; new_rbflags.precedence = 1; break; + default: return FALSE; + } + + return ((new_rbflags.precedence >= rbflags.precedence) || rebuild_append(rb, L"(")) && + rebuild_append_command(rb, node->left, new_rbflags) && + ((node->left->op == CMD_SINGLE && node->op == CMD_CONCAT) ? rebuild_append(rb, L" ") : TRUE) && + rebuild_append(rb, op_string) && + rebuild_append_command(rb, node->right, new_rbflags) && + ((new_rbflags.precedence >= rbflags.precedence) || rebuild_append(rb, L")")); +} + +static BOOL rebuild_append_command(struct command_rebuild *rb, const CMD_NODE *node, struct rebuild_flags rbflags) +{ + BOOL ret; + + switch (node->op) + { + case CMD_SINGLE: + ret = rebuild_expand_and_append(rb, node->command, rbflags.depth == 0); + break; + case CMD_PIPE: + case CMD_CONCAT: + case CMD_ONFAILURE: + case CMD_ONSUCCESS: + ret = rebuild_command_binary(rb, node, rbflags); + break; + default: + FIXME("Shouldn't happen\n"); + ret = FALSE; + } + ret = ret && rebuild_append_all_redirections(rb, node, rbflags.depth == 0); + + return ret; +} + static BOOL lexer_can_accept_do(const struct node_builder *builder) { unsigned d = 0; @@ -4310,6 +4358,11 @@ static BOOL can_run_new_pipe(CMD_NODE *node) { case CMD_SINGLE: return TRUE; + case CMD_PIPE: + case CMD_CONCAT: + case CMD_ONFAILURE: + case CMD_ONSUCCESS: + return can_run_new_pipe(node->left) && can_run_new_pipe(node->right); default: return FALSE; } @@ -4320,6 +4373,7 @@ static RETURN_CODE spawn_pipe_sub_command(CMD_NODE *node, HANDLE *child) WCHAR cmd_string[MAXSTRING]; WCHAR comspec[MAX_PATH]; struct command_rebuild rb = {cmd_string, ARRAY_SIZE(cmd_string), 0}; + struct rebuild_flags rbflags = {}; RETURN_CODE return_code;
switch (node->op) @@ -4335,8 +4389,8 @@ static RETURN_CODE spawn_pipe_sub_command(CMD_NODE *node, HANDLE *child) if ((sc.cmd_index <= WCMD_EXIT && (return_code != NO_ERROR || (!sc.has_path && !sc.has_extension))) || (sc.has_path && sc.is_command_file)) { - rebuild_expand_and_append(&rb, node->command, TRUE); - rebuild_append_all_redirections(&rb, node, TRUE); + if (!rebuild_append_command(&rb, node, rbflags)) + return ERROR_INVALID_FUNCTION; } else { @@ -4354,6 +4408,13 @@ static RETURN_CODE spawn_pipe_sub_command(CMD_NODE *node, HANDLE *child) } } break; + case CMD_PIPE: + case CMD_CONCAT: + case CMD_ONFAILURE: + case CMD_ONSUCCESS: + if (!rebuild_append_command(&rb, node, rbflags)) + return ERROR_INVALID_FUNCTION; + break; default: FIXME("Shouldn't happen\n"); return ERROR_INVALID_FUNCTION;
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 62 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index a44157eb84d..c6fa64ff7a2 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -3370,6 +3370,61 @@ static BOOL rebuild_command_binary(struct command_rebuild *rb, const CMD_NODE *n ((new_rbflags.precedence >= rbflags.precedence) || rebuild_append(rb, L")")); }
+static BOOL rebuild_command_if(struct command_rebuild *rb, const CMD_NODE *node, struct rebuild_flags rbflags) +{ + const WCHAR *unop = NULL, *binop = NULL; + struct rebuild_flags new_rbflags = {.precedence = 0, .depth = rbflags.depth + 1}; + BOOL ret; + + ret = rebuild_append(rb, L"if "); + if (node->condition.case_insensitive) ret = ret && rebuild_append(rb, L"/i "); + if (node->condition.negated) ret = ret && rebuild_append(rb, L"not "); + + switch (node->condition.op) + { + case CMD_IF_ERRORLEVEL: unop = L"errorlevel "; break; + case CMD_IF_EXIST: unop = L"exist "; break; + case CMD_IF_DEFINED: unop = L"defined "; break; + case CMD_IF_BINOP_EQUAL: binop = L" == "; break; + case CMD_IF_BINOP_LSS: binop = L" LSS "; break; + case CMD_IF_BINOP_LEQ: binop = L" LEQ "; break; + case CMD_IF_BINOP_EQU: binop = L" EQU "; break; + case CMD_IF_BINOP_NEQ: binop = L" NEQ "; break; + case CMD_IF_BINOP_GEQ: binop = L" GEQ "; break; + case CMD_IF_BINOP_GTR: binop = L" GTR "; break; + default: + FIXME("Unexpected condition operator %u\n", node->condition.op); + ret = FALSE; + break; + } + if (unop) + { + ret = ret && rebuild_append(rb, unop); + ret = ret && rebuild_expand_and_append(rb, node->condition.operand, rbflags.depth == 0); + } + else if (binop) + { + ret = ret && rebuild_expand_and_append(rb, node->condition.left, rbflags.depth == 0); + ret = ret && rebuild_append(rb, binop); + ret = ret && rebuild_expand_and_append(rb, node->condition.right, rbflags.depth == 0); + } + else + return FALSE; + ret = ret && rebuild_append(rb, L" "); + if (node->else_block) + { + ret = ret && rebuild_append(rb, L"("); + ret = ret && rebuild_append_command(rb, node->then_block, new_rbflags); + ret = ret && rebuild_append(rb, L" ) else ( "); + ret = ret && rebuild_append_command(rb, node->else_block, new_rbflags); + ret = ret && rebuild_append(rb, L" ) "); + } + else + ret = ret && rebuild_append_command(rb, node->then_block, new_rbflags); + + return ret; +} + static BOOL rebuild_append_command(struct command_rebuild *rb, const CMD_NODE *node, struct rebuild_flags rbflags) { BOOL ret; @@ -3385,6 +3440,9 @@ static BOOL rebuild_append_command(struct command_rebuild *rb, const CMD_NODE *n case CMD_ONSUCCESS: ret = rebuild_command_binary(rb, node, rbflags); break; + case CMD_IF: + ret = rebuild_command_if(rb, node, rbflags); + break; default: FIXME("Shouldn't happen\n"); ret = FALSE; @@ -4363,6 +4421,9 @@ static BOOL can_run_new_pipe(CMD_NODE *node) case CMD_ONFAILURE: case CMD_ONSUCCESS: return can_run_new_pipe(node->left) && can_run_new_pipe(node->right); + case CMD_IF: + return can_run_new_pipe(node->then_block) && + (!node->else_block || can_run_new_pipe(node->else_block)); default: return FALSE; } @@ -4412,6 +4473,7 @@ static RETURN_CODE spawn_pipe_sub_command(CMD_NODE *node, HANDLE *child) case CMD_CONCAT: case CMD_ONFAILURE: case CMD_ONSUCCESS: + case CMD_IF: if (!rebuild_append_command(&rb, node, rbflags)) return ERROR_INVALID_FUNCTION; break;
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 98 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index c6fa64ff7a2..8c9002acc1b 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -3291,6 +3291,19 @@ static BOOL rebuild_expand_and_append(struct command_rebuild *rb, const WCHAR *t return rebuild_append(rb, output); }
+static BOOL rebuild_sprintf(struct command_rebuild *rb, const WCHAR *format, ...) +{ + int ret; + va_list args; + + va_start( args, format ); + ret = vswprintf(rb->buffer + rb->pos, rb->buffer_size - rb->pos, format, args); + va_end( args ); + if (ret < 0 || rb->pos + ret > rb->buffer_size) return FALSE; + rb->pos += ret; + return TRUE; +} + static BOOL rebuild_insert(struct command_rebuild *rb, unsigned pos, const WCHAR *toinsert) { size_t len = wcslen(toinsert); @@ -3425,6 +3438,85 @@ static BOOL rebuild_command_if(struct command_rebuild *rb, const CMD_NODE *node, return ret; }
+static const WCHAR *state_to_delim(int state) +{ + if (!state) return L"""; + return (state == 1) ? L"" : L","; +} + +static BOOL rebuild_command_for(struct command_rebuild *rb, const CMD_NODE *node, struct rebuild_flags rbflags) +{ + const CMD_FOR_CONTROL *for_ctrl = &node->for_ctrl; + struct rebuild_flags new_rflags = {.precedence = 0, .depth = rbflags.depth + 1}; + const WCHAR *opt = NULL; + BOOL ret; + + ret = rebuild_append(rb, L"for "); + + /* append qualifiers (if needed) */ + switch (for_ctrl->operator) + { + case CMD_FOR_FILETREE: + switch (for_ctrl->flags) + { + case CMD_FOR_FLAG_TREE_INCLUDE_FILES: opt = L""; break; + case CMD_FOR_FLAG_TREE_INCLUDE_DIRECTORIES: opt = L"/D "; break; + case CMD_FOR_FLAG_TREE_INCLUDE_DIRECTORIES|CMD_FOR_FLAG_TREE_RECURSE: opt = L"/D/R "; break; + case CMD_FOR_FLAG_TREE_INCLUDE_FILES|CMD_FOR_FLAG_TREE_RECURSE: opt = L"/R "; break; + default: FIXME("Shouldn't happen\n"); break; + } + break; + case CMD_FOR_NUMBERS: opt = L"/L "; break; + case CMD_FOR_FILE_SET: opt = L"/F "; break; + } + if (opt) + ret = ret && rebuild_append(rb, opt); + + /* append options (when needed) */ + switch (for_ctrl->operator) + { + case CMD_FOR_FILETREE: + if ((for_ctrl->flags & CMD_FOR_FLAG_TREE_RECURSE) && for_ctrl->root_dir) + ret = ret && rebuild_expand_and_append(rb, for_ctrl->root_dir, rbflags.depth == 0) && + rebuild_append(rb, L" "); + break; + case CMD_FOR_FILE_SET: + { + int state = 0; + + if (for_ctrl->eol != L'\0') + ret = ret && rebuild_append(rb, state_to_delim(state++)) && + rebuild_sprintf(rb, L"eol=%c", for_ctrl->eol); + if (for_ctrl->num_lines_to_skip) + ret = ret && rebuild_append(rb, state_to_delim(state++)) && + rebuild_sprintf(rb, L"skip=%d", for_ctrl->num_lines_to_skip); + if (for_ctrl->use_backq) + ret = ret && rebuild_append(rb, state_to_delim(state++)) && + rebuild_append(rb, L"useback"); + if (for_ctrl->delims[0]) + ret = ret && rebuild_append(rb, state_to_delim(state++)) && + rebuild_sprintf(rb, L"delims=%s", for_ctrl->delims); + if (for_ctrl->tokens[0]) + ret = ret && rebuild_append(rb, state_to_delim(state++)) && + rebuild_sprintf(rb, L"tokens=%s", for_ctrl->tokens); + if (state) + ret = ret && rebuild_append(rb, L"" "); + } + break; + default: + break; + } + + /* append variable and the rest */ + ret = ret && rebuild_sprintf(rb, L"%%%c in (", for_ctrl->variable_index) && + rebuild_expand_and_append(rb, for_ctrl->set, rbflags.depth == 0) && + rebuild_append(rb, L") do ") && + rebuild_append_command(rb, node->do_block, new_rflags); + if (ret && node->do_block->op == CMD_SINGLE) + ret = rebuild_append(rb, L" "); + return ret; +} + static BOOL rebuild_append_command(struct command_rebuild *rb, const CMD_NODE *node, struct rebuild_flags rbflags) { BOOL ret; @@ -3443,6 +3535,9 @@ static BOOL rebuild_append_command(struct command_rebuild *rb, const CMD_NODE *n case CMD_IF: ret = rebuild_command_if(rb, node, rbflags); break; + case CMD_FOR: + ret = rebuild_command_for(rb, node, rbflags); + break; default: FIXME("Shouldn't happen\n"); ret = FALSE; @@ -4424,6 +4519,8 @@ static BOOL can_run_new_pipe(CMD_NODE *node) case CMD_IF: return can_run_new_pipe(node->then_block) && (!node->else_block || can_run_new_pipe(node->else_block)); + case CMD_FOR: + return can_run_new_pipe(node->do_block); default: return FALSE; } @@ -4474,6 +4571,7 @@ static RETURN_CODE spawn_pipe_sub_command(CMD_NODE *node, HANDLE *child) case CMD_ONFAILURE: case CMD_ONSUCCESS: case CMD_IF: + case CMD_FOR: if (!rebuild_append_command(&rb, node, rbflags)) return ERROR_INVALID_FUNCTION; break;
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/tests/test_builtins.cmd.exp | 30 ++++++++++++------------ programs/cmd/wcmdmain.c | 11 +++++++++ 2 files changed, 26 insertions(+), 15 deletions(-)
diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 55768455b6d..1909bb9a8c1 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -438,7 +438,7 @@ h1 else echo h2 --- i1 else echo i2 i3 -@todo_wine@j2@space@ +j2@space@ --- k1 k2 @@ -465,20 +465,20 @@ p2 q2 q3 ------------- Testing for variables expansion in pipes -@todo_wine@a@space@@space@ -@todo_wine@b@space@@space@ +a@space@@space@ +b@space@@space@
--- -@todo_wine@a-c@space@@space@ -@todo_wine@a-d@space@@space@ -@todo_wine@b-c@space@@space@ -@todo_wine@b-d@space@@space@ +a-c@space@@space@ +a-d@space@@space@ +b-c@space@@space@ +b-d@space@@space@
--- -@todo_wine@a same a@space@ -@todo_wine@a diff b@space@ -@todo_wine@b diff a@space@ -@todo_wine@b same b@space@ +a same a@space@ +a diff b@space@ +b diff a@space@ +b same b@space@
--- foo @@ -490,16 +490,16 @@ yy!WINE_VAR!yy@space@ yyfooyy yyfooyy@space@
-@todo_wine@yy!WINE_VAR!yy@space@ +yy!WINE_VAR!yy@space@
-@todo_wine@yy!WINE_VAR!yy@space@ +yy!WINE_VAR!yy@space@
--- bar1bar@space@
-@todo_wine@bar2bar@space@ -@todo_wine@ +bar2bar@space@ + ------------- Testing internal commands return codes --- success/failure for basics SUCCESS 0 diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 8c9002acc1b..365dd071768 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -3538,6 +3538,14 @@ static BOOL rebuild_append_command(struct command_rebuild *rb, const CMD_NODE *n case CMD_FOR: ret = rebuild_command_for(rb, node, rbflags); break; + case CMD_BLOCK: + { + struct rebuild_flags new_rbflags = {.precedence = 0, .depth = rbflags.depth = 1}; + ret = rebuild_append(rb, L"( ") && + rebuild_append_command(rb, node->block, new_rbflags) && + rebuild_append(rb, L" ) "); + } + break; default: FIXME("Shouldn't happen\n"); ret = FALSE; @@ -4521,6 +4529,8 @@ static BOOL can_run_new_pipe(CMD_NODE *node) (!node->else_block || can_run_new_pipe(node->else_block)); case CMD_FOR: return can_run_new_pipe(node->do_block); + case CMD_BLOCK: + return can_run_new_pipe(node->block); default: return FALSE; } @@ -4572,6 +4582,7 @@ static RETURN_CODE spawn_pipe_sub_command(CMD_NODE *node, HANDLE *child) case CMD_ONSUCCESS: case CMD_IF: case CMD_FOR: + case CMD_BLOCK: if (!rebuild_append_command(&rb, node, rbflags)) return ERROR_INVALID_FUNCTION; break;
From: Eric Pouech epouech@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48027
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/builtins.c | 3 +- programs/cmd/wcmdmain.c | 85 ----------------------------------------- 2 files changed, 2 insertions(+), 86 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index ef84bee64ac..db2ced8d5e0 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -410,7 +410,8 @@ RETURN_CODE WCMD_choice(WCHAR *args) char choice;
overlapped.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); - if (SetFilePointerEx(GetStdHandle(STD_INPUT_HANDLE), zeroli, &li, FILE_CURRENT)) + if (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_DISK && + SetFilePointerEx(GetStdHandle(STD_INPUT_HANDLE), zeroli, &li, FILE_CURRENT)) { overlapped.Offset = li.LowPart; overlapped.OffsetHigh = li.HighPart; diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 365dd071768..d5b9eb32799 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -4454,88 +4454,6 @@ static RETURN_CODE for_control_execute(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *node return return_code; }
-static RETURN_CODE handle_pipe_command_old(CMD_NODE *node) -{ - static SECURITY_ATTRIBUTES sa = {.nLength = sizeof(sa), .lpSecurityDescriptor = NULL, .bInheritHandle = TRUE}; - WCHAR temp_path[MAX_PATH]; - WCHAR filename[MAX_PATH]; - CMD_REDIRECTION *output; - HANDLE saved[3]; - struct batch_context *saved_context = context; - RETURN_CODE return_code; - - /* pipe LHS & RHS are run outside of any batch context */ - context = NULL; - /* FIXME: a real pipe instead of writing to an intermediate file would be - * better. - * But waiting for completion of commands will require more work. - */ - /* FIXME check precedence (eg foo > a | more) - * with following code, | has higher precedence than > a - * (which is likely wrong IIRC, and not what previous code was doing) - */ - /* Generate a unique temporary filename */ - GetTempPathW(ARRAY_SIZE(temp_path), temp_path); - GetTempFileNameW(temp_path, L"CMD", 0, filename); - TRACE("Using temporary file of %ls\n", filename); - - /* set output for left hand side command */ - output = redirection_create_file(REDIR_WRITE_TO, 1, filename); - if (push_std_redirections(output, saved)) - { - RETURN_CODE return_code_left = node_execute(node->left); - pop_std_redirections(saved); - - if (errorlevel == RETURN_CODE_CANT_LAUNCH && saved_context) - ExitProcess(255); - return_code = ERROR_INVALID_FUNCTION; - if (!WCMD_is_break(return_code_left) && errorlevel != RETURN_CODE_CANT_LAUNCH) - { - HANDLE h = CreateFileW(filename, GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - if (h != INVALID_HANDLE_VALUE) - { - SetStdHandle(STD_INPUT_HANDLE, h); - return_code = node_execute(node->right); - if (errorlevel == RETURN_CODE_CANT_LAUNCH && saved_context) - ExitProcess(255); - } - } - DeleteFileW(filename); - errorlevel = return_code; - } - else return_code = ERROR_INVALID_FUNCTION; - redirection_dispose_list(output); - context = saved_context; - - return return_code; -} - -/* temp helper during migration */ -static BOOL can_run_new_pipe(CMD_NODE *node) -{ - switch (node->op) - { - case CMD_SINGLE: - return TRUE; - case CMD_PIPE: - case CMD_CONCAT: - case CMD_ONFAILURE: - case CMD_ONSUCCESS: - return can_run_new_pipe(node->left) && can_run_new_pipe(node->right); - case CMD_IF: - return can_run_new_pipe(node->then_block) && - (!node->else_block || can_run_new_pipe(node->else_block)); - case CMD_FOR: - return can_run_new_pipe(node->do_block); - case CMD_BLOCK: - return can_run_new_pipe(node->block); - default: - return FALSE; - } -} - static RETURN_CODE spawn_pipe_sub_command(CMD_NODE *node, HANDLE *child) { WCHAR cmd_string[MAXSTRING]; @@ -4621,9 +4539,6 @@ static RETURN_CODE handle_pipe_command(CMD_NODE *node) HANDLE saved_output; RETURN_CODE return_code;
- if (!can_run_new_pipe(node->left) || !can_run_new_pipe(node->right)) - return handle_pipe_command_old(node); - if (!CreatePipe(&read_pipe, &write_pipe, &sa, 0)) return ERROR_INVALID_FUNCTION; saved_output = GetStdHandle(STD_OUTPUT_HANDLE);