This is the fourth part in cmd.exe's engine rewrite.
It concerns: - start of decoupling parsing from execution by introducing ad hoc structure to hold parsing result to be passed for execution (done here for redirection, and if conditions), - refactor execution code with putting into helpers: + change of input/output streams + save / restore of input/output streams before / after execution
Note: - the handling of fd > 2 is clearly wrong, but it just mimics the current implementation. More work will be required afterwards (likely using directly CRT low level I/O), - I kept a few specific debug channels in place. They will be removed (or simplified at some point), but they could be useful to debug remaining issues.
-- v3: programs/cmd: Separate IF command parsing from execution. programs/cmd: Let errorlevel be a signed integer. programs/cmd: Create helper to execute a command. programs/cmd: Introduce structure CMD_REDIRECTION. programs/cmd: Introduce a helper to set std handles.
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 185 ++++++++++++++++++++-------------------- 1 file changed, 91 insertions(+), 94 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 2c5cf4a3196..a2eef017bfd 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1295,6 +1295,83 @@ void WCMD_run_program (WCHAR *command, BOOL called)
}
+static BOOL set_std_redirections(WCHAR *new_redir, WCHAR *in_pipe) +{ + SECURITY_ATTRIBUTES sa = {.nLength = sizeof(sa), .lpSecurityDescriptor = NULL, .bInheritHandle = TRUE}; + WCHAR *pos, *redir; + HANDLE h; + + /* STDIN could come from a preceding pipe, so delete on close if it does */ + if (in_pipe) + { + WINE_TRACE("Input coming from %s\n", wine_dbgstr_w(in_pipe)); + h = CreateFileW(in_pipe, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL); + if (h == INVALID_HANDLE_VALUE) return FALSE; + SetStdHandle(STD_INPUT_HANDLE, h); + /* Otherwise STDIN could come from a '<' redirect */ + } + else if ((pos = wcschr(new_redir,'<')) != NULL) + { + h = CreateFileW(WCMD_parameter(++pos, 0, NULL, FALSE, FALSE), GENERIC_READ, FILE_SHARE_READ, + &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) return FALSE; + SetStdHandle(STD_INPUT_HANDLE, h); + } + + redir = new_redir; + /* Scan the whole command looking for > and 2> */ + while (redir != NULL && ((pos = wcschr(redir,'>')) != NULL)) + { + DWORD disposition; + int std_idx; + + if (pos > redir && (*(pos-1)=='2')) + std_idx = STD_ERROR_HANDLE; + else + /* FIXME what if a number different from 1 & 2 is present? */ + std_idx = STD_OUTPUT_HANDLE; + + pos++; + if ('>' == *pos) + { + disposition = OPEN_ALWAYS; + pos++; + } + else + disposition = CREATE_ALWAYS; + + /* Add support for 2>&1 */ + redir = pos; + if (*pos == '&') + { + int idx = *(pos+1) - '0'; + + /* FIXME what if a number different from 1 & 2 is present? */ + if (!DuplicateHandle(GetCurrentProcess(), + GetStdHandle(idx == 2 ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE), + GetCurrentProcess(), + &h, + 0, TRUE, DUPLICATE_SAME_ACCESS)) + { + WINE_FIXME("Duplicating handle failed with gle %ld\n", GetLastError()); + } + } + else + { + WCHAR *param = WCMD_parameter(pos, 0, NULL, FALSE, FALSE); + h = CreateFileW(param, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, + &sa, disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) return FALSE; + if (SetFilePointer(h, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) + WCMD_print_error(); + } + SetStdHandle(std_idx, h); + } + return TRUE; +} + /***************************************************************************** * Process one command. If the command is EXIT this routine does not return. * We will recurse through here executing batch files. @@ -1305,21 +1382,16 @@ void WCMD_run_program (WCHAR *command, BOOL called) void WCMD_execute (const WCHAR *command, const WCHAR *redirects, CMD_NODE **cmdList, BOOL retrycall) { - WCHAR *cmd, *parms_start, *redir; - WCHAR *pos; + WCHAR *cmd, *parms_start; int status, i, cmd_index; - DWORD count, creationDisposition; - HANDLE h; + DWORD count; WCHAR *whichcmd; - SECURITY_ATTRIBUTES sa; WCHAR *new_cmd = NULL; WCHAR *new_redir = NULL; HANDLE old_stdhandles[3] = {GetStdHandle (STD_INPUT_HANDLE), GetStdHandle (STD_OUTPUT_HANDLE), GetStdHandle (STD_ERROR_HANDLE)}; - DWORD idx_stdhandles[3] = {STD_INPUT_HANDLE, - STD_OUTPUT_HANDLE, - STD_ERROR_HANDLE}; + static DWORD idx_stdhandles[3] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}; BOOL prev_echo_mode, piped = FALSE;
WINE_TRACE("command on entry:%s (%p)\n", @@ -1332,7 +1404,6 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
/* Move copy of the redirects onto the heap so it can be expanded */ new_redir = xalloc(MAXSTRING * sizeof(WCHAR)); - redir = new_redir;
/* Strip leading whitespaces, and a '@' if supplied */ whichcmd = WCMD_skip_leading_spaces(cmd); @@ -1381,7 +1452,6 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, } else { lstrcpyW(new_redir, redirects); } - /* Expand variables in command line mode only (batch mode will be expanded as the line is read in, except for 'for' loops) */ handleExpansion(new_cmd, (context != NULL), delayedsubst); @@ -1417,97 +1487,24 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, return; }
- sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = NULL; - sa.bInheritHandle = TRUE; - /* * Redirect stdin, stdout and/or stderr if required. * Note: Do not do this for a for or if statement as the pipe is for * the individual statements, not the for or if itself. */ if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF)) { - /* STDIN could come from a preceding pipe, so delete on close if it does */ - if (cmdList && CMD_node_get_command(*cmdList)->pipeFile[0] != 0x00) { - WINE_TRACE("Input coming from %s\n", wine_dbgstr_w(CMD_node_get_command(*cmdList)->pipeFile)); - h = CreateFileW(CMD_node_get_command(*cmdList)->pipeFile, GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL); - if (h == INVALID_HANDLE_VALUE) { - WCMD_print_error (); - free(cmd); - free(new_redir); - return; - } - SetStdHandle (STD_INPUT_HANDLE, h); - - /* No need to remember the temporary name any longer once opened */ - CMD_node_get_command(*cmdList)->pipeFile[0] = 0x00; - - /* Otherwise STDIN could come from a '<' redirect */ - } else if ((pos = wcschr(new_redir,'<')) != NULL) { - h = CreateFileW(WCMD_parameter(++pos, 0, NULL, FALSE, FALSE), GENERIC_READ, FILE_SHARE_READ, - &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (h == INVALID_HANDLE_VALUE) { - WCMD_print_error (); - free(cmd); - free(new_redir); - return; - } - SetStdHandle (STD_INPUT_HANDLE, h); - } - - /* Scan the whole command looking for > and 2> */ - while (redir != NULL && ((pos = wcschr(redir,'>')) != NULL)) { - int handle = 0; - - if (pos > redir && (*(pos-1)=='2')) - handle = 2; - else - handle = 1; - - pos++; - if ('>' == *pos) { - creationDisposition = OPEN_ALWAYS; - pos++; - } - else { - creationDisposition = CREATE_ALWAYS; - } - - /* Add support for 2>&1 */ - redir = pos; - if (*pos == '&') { - int idx = *(pos+1) - '0'; - - if (DuplicateHandle(GetCurrentProcess(), - GetStdHandle(idx_stdhandles[idx]), - GetCurrentProcess(), - &h, - 0, TRUE, DUPLICATE_SAME_ACCESS) == 0) { - WINE_FIXME("Duplicating handle failed with gle %ld\n", GetLastError()); - } - WINE_TRACE("Redirect %d (%p) to %d (%p)\n", handle, GetStdHandle(idx_stdhandles[idx]), idx, h); - - } else { - WCHAR *param = WCMD_parameter(pos, 0, NULL, FALSE, FALSE); - h = CreateFileW(param, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, - &sa, creationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (h == INVALID_HANDLE_VALUE) { - WCMD_print_error (); - free(cmd); - free(new_redir); - return; - } - if (SetFilePointer (h, 0, NULL, FILE_END) == - INVALID_SET_FILE_POINTER) { - WCMD_print_error (); - } - WINE_TRACE("Redirect %d to '%s' (%p)\n", handle, wine_dbgstr_w(param), h); - } - - SetStdHandle (idx_stdhandles[handle], h); + WCHAR *in_pipe = NULL; + if (cmdList && CMD_node_get_command(*cmdList)->pipeFile[0] != 0x00) + in_pipe = CMD_node_get_command(*cmdList)->pipeFile; + if (!set_std_redirections(new_redir, in_pipe)) { + WCMD_print_error (); + free(cmd); + free(new_redir); + return; } + if (in_pipe) + /* No need to remember the temporary name any longer once opened */ + in_pipe[0] = 0x00; } else { WINE_TRACE("Not touching redirects for a FOR or IF command\n"); }
From: Eric Pouech epouech@codeweavers.com
This structure will held information for a redirection, and can be chained in a list when multiple redirections are listed.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/batch.c | 2 +- programs/cmd/wcmd.h | 21 ++- programs/cmd/wcmdmain.c | 399 ++++++++++++++++++++++++++-------------- 3 files changed, 281 insertions(+), 141 deletions(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c index b68642fc29f..455cc99083c 100644 --- a/programs/cmd/batch.c +++ b/programs/cmd/batch.c @@ -93,7 +93,7 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA the command as a result of a call failing to find a program, hence the retryCall parameter below is FALSE */ WCMD_process_commands(toExecute, FALSE, FALSE); - WCMD_free_commands(toExecute); + node_dispose_tree(toExecute); toExecute = NULL; } CloseHandle (h); diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index f538e4fc1a6..3ed98bcf73e 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -34,6 +34,19 @@ /* msdn specified max for Win XP */ #define MAXSTRING 8192
+/* Data structure to express a redirection */ +typedef struct _CMD_REDIRECTION +{ + enum CMD_REDIRECTION_KIND {REDIR_READ_FROM, REDIR_WRITE_TO, REDIR_WRITE_APPEND, REDIR_WRITE_CLONE} kind; + unsigned short fd; + struct _CMD_REDIRECTION *next; + union + { + unsigned short clone; /* only if kind is REDIR_WRITE_CLONE */ + WCHAR file[1]; /* only if kind is READ_FROM, WRITE or WRITE_APPEND */ + }; +} CMD_REDIRECTION; + /* Data structure to hold commands delimiters/separators */
typedef enum _CMD_OPERATOR @@ -50,7 +63,7 @@ typedef enum _CMD_OPERATOR typedef struct _CMD_COMMAND { WCHAR *command; /* Command string to execute */ - WCHAR *redirects; /* Redirects in place */ + CMD_REDIRECTION *redirects; /* Redirects in place */ int bracketDepth;/* How deep bracketing have we got to */ WCHAR pipeFile[MAX_PATH]; /* Where to get input from for pipes */ } CMD_COMMAND; @@ -153,9 +166,9 @@ BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars, LPDWO
WCHAR *WCMD_ReadAndParseLine(const WCHAR *initialcmd, CMD_NODE **output, HANDLE readFrom); CMD_NODE *WCMD_process_commands(CMD_NODE *thisCmd, BOOL oneBracket, BOOL retrycall); -void WCMD_free_commands(CMD_NODE *cmds); -void WCMD_execute (const WCHAR *orig_command, const WCHAR *redirects, - CMD_NODE **cmdList, BOOL retrycall); +void node_dispose_tree(CMD_NODE *cmds); +void WCMD_execute(const WCHAR *orig_command, CMD_REDIRECTION *redirects, + CMD_NODE **cmdList, BOOL retrycall);
void *xrealloc(void *, size_t) __WINE_ALLOC_SIZE(2) __WINE_DEALLOC(free);
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index a2eef017bfd..7f3e995b6e4 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -31,6 +31,8 @@ #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(cmd); +WINE_DECLARE_DEBUG_CHANNEL(cmd_lexer); +WINE_DECLARE_DEBUG_CHANNEL(cmd_redir);
extern const WCHAR inbuilt[][10]; extern struct env_stack *pushd_directories; @@ -946,6 +948,92 @@ static void WCMD_parse (const WCHAR *s, WCHAR *q, WCHAR *p1, WCHAR *p2) /* Data structures for commands */ /* ============================== */
+static void redirection_dispose_list(CMD_REDIRECTION *redir) + { + while (redir) + { + CMD_REDIRECTION *next = redir->next; + free(redir); + redir = next; + } +} + +static CMD_REDIRECTION *redirection_create_file(enum CMD_REDIRECTION_KIND kind, unsigned fd, const WCHAR *file) +{ + size_t len = wcslen(file); + CMD_REDIRECTION *redir = xalloc(offsetof(CMD_REDIRECTION, file[len + 1])); + + redir->kind = kind; + redir->fd = fd; + memcpy(redir->file, file, len * sizeof(WCHAR)); + redir->file[len] = L'\0'; + redir->next = NULL; + + return redir; +} + +static CMD_REDIRECTION *redirection_create_clone(unsigned fd, unsigned fd_clone) +{ + CMD_REDIRECTION *redir = xalloc(sizeof(*redir)); + + redir->kind = REDIR_WRITE_CLONE; + redir->fd = fd; + redir->clone = fd_clone; + redir->next = NULL; + + return redir; +} + +static const char *redirection_as_string(const CMD_REDIRECTION *redir) +{ + switch (redir->kind) + { + case REDIR_READ_FROM: + return wine_dbg_sprintf("%u< (%ls)", redir->fd, redir->file); + case REDIR_WRITE_TO: + return wine_dbg_sprintf("%u> (%ls)", redir->fd, redir->file); + case REDIR_WRITE_APPEND: + return wine_dbg_sprintf("%u>> (%ls)", redir->fd, redir->file); + case REDIR_WRITE_CLONE: + return wine_dbg_sprintf("%u>&%u", redir->fd, redir->clone); + default: + return "-^-"; + } +} + +static void command_dispose(CMD_COMMAND *cmd) +{ + if (cmd) + { + free(cmd->command); + redirection_dispose_list(cmd->redirects); + free(cmd); + } +} + +/*************************************************************************** + * node_dispose_tree + * + * Frees the storage held for a parsed command line + * - This is not done in the process_commands, as eventually the current + * pointer will be modified within the commands, and hence a single free + * routine is simpler + */ +void node_dispose_tree(CMD_NODE *cmds) +{ + /* Loop through the commands, freeing them one by one */ + while (cmds) + { + CMD_NODE *thisCmd = cmds; + cmds = CMD_node_next(cmds); + if (thisCmd->op == CMD_SINGLE) + command_dispose(thisCmd->command); + else + node_dispose_tree(thisCmd->left); + free(thisCmd); + } +} + static CMD_NODE *node_create_single(CMD_COMMAND *c) { CMD_NODE *new = xalloc(sizeof(CMD_NODE)); @@ -967,6 +1055,66 @@ static CMD_NODE *node_create_binary(CMD_OPERATOR op, CMD_NODE *l, CMD_NODE *r) return new; }
+static const char *op2str(CMD_OPERATOR op) +{ + static const char* optable[] = {"op-single", "op-&", "op-||", "op-&&", "op-|"}; + if (op < ARRAY_SIZE(optable)) return optable[op]; + return "op-unk"; +} + +/* Helper for WCMD_DumpCommands */ +static void dump_cmd_node_tree(CMD_NODE *node) +{ + TRACE("("); + if (!node) + TRACE("<<>>"); + else + { + switch (node->op) + { + case CMD_SINGLE: + if (node->command) /* FIXME temp */ + { + TRACE("[[%ls]]", node->command->command); + if (node->command->redirects) + { + const CMD_REDIRECTION *redir; + TRACE("{{"); + for (redir = node->command->redirects; redir; redir = redir->next) + TRACE("%s%s", redirection_as_string(redir), redir->next ? ", " : ""); + TRACE("}}"); + } + } + else + TRACE("[~]"); + break; + case CMD_CONCAT: + case CMD_ONFAILURE: + case CMD_ONSUCCESS: + case CMD_PIPE: + dump_cmd_node_tree(node->left); + TRACE(" %s ", op2str(node->op)); + dump_cmd_node_tree(node->right); + break; + default: + FIXME("Unsupported node operator %u\n", node->op); + } + } + TRACE(")"); +} + +/*************************************************************************** + * WCMD_DumpCommands + * + * Dumps out the parsed command line to ensure syntax is correct + */ +static void WCMD_DumpCommands(CMD_NODE *commands, const char *what) +{ + TRACE("%s:\n", what); + dump_cmd_node_tree(commands); + TRACE("\n"); +} + static void init_msvcrt_io_block(STARTUPINFOW* st) { STARTUPINFOW st_p; @@ -1280,7 +1428,7 @@ void WCMD_run_program (WCHAR *command, BOOL called) /* Parse the command string, without reading any more input */ WCMD_ReadAndParseLine(command, &toExecute, INVALID_HANDLE_VALUE); WCMD_process_commands(toExecute, FALSE, called); - WCMD_free_commands(toExecute); + node_dispose_tree(toExecute); toExecute = NULL; return; } @@ -1295,10 +1443,17 @@ void WCMD_run_program (WCHAR *command, BOOL called)
}
-static BOOL set_std_redirections(WCHAR *new_redir, WCHAR *in_pipe) +/* this is obviously wrong... will require more work to be fixed */ +static inline unsigned clamp_fd(unsigned fd) { - SECURITY_ATTRIBUTES sa = {.nLength = sizeof(sa), .lpSecurityDescriptor = NULL, .bInheritHandle = TRUE}; - WCHAR *pos, *redir; + return fd <= 2 ? fd : 1; +} + +static BOOL set_std_redirections(CMD_REDIRECTION *redir, WCHAR *in_pipe) +{ + static DWORD std_index[3] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}; + static SECURITY_ATTRIBUTES sa = {.nLength = sizeof(sa), .lpSecurityDescriptor = NULL, .bInheritHandle = TRUE}; + WCHAR expanded_filename[MAXSTRING]; HANDLE h;
/* STDIN could come from a preceding pipe, so delete on close if it does */ @@ -1312,62 +1467,54 @@ static BOOL set_std_redirections(WCHAR *new_redir, WCHAR *in_pipe) SetStdHandle(STD_INPUT_HANDLE, h); /* Otherwise STDIN could come from a '<' redirect */ } - else if ((pos = wcschr(new_redir,'<')) != NULL) + for (; redir; redir = redir->next) { - h = CreateFileW(WCMD_parameter(++pos, 0, NULL, FALSE, FALSE), GENERIC_READ, FILE_SHARE_READ, - &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (h == INVALID_HANDLE_VALUE) return FALSE; - SetStdHandle(STD_INPUT_HANDLE, h); - } - - redir = new_redir; - /* Scan the whole command looking for > and 2> */ - while (redir != NULL && ((pos = wcschr(redir,'>')) != NULL)) - { - DWORD disposition; - int std_idx; - - if (pos > redir && (*(pos-1)=='2')) - std_idx = STD_ERROR_HANDLE; - else - /* FIXME what if a number different from 1 & 2 is present? */ - std_idx = STD_OUTPUT_HANDLE; - - pos++; - if ('>' == *pos) + if (redir->kind != REDIR_WRITE_CLONE) { - disposition = OPEN_ALWAYS; - pos++; + wcscpy(expanded_filename, redir->file); + handleExpansion(expanded_filename, (context != NULL), delayedsubst); } - else - disposition = CREATE_ALWAYS; - - /* Add support for 2>&1 */ - redir = pos; - if (*pos == '&') + switch (redir->kind) { - int idx = *(pos+1) - '0'; - - /* FIXME what if a number different from 1 & 2 is present? */ + case REDIR_READ_FROM: + if (in_pipe) continue;/* give precedence to pipe */ + h = CreateFileW(expanded_filename, GENERIC_READ, FILE_SHARE_READ, + &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + TRACE_(cmd_redir)("Open (%ls) => %p\n", redir->file, h); + if (h == INVALID_HANDLE_VALUE) + { + WARN("Failed to open (%ls): %lu\n", redir->file, GetLastError()); + return FALSE; + } + break; + case REDIR_WRITE_TO: + case REDIR_WRITE_APPEND: + { + DWORD disposition = redir->kind == REDIR_WRITE_TO ? CREATE_ALWAYS : OPEN_ALWAYS; + h = CreateFileW(expanded_filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, + &sa, disposition, FILE_ATTRIBUTE_NORMAL, NULL); + TRACE_(cmd_redir)("Open %u (%ls) => %p\n", redir->fd, redir->file, h); + if (h == INVALID_HANDLE_VALUE) + { + WARN("Failed to open %u (%ls): %lu\n", redir->fd, redir->file, GetLastError()); + return FALSE; + } + if (SetFilePointer(h, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) + WCMD_print_error(); + } + break; + case REDIR_WRITE_CLONE: if (!DuplicateHandle(GetCurrentProcess(), - GetStdHandle(idx == 2 ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE), + GetStdHandle(std_index[clamp_fd(redir->clone)]), GetCurrentProcess(), &h, 0, TRUE, DUPLICATE_SAME_ACCESS)) { - WINE_FIXME("Duplicating handle failed with gle %ld\n", GetLastError()); + WARN("Duplicating handle failed with gle %ld\n", GetLastError()); } + break; } - else - { - WCHAR *param = WCMD_parameter(pos, 0, NULL, FALSE, FALSE); - h = CreateFileW(param, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, - &sa, disposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (h == INVALID_HANDLE_VALUE) return FALSE; - if (SetFilePointer(h, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) - WCMD_print_error(); - } - SetStdHandle(std_idx, h); + SetStdHandle(std_index[clamp_fd(redir->fd)], h); } return TRUE; } @@ -1379,20 +1526,20 @@ static BOOL set_std_redirections(WCHAR *new_redir, WCHAR *in_pipe) * try to run it as an internal command. 'retrycall' represents whether * we are attempting this retry. */ -void WCMD_execute (const WCHAR *command, const WCHAR *redirects, - CMD_NODE **cmdList, BOOL retrycall) +void WCMD_execute(const WCHAR *command, CMD_REDIRECTION *redirects, + CMD_NODE **cmdList, BOOL retrycall) { WCHAR *cmd, *parms_start; int status, i, cmd_index; DWORD count; WCHAR *whichcmd; WCHAR *new_cmd = NULL; - WCHAR *new_redir = NULL; HANDLE old_stdhandles[3] = {GetStdHandle (STD_INPUT_HANDLE), GetStdHandle (STD_OUTPUT_HANDLE), GetStdHandle (STD_ERROR_HANDLE)}; static DWORD idx_stdhandles[3] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}; BOOL prev_echo_mode, piped = FALSE; + CMD_REDIRECTION *piped_redir;
WINE_TRACE("command on entry:%s (%p)\n", wine_dbgstr_w(command), cmdList); @@ -1402,9 +1549,6 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, lstrcpyW(new_cmd, command); cmd = new_cmd;
- /* Move copy of the redirects onto the heap so it can be expanded */ - new_redir = xalloc(MAXSTRING * sizeof(WCHAR)); - /* Strip leading whitespaces, and a '@' if supplied */ whichcmd = WCMD_skip_leading_spaces(cmd); WINE_TRACE("Command: '%s'\n", wine_dbgstr_w(cmd)); @@ -1447,15 +1591,15 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
/* If piped output, send stdout to the pipe by appending >filename to redirects */ if (piped) { - wsprintfW (new_redir, L"%s > %s", redirects, CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile); - WINE_TRACE("Redirects now %s\n", wine_dbgstr_w(new_redir)); + const WCHAR *to = CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile; + piped_redir = redirection_create_file(REDIR_WRITE_TO, 1, to); + piped_redir->next = redirects; } else { - lstrcpyW(new_redir, redirects); + piped_redir = redirects; } /* Expand variables in command line mode only (batch mode will be expanded as the line is read in, except for 'for' loops) */ handleExpansion(new_cmd, (context != NULL), delayedsubst); - handleExpansion(new_redir, (context != NULL), delayedsubst);
/* * Changing default drive has to be handled as a special case, anything @@ -1482,9 +1626,7 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, WINE_TRACE("Got directory %s as %s\n", wine_dbgstr_w(envvar), wine_dbgstr_w(cmd)); status = SetCurrentDirectoryW(cmd); if (!status) WCMD_print_error (); - free(cmd); - free(new_redir); - return; + goto cleanup; }
/* @@ -1496,11 +1638,9 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, WCHAR *in_pipe = NULL; if (cmdList && CMD_node_get_command(*cmdList)->pipeFile[0] != 0x00) in_pipe = CMD_node_get_command(*cmdList)->pipeFile; - if (!set_std_redirections(new_redir, in_pipe)) { + if (!set_std_redirections(piped_redir, in_pipe)) { WCMD_print_error (); - free(cmd); - free(new_redir); - return; + goto cleanup; } if (in_pipe) /* No need to remember the temporary name any longer once opened */ @@ -1663,8 +1803,9 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, WCMD_run_program (whichcmd, FALSE); echo_mode = prev_echo_mode; } +cleanup: free(cmd); - free(new_redir); + if (piped_redir != redirects) free(piped_redir);
/* Restore old handles */ for (i=0; i<3; i++) { @@ -1690,34 +1831,11 @@ WCHAR *WCMD_LoadMessage(UINT id) { return msg; }
-static const char *op2str(CMD_OPERATOR op) +static WCHAR *find_chr(WCHAR *in, WCHAR *last, const WCHAR *delims) { - static const char* optable[] = {"op-single", "op-&", "op-||", "op-&&", "op-|"}; - if (op < ARRAY_SIZE(optable)) return optable[op]; - return "op-unk"; -} - -/*************************************************************************** - * WCMD_DumpCommands - * - * Dumps out the parsed command line to ensure syntax is correct - */ -static void WCMD_DumpCommands(CMD_NODE *commands) -{ - CMD_NODE *thisCmd = commands; - - WINE_TRACE("Parsed line:\n"); - while (thisCmd != NULL) - { - TRACE("%p %2.2d %p %s Redir:%s %s\n", - thisCmd, - CMD_node_get_depth(thisCmd), - CMD_node_next(thisCmd), - wine_dbgstr_w(CMD_node_get_command(thisCmd)->command), - wine_dbgstr_w(CMD_node_get_command(thisCmd)->redirects), - op2str(thisCmd->op)); - thisCmd = CMD_node_next(thisCmd); - } + for (; in < last; in++) + if (wcschr(delims, *in)) return in; + return NULL; }
/*************************************************************************** @@ -1737,15 +1855,53 @@ static CMD_COMMAND *WCMD_createCommand(WCHAR *command, int *commandLen,
/* Copy in the command */ if (command) { + WCHAR *pos; + WCHAR *last = redirs + *redirLen; + CMD_REDIRECTION **insrt; + thisEntry->command = xalloc((*commandLen + 1) * sizeof(WCHAR)); memcpy(thisEntry->command, command, *commandLen * sizeof(WCHAR)); thisEntry->command[*commandLen] = 0x00;
- /* Copy in the redirects */ - thisEntry->redirects = xalloc((*redirLen + 1) * sizeof(WCHAR)); - memcpy(thisEntry->redirects, redirs, *redirLen * sizeof(WCHAR)); - thisEntry->redirects[*redirLen] = 0x00; - thisEntry->pipeFile[0] = 0x00; + TRACE_(cmd_redir)("Splitting redirect from %s\n", wine_dbgstr_wn(redirs, *redirLen)); + if (redirs) redirs[*redirLen] = 0; + /* Create redirects, keeping order (eg "2>foo 1>&2") */ + insrt = &thisEntry->redirects; + *insrt = NULL; + for (pos = redirs; pos; insrt = &(*insrt)->next) + { + WCHAR *p = find_chr(pos, last, L"<>"); + WCHAR *filename; + + TRACE_(cmd_redir)("\t=> %s\n", wine_dbgstr_wn(p, *redirLen - (p - redirs))); + if (!p) break; + + if (*p == L'<') + { + filename = WCMD_parameter(p + 1, 0, NULL, FALSE, FALSE); + *insrt = redirection_create_file(REDIR_READ_FROM, 0, filename); + } + else + { + unsigned fd = 1; + unsigned op = REDIR_WRITE_TO; + + if (p > redirs && p[-1] >= L'2' && p[-1] <= L'9') fd = p[-1] - L'0'; + if (*++p == L'>') {p++; op = REDIR_WRITE_APPEND;} + if (*p == L'&' && (p[1] >= L'0' && p[1] <= L'9')) + { + *insrt = redirection_create_clone(fd, p[1] - '0'); + p++; + } + else + { + filename = WCMD_parameter(p, 0, NULL, FALSE, FALSE); + *insrt = redirection_create_file(op, fd, filename); + } + } + TRACE_(cmd_redir)("Adding redirect %s\n", redirection_as_string(*insrt)); + pos = p + 1; + }
/* Reset the lengths */ *commandLen = 0; @@ -1756,10 +1912,10 @@ static CMD_COMMAND *WCMD_createCommand(WCHAR *command, int *commandLen, } else { thisEntry->command = NULL; thisEntry->redirects = NULL; - thisEntry->pipeFile[0] = 0x00; }
/* Fill in other fields */ + thisEntry->pipeFile[0] = 0x00; thisEntry->bracketDepth = curDepth; return thisEntry; } @@ -1901,6 +2057,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE return NULL; } curPos = extraSpace; + TRACE_(cmd_lexer)("About to parse line (%ls)\n", extraSpace);
/* Handle truncated input - issue warning */ if (lstrlenW(extraSpace) == MAXSTRING -1) { @@ -2381,13 +2538,13 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE
if (curDepth > lineCurDepth) { WINE_TRACE("Brackets do not match, error out without executing.\n"); - WCMD_free_commands(*output); + node_dispose_tree(*output); *output = NULL; errorlevel = 255; }
/* Dump out the parsed output */ - WCMD_DumpCommands(*output); + WCMD_DumpCommands(*output, "Parsed line");
return extraSpace; } @@ -2423,7 +2580,7 @@ CMD_NODE *WCMD_process_commands(CMD_NODE *thisCmd, BOOL oneBracket, Also, skip over any batch labels (eg. :fred) */ if (CMD_node_get_command(thisCmd)->command && CMD_node_get_command(thisCmd)->command[0] != ':') { WINE_TRACE("Executing command: '%s'\n", wine_dbgstr_w(CMD_node_get_command(thisCmd)->command)); - WCMD_execute (CMD_node_get_command(thisCmd)->command, CMD_node_get_command(thisCmd)->redirects, &thisCmd, retrycall); + WCMD_execute(CMD_node_get_command(thisCmd)->command, CMD_node_get_command(thisCmd)->redirects, &thisCmd, retrycall); }
/* Step on unless the command itself already stepped on */ @@ -2432,36 +2589,6 @@ CMD_NODE *WCMD_process_commands(CMD_NODE *thisCmd, BOOL oneBracket, return NULL; }
-static void WCMD_free_command(CMD_COMMAND *cmd) -{ - free(cmd->command); - free(cmd->redirects); - free(cmd); -} - -/*************************************************************************** - * WCMD_free_commands - * - * Frees the storage held for a parsed command line - * - This is not done in the process_commands, as eventually the current - * pointer will be modified within the commands, and hence a single free - * routine is simpler - */ -void WCMD_free_commands(CMD_NODE *cmds) -{ - /* Loop through the commands, freeing them one by one */ - while (cmds) - { - CMD_NODE *thisCmd = cmds; - cmds = CMD_node_next(cmds); - if (thisCmd->op == CMD_SINGLE) - WCMD_free_command(thisCmd->command); - else - WCMD_free_commands(thisCmd->left); - free(thisCmd); - } -} - static BOOL WINAPI my_event_handler(DWORD ctrl) { WCMD_output(L"\n"); @@ -2738,7 +2865,7 @@ int __cdecl wmain (int argc, WCHAR *argvW[]) /* Parse the command string, without reading any more input */ WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE); WCMD_process_commands(toExecute, FALSE, FALSE); - WCMD_free_commands(toExecute); + node_dispose_tree(toExecute); toExecute = NULL;
free(cmd); @@ -2819,7 +2946,7 @@ int __cdecl wmain (int argc, WCHAR *argvW[]) /* Parse the command string, without reading any more input */ WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE); WCMD_process_commands(toExecute, FALSE, FALSE); - WCMD_free_commands(toExecute); + node_dispose_tree(toExecute); toExecute = NULL; free(cmd); } @@ -2838,7 +2965,7 @@ int __cdecl wmain (int argc, WCHAR *argvW[]) if (!WCMD_ReadAndParseLine(NULL, &toExecute, GetStdHandle(STD_INPUT_HANDLE))) break; WCMD_process_commands(toExecute, FALSE, FALSE); - WCMD_free_commands(toExecute); + node_dispose_tree(toExecute); promptNewLine = !!toExecute; toExecute = NULL; }
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 177 +++++++++++++++++++++++++--------------- 1 file changed, 109 insertions(+), 68 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 7f3e995b6e4..1a5cd8ea326 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1526,20 +1526,13 @@ static BOOL set_std_redirections(CMD_REDIRECTION *redir, WCHAR *in_pipe) * try to run it as an internal command. 'retrycall' represents whether * we are attempting this retry. */ -void WCMD_execute(const WCHAR *command, CMD_REDIRECTION *redirects, - CMD_NODE **cmdList, BOOL retrycall) +static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOOL retrycall) { WCHAR *cmd, *parms_start; - int status, i, cmd_index; - DWORD count; + int status, cmd_index, count; WCHAR *whichcmd; WCHAR *new_cmd = NULL; - HANDLE old_stdhandles[3] = {GetStdHandle (STD_INPUT_HANDLE), - GetStdHandle (STD_OUTPUT_HANDLE), - GetStdHandle (STD_ERROR_HANDLE)}; - static DWORD idx_stdhandles[3] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}; - BOOL prev_echo_mode, piped = FALSE; - CMD_REDIRECTION *piped_redir; + BOOL prev_echo_mode;
WINE_TRACE("command on entry:%s (%p)\n", wine_dbgstr_w(command), cmdList); @@ -1565,40 +1558,6 @@ void WCMD_execute(const WCHAR *command, CMD_REDIRECTION *redirects, } parms_start = WCMD_skip_leading_spaces (&whichcmd[count]);
- /* If the next command is a pipe then we implement pipes by redirecting - the output from this command to a temp file and input into the - next command from that temp file. - Note: Do not do this for a for or if statement as the pipe is for - the individual statements, not the for or if itself. - FIXME: Use of named pipes would make more sense here as currently this - process has to finish before the next one can start but this requires - a change to not wait for the first app to finish but rather the pipe */ - if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF) && - cmdList && (*cmdList)->op == CMD_PIPE) { - - WCHAR temp_path[MAX_PATH]; - - /* Remember piping is in action */ - WINE_TRACE("Output needs to be piped\n"); - piped = TRUE; - - /* Generate a unique temporary filename */ - GetTempPathW(ARRAY_SIZE(temp_path), temp_path); - GetTempFileNameW(temp_path, L"CMD", 0, CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile); - WINE_TRACE("Using temporary file of %s\n", - wine_dbgstr_w(CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile)); - } - - /* If piped output, send stdout to the pipe by appending >filename to redirects */ - if (piped) { - const WCHAR *to = CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile; - piped_redir = redirection_create_file(REDIR_WRITE_TO, 1, to); - piped_redir->next = redirects; - } else { - piped_redir = redirects; - } - /* Expand variables in command line mode only (batch mode will - be expanded as the line is read in, except for 'for' loops) */ handleExpansion(new_cmd, (context != NULL), delayedsubst);
/* @@ -1629,25 +1588,6 @@ void WCMD_execute(const WCHAR *command, CMD_REDIRECTION *redirects, goto cleanup; }
- /* - * Redirect stdin, stdout and/or stderr if required. - * Note: Do not do this for a for or if statement as the pipe is for - * the individual statements, not the for or if itself. - */ - if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF)) { - WCHAR *in_pipe = NULL; - if (cmdList && CMD_node_get_command(*cmdList)->pipeFile[0] != 0x00) - in_pipe = CMD_node_get_command(*cmdList)->pipeFile; - if (!set_std_redirections(piped_redir, in_pipe)) { - WCMD_print_error (); - goto cleanup; - } - if (in_pipe) - /* No need to remember the temporary name any longer once opened */ - in_pipe[0] = 0x00; - } else { - WINE_TRACE("Not touching redirects for a FOR or IF command\n"); - } WCMD_parse (parms_start, quals, param1, param2); WINE_TRACE("param1: %s, param2: %s\n", wine_dbgstr_w(param1), wine_dbgstr_w(param2));
@@ -1803,16 +1743,117 @@ void WCMD_execute(const WCHAR *command, CMD_REDIRECTION *redirects, WCMD_run_program (whichcmd, FALSE); echo_mode = prev_echo_mode; } +cleanup: + free(cmd); +} + +/***************************************************************************** + * Process one command. If the command is EXIT this routine does not return. + * We will recurse through here executing batch files. + * Note: If call is used to a non-existing program, we reparse the line and + * try to run it as an internal command. 'retrycall' represents whether + * we are attempting this retry. + */ +void WCMD_execute(const WCHAR *command, CMD_REDIRECTION *redirects, + CMD_NODE **cmdList, BOOL retrycall) +{ + WCHAR *cmd; + int i, cmd_index, count; + WCHAR *whichcmd; + WCHAR *new_cmd = NULL; + HANDLE old_stdhandles[3] = {GetStdHandle (STD_INPUT_HANDLE), + GetStdHandle (STD_OUTPUT_HANDLE), + GetStdHandle (STD_ERROR_HANDLE)}; + static DWORD idx_stdhandles[3] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}; + CMD_REDIRECTION *piped_redir = redirects; + + WINE_TRACE("command on entry:%s (%p)\n", + wine_dbgstr_w(command), cmdList); + + /* Move copy of the command onto the heap so it can be expanded */ + new_cmd = xalloc(MAXSTRING * sizeof(WCHAR)); + lstrcpyW(new_cmd, command); + cmd = new_cmd; + + /* Strip leading whitespaces, and a '@' if supplied */ + whichcmd = WCMD_skip_leading_spaces(cmd); + WINE_TRACE("Command: '%s'\n", wine_dbgstr_w(cmd)); + if (whichcmd[0] == '@') whichcmd++; + + /* Check if the command entered is internal, and identify which one */ + count = 0; + while (IsCharAlphaNumericW(whichcmd[count])) { + count++; + } + for (cmd_index=0; cmd_index<=WCMD_EXIT; cmd_index++) { + if (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, + whichcmd, count, inbuilt[cmd_index], -1) == CSTR_EQUAL) break; + } + + /* If the next command is a pipe then we implement pipes by redirecting + the output from this command to a temp file and input into the + next command from that temp file. + Note: Do not do this for a for or if statement as the pipe is for + the individual statements, not the for or if itself. + FIXME: Use of named pipes would make more sense here as currently this + process has to finish before the next one can start but this requires + a change to not wait for the first app to finish but rather the pipe */ + /* FIXME this is wrong: we need to discriminate between redirection in individual + * commands in the blocks, vs redirection of the whole command. + */ + if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF) && + cmdList && (*cmdList)->op == CMD_PIPE) + { + const WCHAR *to; + WCHAR temp_path[MAX_PATH]; + + /* Remember piping is in action */ + WINE_TRACE("Output needs to be piped\n"); + + /* Generate a unique temporary filename */ + GetTempPathW(ARRAY_SIZE(temp_path), temp_path); + GetTempFileNameW(temp_path, L"CMD", 0, CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile); + WINE_TRACE("Using temporary file of %s\n", + wine_dbgstr_w(CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile)); + /* send stdout to the pipe by appending >filename to redirects */ + to = CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile; + piped_redir = redirection_create_file(REDIR_WRITE_TO, 1, to); + piped_redir->next = redirects; + } + + /* + * Redirect stdin, stdout and/or stderr if required. + * Note: Do not do this for a for or if statement as the pipe is for + * the individual statements, not the for or if itself. + */ + /* FIXME this is wrong (see above) */ + if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF)) { + WCHAR *in_pipe = NULL; + if (cmdList && CMD_node_get_command(*cmdList)->pipeFile[0] != 0x00) + in_pipe = CMD_node_get_command(*cmdList)->pipeFile; + if (!set_std_redirections(piped_redir, in_pipe)) { + WCMD_print_error (); + goto cleanup; + } + if (in_pipe) + /* No need to remember the temporary name any longer once opened */ + in_pipe[0] = 0x00; + } else { + WINE_TRACE("Not touching redirects for a FOR or IF command\n"); + } + execute_single_command(command, cmdList, retrycall); cleanup: free(cmd); if (piped_redir != redirects) free(piped_redir);
/* Restore old handles */ - for (i=0; i<3; i++) { - if (old_stdhandles[i] != GetStdHandle(idx_stdhandles[i])) { - CloseHandle (GetStdHandle (idx_stdhandles[i])); - SetStdHandle (idx_stdhandles[i], old_stdhandles[i]); - } + for (i = 0; i < 3; i++) + { + if (old_stdhandles[i] != GetStdHandle(idx_stdhandles[i])) + { + CloseHandle(GetStdHandle (idx_stdhandles[i])); + SetStdHandle(idx_stdhandles[i], old_stdhandles[i]); + } } }
From: Eric Pouech epouech@codeweavers.com
Manual testing show that native reports errorlevel as a signed entity.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/builtins.c | 9 +++++---- programs/cmd/tests/test_builtins.cmd | 2 ++ programs/cmd/tests/test_builtins.cmd.exp | 1 + programs/cmd/wcmd.h | 2 +- programs/cmd/wcmdmain.c | 8 ++++---- 5 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index bc9ba9e5f11..75e651d2086 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -402,7 +402,7 @@ void WCMD_choice (const WCHAR * args) { SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldmode);
errorlevel = (ptr - opt_c) + 1; - WINE_TRACE("answer: %ld\n", errorlevel); + WINE_TRACE("answer: %d\n", errorlevel); free(my_command); return; } @@ -2838,7 +2838,7 @@ int evaluate_if_condition(WCHAR *p, WCHAR **command, int *test, int *negate) WCHAR *endptr; long int param_int = wcstol(param, &endptr, 10); if (*endptr) goto syntax_err; - *test = ((long int)errorlevel >= param_int); + *test = (errorlevel >= param_int); WCMD_parameter(p, 2+(*negate), command, FALSE, FALSE); } else if (!lstrcmpiW(condition, L"exist")) { @@ -4483,9 +4483,10 @@ void WCMD_start(WCHAR *args)
if (CreateProcessW( file, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &st, &pi )) { + DWORD exit_code; WaitForSingleObject( pi.hProcess, INFINITE ); - GetExitCodeProcess( pi.hProcess, &errorlevel ); - if (errorlevel == STILL_ACTIVE) errorlevel = 0; + GetExitCodeProcess( pi.hProcess, &exit_code ); + errorlevel = (exit_code == STILL_ACTIVE) ? 0 : exit_code; CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index 8fa724c2682..02020a5a6ef 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -3310,6 +3310,8 @@ should_not_exist 2> nul > nul echo %ErrorLevel% rem nt 4.0 doesn't really support a way of setting errorlevel, so this is weak rem See http://www.robvanderwoude.com/exit.php +call :setError -9999 +echo %ErrorLevel% call :setError 1 echo %ErrorLevel% if errorlevel 2 echo errorlevel too high, bad diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 76665765b26..0f33f2ae05e 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -1704,6 +1704,7 @@ value1 1 ------------ Testing Errorlevel ------------ 9009 +-9999 1 errorlevel just right, good errorlevel with leading zero just right, good diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 3ed98bcf73e..cd8d7bbdde7 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -251,7 +251,7 @@ typedef struct _FOR_CONTEXT { * variables and batch parameters substitution already done. */ extern WCHAR quals[MAXSTRING], param1[MAXSTRING], param2[MAXSTRING]; -extern DWORD errorlevel; +extern int errorlevel; extern BATCH_CONTEXT *context; extern FOR_CONTEXT forloopcontext; extern BOOL delayedsubst; diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 1a5cd8ea326..6d312ec5989 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -38,7 +38,7 @@ extern const WCHAR inbuilt[][10]; extern struct env_stack *pushd_directories;
BATCH_CONTEXT *context = NULL; -DWORD errorlevel; +int errorlevel; WCHAR quals[MAXSTRING], param1[MAXSTRING], param2[MAXSTRING]; BOOL interactive; FOR_CONTEXT forloopcontext; /* The 'for' loop context */ @@ -1377,7 +1377,7 @@ void WCMD_run_program (WCHAR *command, BOOL called) interactive = oldinteractive; return; } else { - + DWORD exit_code; /* thisDir contains the file to be launched, but with what? eg. a.exe will require a.exe to be launched, a.html may be iexplore */ hinst = FindExecutableW (thisDir, NULL, temp); @@ -1411,8 +1411,8 @@ void WCMD_run_program (WCHAR *command, BOOL called) or for console applications */ if (!interactive || (console && !HIWORD(console))) WaitForSingleObject (pe.hProcess, INFINITE); - GetExitCodeProcess (pe.hProcess, &errorlevel); - if (errorlevel == STILL_ACTIVE) errorlevel = 0; + GetExitCodeProcess (pe.hProcess, &exit_code); + errorlevel = (exit_code == STILL_ACTIVE) ? 0 : exit_code;
CloseHandle(pe.hProcess); CloseHandle(pe.hThread);
From: Eric Pouech epouech@codeweavers.com
Introducting CMD_IF_CONDITION to hold IF condition.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/builtins.c | 205 ++++------------------------------ programs/cmd/wcmd.h | 29 +++++ programs/cmd/wcmdmain.c | 236 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 273 insertions(+), 197 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 75e651d2086..6b9842a4103 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -2739,171 +2739,6 @@ void WCMD_popd (void) { LocalFree (temp); }
-/******************************************************************* - * evaluate_if_comparison - * - * Evaluates an "if" comparison operation - * - * PARAMS - * leftOperand [I] left operand, non NULL - * operator [I] "if" binary comparison operator, non NULL - * rightOperand [I] right operand, non NULL - * caseInsensitive [I] 0 for case sensitive comparison, anything else for insensitive - * - * RETURNS - * Success: 1 if operator applied to the operands evaluates to TRUE - * 0 if operator applied to the operands evaluates to FALSE - * Failure: -1 if operator is not recognized - */ -static int evaluate_if_comparison(const WCHAR *leftOperand, const WCHAR *operator, - const WCHAR *rightOperand, int caseInsensitive) -{ - WCHAR *endptr_leftOp, *endptr_rightOp; - long int leftOperand_int, rightOperand_int; - BOOL int_operands; - - /* == is a special case, as it always compares strings */ - if (!lstrcmpiW(operator, L"==")) - return caseInsensitive ? lstrcmpiW(leftOperand, rightOperand) == 0 - : lstrcmpW (leftOperand, rightOperand) == 0; - - /* Check if we have plain integers (in decimal, octal or hexadecimal notation) */ - leftOperand_int = wcstol(leftOperand, &endptr_leftOp, 0); - rightOperand_int = wcstol(rightOperand, &endptr_rightOp, 0); - int_operands = (!*endptr_leftOp) && (!*endptr_rightOp); - - /* Perform actual (integer or string) comparison */ - if (!lstrcmpiW(operator, L"lss")) { - if (int_operands) - return leftOperand_int < rightOperand_int; - else - return caseInsensitive ? lstrcmpiW(leftOperand, rightOperand) < 0 - : lstrcmpW (leftOperand, rightOperand) < 0; - } - - if (!lstrcmpiW(operator, L"leq")) { - if (int_operands) - return leftOperand_int <= rightOperand_int; - else - return caseInsensitive ? lstrcmpiW(leftOperand, rightOperand) <= 0 - : lstrcmpW (leftOperand, rightOperand) <= 0; - } - - if (!lstrcmpiW(operator, L"equ")) { - if (int_operands) - return leftOperand_int == rightOperand_int; - else - return caseInsensitive ? lstrcmpiW(leftOperand, rightOperand) == 0 - : lstrcmpW (leftOperand, rightOperand) == 0; - } - - if (!lstrcmpiW(operator, L"neq")) { - if (int_operands) - return leftOperand_int != rightOperand_int; - else - return caseInsensitive ? lstrcmpiW(leftOperand, rightOperand) != 0 - : lstrcmpW (leftOperand, rightOperand) != 0; - } - - if (!lstrcmpiW(operator, L"geq")) { - if (int_operands) - return leftOperand_int >= rightOperand_int; - else - return caseInsensitive ? lstrcmpiW(leftOperand, rightOperand) >= 0 - : lstrcmpW (leftOperand, rightOperand) >= 0; - } - - if (!lstrcmpiW(operator, L"gtr")) { - if (int_operands) - return leftOperand_int > rightOperand_int; - else - return caseInsensitive ? lstrcmpiW(leftOperand, rightOperand) > 0 - : lstrcmpW (leftOperand, rightOperand) > 0; - } - - return -1; -} - -int evaluate_if_condition(WCHAR *p, WCHAR **command, int *test, int *negate) -{ - WCHAR condition[MAX_PATH]; - int caseInsensitive = (wcsstr(quals, L"/I") != NULL); - - *negate = !lstrcmpiW(param1,L"not"); - lstrcpyW(condition, (*negate ? param2 : param1)); - WINE_TRACE("Condition: %s\n", wine_dbgstr_w(condition)); - - if (!lstrcmpiW(condition, L"errorlevel")) { - WCHAR *param = WCMD_parameter(p, 1+(*negate), NULL, FALSE, FALSE); - WCHAR *endptr; - long int param_int = wcstol(param, &endptr, 10); - if (*endptr) goto syntax_err; - *test = (errorlevel >= param_int); - WCMD_parameter(p, 2+(*negate), command, FALSE, FALSE); - } - else if (!lstrcmpiW(condition, L"exist")) { - WIN32_FIND_DATAW fd; - HANDLE hff; - WCHAR *param = WCMD_parameter(p, 1+(*negate), NULL, FALSE, FALSE); - int len = lstrlenW(param); - - if (!len) { - *test = FALSE; - } else { - /* FindFirstFile does not like a directory path ending in '' or '/', so append a '.' */ - if (param[len-1] == '\' || param[len-1] == '/') wcscat(param, L"."); - - hff = FindFirstFileW(param, &fd); - *test = (hff != INVALID_HANDLE_VALUE); - if (*test) FindClose(hff); - } - - WCMD_parameter(p, 2+(*negate), command, FALSE, FALSE); - } - else if (!lstrcmpiW(condition, L"defined")) { - *test = (GetEnvironmentVariableW(WCMD_parameter(p, 1+(*negate), NULL, FALSE, FALSE), - NULL, 0) > 0); - WCMD_parameter(p, 2+(*negate), command, FALSE, FALSE); - } - else { /* comparison operation */ - WCHAR leftOperand[MAXSTRING], rightOperand[MAXSTRING], operator[MAXSTRING]; - WCHAR *paramStart; - - lstrcpyW(leftOperand, WCMD_parameter(p, (*negate)+caseInsensitive, ¶mStart, TRUE, FALSE)); - if (!*leftOperand) - goto syntax_err; - - /* Note: '==' can't be returned by WCMD_parameter since '=' is a separator */ - p = paramStart + lstrlenW(leftOperand); - while (*p == ' ' || *p == '\t') - p++; - - if (!wcsncmp(p, L"==", lstrlenW(L"=="))) - lstrcpyW(operator, L"=="); - else { - lstrcpyW(operator, WCMD_parameter(p, 0, ¶mStart, FALSE, FALSE)); - if (!*operator) goto syntax_err; - } - p += lstrlenW(operator); - - lstrcpyW(rightOperand, WCMD_parameter(p, 0, ¶mStart, TRUE, FALSE)); - if (!*rightOperand) - goto syntax_err; - - *test = evaluate_if_comparison(leftOperand, operator, rightOperand, caseInsensitive); - if (*test == -1) - goto syntax_err; - - p = paramStart + lstrlenW(rightOperand); - WCMD_parameter(p, 0, command, FALSE, FALSE); - } - - return 1; - -syntax_err: - return -1; -} - /**************************************************************************** * WCMD_if * @@ -2920,26 +2755,28 @@ syntax_err: */ void WCMD_if (WCHAR *p, CMD_NODE **cmdList) { - int negate; /* Negate condition */ - int test; /* Condition evaluation result */ - WCHAR *command; - - /* Function evaluate_if_condition relies on the global variables quals, param1 and param2 - set in a call to WCMD_parse before */ - if (evaluate_if_condition(p, &command, &test, &negate) == -1) - goto syntax_err; - - WINE_TRACE("p: %s, quals: %s, param1: %s, param2: %s, command: %s\n", - wine_dbgstr_w(p), wine_dbgstr_w(quals), wine_dbgstr_w(param1), - wine_dbgstr_w(param2), wine_dbgstr_w(command)); - - /* Process rest of IF statement which is on the same line - Note: This may process all or some of the cmdList (eg a GOTO) */ - WCMD_part_execute(cmdList, command, TRUE, (test != negate)); - return; + CMD_IF_CONDITION if_cond; + WCHAR *command; + int test; + + if (if_condition_create(p, &command, &if_cond)) + { + TRACE("%s\n", if_condition_as_string(&if_cond)); + if (if_condition_evaluate(&if_cond, &test)) + { + WINE_TRACE("p: %s, quals: %s, param1: %s, param2: %s, command: %s\n", + wine_dbgstr_w(p), wine_dbgstr_w(quals), wine_dbgstr_w(param1), + wine_dbgstr_w(param2), wine_dbgstr_w(command));
-syntax_err: - WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR)); + /* Process rest of IF statement which is on the same line + Note: This may process all or some of the cmdList (eg a GOTO) */ + WCMD_part_execute(cmdList, command, TRUE, test); + } + if_condition_dispose(&if_cond); + return; + } + + WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR)); }
/**************************************************************************** diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index cd8d7bbdde7..7c1c89f6b0d 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -60,6 +60,29 @@ typedef enum _CMD_OPERATOR
/* Data structure to hold commands to be processed */
+enum cond_operator {CMD_IF_ERRORLEVEL, CMD_IF_EXIST, CMD_IF_DEFINED, + CMD_IF_BINOP_EQUAL /* == */, CMD_IF_BINOP_LSS, CMD_IF_BINOP_LEQ, CMD_IF_BINOP_EQU, + CMD_IF_BINOP_NEQ, CMD_IF_BINOP_GEQ, CMD_IF_BINOP_GTR}; +typedef struct _CMD_IF_CONDITION +{ + unsigned case_insensitive : 1, + negated : 1, + op; + union + { + /* CMD_IF_ERRORLEVEL */ + int level; + /* CMD_IF_EXIST, CMD_IF_DEFINED */ + const WCHAR *operand; + /* CMD_BINOP_EQUAL, CMD_BINOP_LSS, CMD_BINOP_LEQ, CMD_BINOP_EQU, CMD_BINOP_NEQ, CMD_BINOP_GEQ, CMD_BINOP_GTR */ + struct + { + const WCHAR *left; + const WCHAR *right; + }; + }; +} CMD_IF_CONDITION; + typedef struct _CMD_COMMAND { WCHAR *command; /* Command string to execute */ @@ -100,6 +123,12 @@ static inline int CMD_node_get_depth(const CMD_NODE *node) } /* end temporary */
+/* temporary helpers for parsing transition */ +BOOL if_condition_create(WCHAR *start, WCHAR **end, CMD_IF_CONDITION *cond); +void if_condition_dispose(CMD_IF_CONDITION *); +BOOL if_condition_evaluate(CMD_IF_CONDITION *cond, int *test); +const char *if_condition_as_string(const CMD_IF_CONDITION *cond); + 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 6d312ec5989..41e4df99b40 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -31,6 +31,7 @@ #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(cmd); +WINE_DECLARE_DEBUG_CHANNEL(cmd_if); WINE_DECLARE_DEBUG_CHANNEL(cmd_lexer); WINE_DECLARE_DEBUG_CHANNEL(cmd_redir);
@@ -1055,7 +1056,152 @@ static CMD_NODE *node_create_binary(CMD_OPERATOR op, CMD_NODE *l, CMD_NODE *r) return new; }
-static const char *op2str(CMD_OPERATOR op) +void if_condition_dispose(CMD_IF_CONDITION *cond) +{ + switch (cond->op) + { + case CMD_IF_ERRORLEVEL: + break; + case CMD_IF_EXIST: + case CMD_IF_DEFINED: + free((void*)cond->operand); + break; + case CMD_IF_BINOP_EQUAL: + case CMD_IF_BINOP_LSS: + case CMD_IF_BINOP_LEQ: + case CMD_IF_BINOP_EQU: + case CMD_IF_BINOP_NEQ: + case CMD_IF_BINOP_GEQ: + case CMD_IF_BINOP_GTR: + free((void*)cond->left); + free((void*)cond->right); + break; + } +} + +BOOL if_condition_create(WCHAR *start, WCHAR **end, CMD_IF_CONDITION *cond) +{ + WCHAR *param_start; + const WCHAR *param_copy; + int narg = 0; + + if (cond) memset(cond, 0, sizeof(*cond)); + param_copy = WCMD_parameter(start, narg++, ¶m_start, TRUE, FALSE); + /* /I is the only option supported */ + if (!wcsicmp(param_copy, L"/I")) + { + param_copy = WCMD_parameter(start, narg++, ¶m_start, TRUE, FALSE); + if (cond) cond->case_insensitive = 1; + } + if (!wcsicmp(param_copy, L"NOT")) + { + param_copy = WCMD_parameter(start, narg++, ¶m_start, TRUE, FALSE); + if (cond) cond->negated = 1; + } + if (!wcsicmp(param_copy, L"errorlevel")) + { + WCHAR *endptr; + long int level; + param_copy = WCMD_parameter(start, narg++, ¶m_start, TRUE, FALSE); + if (cond) cond->op = CMD_IF_ERRORLEVEL; + level = wcstol(param_copy, &endptr, 10); + if (*endptr) return FALSE; + if (cond) cond->level = level; + } + else if (!wcsicmp(param_copy, L"exist")) + { + param_copy = WCMD_parameter(start, narg++, ¶m_start, FALSE, FALSE); + if (cond) cond->op = CMD_IF_EXIST; + if (cond) cond->operand = wcsdup(param_copy); + } + else if (!wcsicmp(param_copy, L"defined")) + { + param_copy = WCMD_parameter(start, narg++, ¶m_start, TRUE, FALSE); + if (cond) cond->op = CMD_IF_DEFINED; + if (cond) cond->operand = wcsdup(param_copy); + } + else /* comparison operation */ + { + if (*param_copy == L'\0') return FALSE; + param_copy = WCMD_parameter(start, narg - 1, ¶m_start, TRUE, FALSE); + if (cond) cond->left = wcsdup(param_copy); + + start = WCMD_skip_leading_spaces(param_start + wcslen(param_copy)); + + /* Note: '==' can't be returned by WCMD_parameter since '=' is a separator */ + if (start[0] == L'=' && start[1] == L'=') + { + start += 2; /* == */ + if (cond) cond->op = CMD_IF_BINOP_EQUAL; + } + else + { + static struct + { + const WCHAR *name; + enum cond_operator binop; + } + allowed_operators[] = {{L"lss", CMD_IF_BINOP_LSS}, + {L"leq", CMD_IF_BINOP_LEQ}, + {L"equ", CMD_IF_BINOP_EQU}, + {L"neq", CMD_IF_BINOP_NEQ}, + {L"geq", CMD_IF_BINOP_GEQ}, + {L"gtr", CMD_IF_BINOP_GTR}, + }; + int i; + + param_copy = WCMD_parameter(start, 0, ¶m_start, FALSE, FALSE); + for (i = 0; i < ARRAY_SIZE(allowed_operators); i++) + if (!wcsicmp(param_copy, allowed_operators[i].name)) break; + if (i == ARRAY_SIZE(allowed_operators)) + { + if (cond) free((void*)cond->left); + return FALSE; + } + if (cond) cond->op = allowed_operators[i].binop; + start += wcslen(param_copy); + } + + param_copy = WCMD_parameter(start, 0, ¶m_start, TRUE, FALSE); + if (*param_copy == L'\0') + { + if (cond) free((void*)cond->left); + return FALSE; + } + if (cond) cond->right = wcsdup(param_copy); + + start = param_start + wcslen(param_copy); + narg = 0; + } + /* check all remaning args are present, and compute pointer to end of condition */ + param_copy = WCMD_parameter(start, narg, end, TRUE, FALSE); + return cond || *param_copy != L'\0'; +} + +const char *if_condition_as_string(const CMD_IF_CONDITION *cond) +{ + const char *header = wine_dbg_sprintf("{{%s%s", cond->negated ? "not " : "", cond->case_insensitive ? "nocase " : ""); + + switch (cond->op) + { + case CMD_IF_ERRORLEVEL: return wine_dbg_sprintf("%serrorlevel %d}}", header, cond->level); + case CMD_IF_EXIST: return wine_dbg_sprintf("%sexist %ls}}", header, cond->operand); + case CMD_IF_DEFINED: return wine_dbg_sprintf("%sdefined %ls}}", header, cond->operand); + case CMD_IF_BINOP_EQUAL: return wine_dbg_sprintf("%s%ls == %ls}}", header, cond->left, cond->right); + + case CMD_IF_BINOP_LSS: return wine_dbg_sprintf("%s%ls LSS %ls}}", header, cond->left, cond->right); + case CMD_IF_BINOP_LEQ: return wine_dbg_sprintf("%s%ls LEQ %ls}}", header, cond->left, cond->right); + case CMD_IF_BINOP_EQU: return wine_dbg_sprintf("%s%ls EQU %ls}}", header, cond->left, cond->right); + case CMD_IF_BINOP_NEQ: return wine_dbg_sprintf("%s%ls NEQ %ls}}", header, cond->left, cond->right); + case CMD_IF_BINOP_GEQ: return wine_dbg_sprintf("%s%ls GEQ %ls}}", header, cond->left, cond->right); + case CMD_IF_BINOP_GTR: return wine_dbg_sprintf("%s%ls GTR %ls}}", header, cond->left, cond->right); + default: + FIXME("Unexpected condition operator %u\n", cond->op); + return "{{}}"; + } +} + +static const char *op_as_string(CMD_OPERATOR op) { static const char* optable[] = {"op-single", "op-&", "op-||", "op-&&", "op-|"}; if (op < ARRAY_SIZE(optable)) return optable[op]; @@ -1093,7 +1239,7 @@ static void dump_cmd_node_tree(CMD_NODE *node) case CMD_ONSUCCESS: case CMD_PIPE: dump_cmd_node_tree(node->left); - TRACE(" %s ", op2str(node->op)); + TRACE(" %s ", op_as_string(node->op)); dump_cmd_node_tree(node->right); break; default: @@ -2176,20 +2322,12 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE To be able to handle ('s in the condition part take as much as evaluate_if_condition would take and skip parsing it here. */ } else if (WCMD_keyword_ws_found(L"if", curPos)) { - int negate; /* Negate condition */ - int test; /* Condition evaluation result */ WCHAR *p, *command;
inIf = TRUE;
- p = curPos+(lstrlenW(L"if")); - while (*p == ' ' || *p == '\t') - p++; - WCMD_parse (p, quals, param1, param2); - - /* Function evaluate_if_condition relies on the global variables quals, param1 and param2 - set in a call to WCMD_parse before */ - if (evaluate_if_condition(p, &command, &test, &negate) != -1) + p = WCMD_skip_leading_spaces(curPos + 2); /* "if" */ + if (if_condition_create(p, &command, NULL)) { int if_condition_len = command - curPos; WINE_TRACE("p: %s, quals: %s, param1: %s, param2: %s, command: %s, if_condition_len: %d\n", @@ -2199,7 +2337,6 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE (*curLen)+=if_condition_len; curPos+=if_condition_len; } - if (WCMD_keyword_ws_found(L"set", curPos)) ignoreBracket = TRUE;
@@ -2590,6 +2727,79 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE return extraSpace; }
+BOOL if_condition_evaluate(CMD_IF_CONDITION *cond, int *test) +{ + int (WINAPI *cmp)(const WCHAR*, const WCHAR*) = cond->case_insensitive ? lstrcmpiW : lstrcmpW; + + *test = 0; + switch (cond->op) + { + case CMD_IF_ERRORLEVEL: + *test = errorlevel >= cond->level; + break; + case CMD_IF_EXIST: + { + WIN32_FIND_DATAW fd; + HANDLE hff; + size_t len = wcslen(cond->operand); + + if (len) + { + /* FindFirstFile does not like a directory path ending in '' or '/', so append a '.' */ + if (cond->operand[len - 1] == '\' || cond->operand[len - 1] == '/') + { + WCHAR *new = xrealloc((void*)cond->operand, (wcslen(cond->operand) + 2) * sizeof(WCHAR)); + wcscat(new, L"."); + cond->operand = new; + } + TRACE_(cmd_if)("testing on (%ls)\n", cond->operand); + hff = FindFirstFileW(cond->operand, &fd); + *test = (hff != INVALID_HANDLE_VALUE); + if (*test) FindClose(hff); + } + } + break; + case CMD_IF_DEFINED: + *test = GetEnvironmentVariableW(cond->operand, NULL, 0) > 0; + TRACE_(cmd_if)("testing2 on (%ls) => %u\n", cond->operand, *test); + break; + case CMD_IF_BINOP_EQUAL: + /* == is a special case, as it always compares strings */ + *test = (*cmp)(cond->left, cond->right) == 0; + break; + default: + { + long int left_int, right_int; + WCHAR *end_left, *end_right; + long int cmp_val; + + /* Check if we have plain integers (in decimal, octal or hexadecimal notation) */ + left_int = wcstol(cond->left, &end_left, 0); + right_int = wcstol(cond->right, &end_right, 0); + if (!*end_left && !*end_right) + cmp_val = left_int - right_int; + else + cmp_val = (*cmp)(cond->left, cond->right); + TRACE_(cmd_if)("(%ls) %u (%ls) %ld\n", cond->left, cond->op, cond->right, cmp_val); + switch (cond->op) + { + case CMD_IF_BINOP_LSS: *test = cmp_val < 0; break; + case CMD_IF_BINOP_LEQ: *test = cmp_val <= 0; break; + case CMD_IF_BINOP_EQU: *test = cmp_val == 0; break; + case CMD_IF_BINOP_NEQ: *test = cmp_val != 0; break; + case CMD_IF_BINOP_GEQ: *test = cmp_val >= 0; break; + case CMD_IF_BINOP_GTR: *test = cmp_val > 0; break; + default: + FIXME("Unsupported op %u\n", cond->op); + return FALSE; + } + } + break; + } + if (cond->negated) *test ^= 1; + return TRUE; +} + /*************************************************************************** * WCMD_process_commands *
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=145759
Your paranoid android.
=== debian11b (64 bit WoW report) ===
winmm: mci: Timeout
=== debian11b (build log) ===
009c:err:winediag:gnutls_process_attach failed to load libgnutls, no support for encryption
V3 pushed: - also convert global variable errorlevel to signed int