This is part VIII of cmd engine rewrite.
It covers: - introduction of a token based parser. For now, it produces the same (degenerated) list of commands as we currently do. Note: I opted for a pull mode for the parser while the lexer is in push mode. Long term plan is to move the lexer into pull mode too (and also converge the two different current methods of argument/token access). - introduction of return codes. It's not obvious from current tests that lots of these codes are actually the kernel32 ones, but tests in later MRs will clearly show it. - starting changing 'exit' and 'goto' builtin implementations to use return code. All builtins will have to adopt that scheme for command chaining to be properly supported. - add delayed expansion for IF command. That's not needed for now (as single command execution already takes care of it), but will be needed when we move IF and FOR commands out of single command in yet to come MR. Includes some test to cover that part. (in case someone asks, it was needed in previous MR regarding the FOR command, as the delayed expansion was only called on the body of the FOR command, not the control block)
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/cmd.rc | 1 + programs/cmd/wcmd.h | 1 + programs/cmd/wcmdmain.c | 295 ++++++++++++++++++++++++++++++++-------- 3 files changed, 239 insertions(+), 58 deletions(-)
diff --git a/programs/cmd/cmd.rc b/programs/cmd/cmd.rc index f72ba87a9a2..59cb570b167 100644 --- a/programs/cmd/cmd.rc +++ b/programs/cmd/cmd.rc @@ -407,4 +407,5 @@ Enter HELP <command> for further information on any of the above commands.\n" WCMD_BADPAREN, "Mismatch in parentheses.\n" WCMD_BADHEXOCT, "Badly formed number - must be one of decimal (12),\n hexadecimal (0x34) or octal (056).\n" WCMD_FILENAMETOOLONG, "File name is too long.\n" + WCMD_BADTOKEN, "Syntax error: unexpected %1\n" } diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index fd2b541c830..dcf7c7a1492 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -469,3 +469,4 @@ extern WCHAR version_string[]; #define WCMD_BADPAREN 1044 #define WCMD_BADHEXOCT 1045 #define WCMD_FILENAMETOOLONG 1046 +#define WCMD_BADTOKEN 1047 diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 22bfb1853da..24eff3cc68c 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -2125,23 +2125,6 @@ static CMD_COMMAND *WCMD_createCommand(WCHAR *command, int *commandLen, return thisEntry; }
-static void WCMD_appendCommand(CMD_OPERATOR op, CMD_COMMAND *command, CMD_NODE **node) -{ - /* append as left to right operators */ - if (*node) - { - CMD_NODE **last = node; - while ((*last)->op != CMD_SINGLE) - last = &(*last)->right; - - *last = node_create_binary(op, *last, node_create_single(command)); - } - else - { - *node = node_create_single(command); - } -} - /*************************************************************************** * WCMD_IsEndQuote * @@ -2377,6 +2360,190 @@ syntax_error: return NULL; }
+/* used to store additional information dedicated a given token */ +union token_parameter +{ + CMD_COMMAND *command; + void *none; +}; + +struct node_builder +{ + unsigned num; + unsigned allocated; + struct token + { + enum builder_token + { + TKN_EOL, TKN_AMP, TKN_BARBAR, TKN_AMPAMP, TKN_BAR, TKN_COMMAND, + } token; + union token_parameter parameter; + } *stack; + unsigned pos; +}; + +static const char* debugstr_token(enum builder_token tkn, union token_parameter tkn_pmt) +{ + static const char *tokens[] = {"EOL", "&", "||", "&&", "|", "CMD"}; + + if (tkn >= ARRAY_SIZE(tokens)) return "<<<>>>"; + switch (tkn) + { + case TKN_COMMAND: return wine_dbg_sprintf("%s {{%ls}}", tokens[tkn], tkn_pmt.command->command); + default: return wine_dbg_sprintf("%s", tokens[tkn]); + } +} + +static void node_builder_init(struct node_builder *builder) +{ + memset(builder, 0, sizeof(*builder)); +} + +static void node_builder_dispose(struct node_builder *builder) +{ + free(builder->stack); +} + +static void node_builder_push_token_parameter(struct node_builder *builder, enum builder_token tkn, union token_parameter pmt) +{ + if (builder->allocated <= builder->num) + { + unsigned sz = builder->allocated ? 2 * builder->allocated : 64; + builder->stack = xrealloc(builder->stack, sz * sizeof(builder->stack[0])); + builder->allocated = sz; + } + builder->stack[builder->num].token = tkn; + builder->stack[builder->num].parameter = pmt; + builder->num++; +} + +static void node_builder_push_token(struct node_builder *builder, enum builder_token tkn) +{ + union token_parameter pmt = {.none = NULL}; + node_builder_push_token_parameter(builder, tkn, pmt); +} + +static enum builder_token node_builder_peek_next_token(struct node_builder *builder, union token_parameter *pmt) +{ + enum builder_token tkn; + + if (builder->pos >= builder->num) + { + tkn = TKN_EOL; + if (pmt) pmt->none = NULL; + } + else + { + tkn = builder->stack[builder->pos].token; + if (pmt) + *pmt = builder->stack[builder->pos].parameter; + } + return tkn; +} + +static void node_builder_consume(struct node_builder *builder) +{ + builder->stack[builder->pos].parameter.none = NULL; + builder->pos++; +} + +static void WCMD_appendCommand(CMD_OPERATOR op, CMD_COMMAND *command, CMD_NODE **node) +{ + /* append as left to right operators */ + if (*node) + { + CMD_NODE **last = node; + while ((*last)->op != CMD_SINGLE) + last = &(*last)->right; + + *last = node_create_binary(op, *last, node_create_single(command)); + } + else + { + *node = node_create_single(command); + } +} + +static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) +{ + unsigned bogus_line; + CMD_NODE *left = NULL; + union token_parameter pmt; + enum builder_token tkn; + BOOL done; + +#define ERROR_IF(x) if (x) {bogus_line = __LINE__; goto error_handling;} + + for (;;) + { + CMD_OPERATOR cmd_op; + + done = FALSE; + /* we always get a pair of OP CMMAND */ + tkn = node_builder_peek_next_token(builder, &pmt); + switch (tkn) + { + case TKN_EOL: done = TRUE; break; + case TKN_AMP: cmd_op = CMD_CONCAT; break; + case TKN_AMPAMP: cmd_op = CMD_ONSUCCESS; break; + case TKN_BAR: cmd_op = CMD_PIPE; break; + case TKN_BARBAR: cmd_op = CMD_ONFAILURE; break; + case TKN_COMMAND: ERROR_IF(TRUE); + default: ERROR_IF(TRUE); + } + if (done) break; + node_builder_consume(builder); + + tkn = node_builder_peek_next_token(builder, &pmt); + ERROR_IF(tkn != TKN_COMMAND) + node_builder_consume(builder); + + WCMD_appendCommand(cmd_op, pmt.command, &left); + } while (!done); +#undef ERROR_IF + *result = left; + return TRUE; +error_handling: + TRACE("Parser failed at line %u:token %s\n", bogus_line, debugstr_token(tkn, pmt)); + node_dispose_tree(left); + + return FALSE; +} + +static BOOL node_builder_generate(struct node_builder *builder, CMD_NODE **node) +{ + union token_parameter tkn_pmt; + enum builder_token tkn; + + if (node_builder_parse(builder, node) && + builder->pos + 1 >= builder->num) /* consumed all tokens? */ + return TRUE; + /* print error on first unused token */ + if (builder->pos < builder->num) + { + tkn = node_builder_peek_next_token(builder, &tkn_pmt); + switch (tkn) + { + case TKN_COMMAND: + WCMD_output_stderr(WCMD_LoadMessage(WCMD_BADTOKEN), tkn_pmt.command->command); + break; + default: + WCMD_output_stderr(WCMD_LoadMessage(WCMD_BADTOKEN), debugstr_token(tkn, tkn_pmt)); + } + } + /* free remaining tokens */ + for (;;) + { + tkn = node_builder_peek_next_token(builder, &tkn_pmt); + if (tkn == TKN_EOL) break; + if (tkn == TKN_COMMAND) command_dispose(tkn_pmt.command); + node_builder_consume(builder); + } + + *node = NULL; + return FALSE; +} + /*************************************************************************** * WCMD_ReadAndParseLine * @@ -2402,8 +2569,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE WCHAR *curCopyTo; int *curLen; int curDepth = 0; - CMD_COMMAND *single_cmd = NULL; - CMD_OPERATOR cmd_op = CMD_CONCAT; + union token_parameter tkn_pmt; + enum builder_token cmd_tkn = TKN_AMP; static WCHAR *extraSpace = NULL; /* Deliberately never freed */ BOOL inOneLine = FALSE; BOOL inFor = FALSE; @@ -2421,6 +2588,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE /* handling brackets as a normal character */ int lineCurDepth; /* Bracket depth when line was read in */ BOOL resetAtEndOfLine = FALSE; /* Do we need to reset curdepth at EOL */ + struct node_builder builder; + BOOL ret;
*output = NULL; /* Allocate working space for a command read from keyboard, file etc */ @@ -2445,6 +2614,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE curPos = extraSpace; TRACE("About to parse line (%ls)\n", extraSpace);
+ node_builder_init(&builder); + /* Handle truncated input - issue warning */ if (lstrlenW(extraSpace) == MAXSTRING -1) { WCMD_output_asis_stderr(WCMD_LoadMessage(WCMD_TRUNCATEDLINE)); @@ -2662,19 +2833,20 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE /* Add an entry to the command list */ if (curStringLen > 0) {
+ node_builder_push_token(&builder, cmd_tkn); /* Add the current command */ - single_cmd = WCMD_createCommand(curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen, - curDepth); - WCMD_appendCommand(cmd_op, single_cmd, output); + tkn_pmt.command = WCMD_createCommand(curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen, + curDepth); + node_builder_push_token_parameter(&builder, TKN_COMMAND, tkn_pmt); }
if (*(curPos+1) == '|') { curPos++; /* Skip other | */ - cmd_op = CMD_ONFAILURE; + cmd_tkn = TKN_BARBAR; } else { - cmd_op = CMD_PIPE; + cmd_tkn = TKN_BAR; }
/* If in an IF or ELSE statement, put subsequent chained @@ -2735,12 +2907,13 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE inIn = TRUE; }
+ node_builder_push_token(&builder, cmd_tkn); /* Add the current command */ - single_cmd = WCMD_createCommand(curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen, - curDepth); - WCMD_appendCommand(cmd_op, single_cmd, output); + tkn_pmt.command = WCMD_createCommand(curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen, + curDepth); + node_builder_push_token_parameter(&builder, TKN_COMMAND, tkn_pmt);
curDepth++; } else { @@ -2766,19 +2939,20 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE /* Add an entry to the command list */ if (curStringLen > 0) {
+ node_builder_push_token(&builder, cmd_tkn); /* Add the current command */ - single_cmd = WCMD_createCommand(curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen, - curDepth); - WCMD_appendCommand(cmd_op, single_cmd, output); + tkn_pmt.command = WCMD_createCommand(curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen, + curDepth); + node_builder_push_token_parameter(&builder, TKN_COMMAND, tkn_pmt); }
if (*(curPos+1) == '&') { curPos++; /* Skip other & */ - cmd_op = CMD_ONSUCCESS; + cmd_tkn = TKN_AMPAMP; } else { - cmd_op = CMD_CONCAT; + cmd_tkn = TKN_AMP; } /* If in an IF or ELSE statement, put subsequent chained commands at a higher depth as if brackets were supplied @@ -2798,21 +2972,23 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE /* Add the current command if there is one */ if (curStringLen) {
+ node_builder_push_token(&builder, cmd_tkn); /* Add the current command */ - single_cmd = WCMD_createCommand(curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen, - curDepth); - WCMD_appendCommand(cmd_op, single_cmd, output); + tkn_pmt.command = WCMD_createCommand(curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen, + curDepth); + node_builder_push_token_parameter(&builder, TKN_COMMAND, tkn_pmt); }
/* Add an empty entry to the command list */ - cmd_op = CMD_CONCAT; - single_cmd = WCMD_createCommand(NULL, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen, - curDepth); - WCMD_appendCommand(cmd_op, single_cmd, output); + cmd_tkn = TKN_AMP; + node_builder_push_token(&builder, cmd_tkn); + tkn_pmt.command = WCMD_createCommand(NULL, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen, + curDepth); + node_builder_push_token_parameter(&builder, TKN_COMMAND, tkn_pmt);
curDepth--;
@@ -2844,12 +3020,13 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE Do not add command to list if escape char ^ was last */ if (*curPos == 0x00 && !lastWasCaret && *curLen > 0) {
+ node_builder_push_token(&builder, cmd_tkn); /* Add an entry to the command list */ - single_cmd = WCMD_createCommand(curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen, - curDepth); - WCMD_appendCommand(cmd_op, single_cmd, output); + tkn_pmt.command = WCMD_createCommand(curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen, + curDepth); + node_builder_push_token_parameter(&builder, TKN_COMMAND, tkn_pmt);
/* If we had a single line if or else, and we pretended to add brackets, end them now */ @@ -2869,7 +3046,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE WINE_TRACE("Need to read more data as outstanding brackets or carets\n"); inOneLine = FALSE; ignoreBracket = FALSE; - cmd_op = CMD_CONCAT; + cmd_tkn = TKN_AMP; inQuotes = 0; memset(extraSpace, 0x00, (MAXSTRING+1) * sizeof(WCHAR)); extraData = extraSpace; @@ -2913,10 +3090,12 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE } }
- if (curDepth > lineCurDepth) { + *output = NULL; + ret = curDepth <= lineCurDepth && node_builder_generate(&builder, output); + node_builder_dispose(&builder); + if (!ret) + { WINE_TRACE("Brackets do not match, error out without executing.\n"); - node_dispose_tree(*output); - *output = NULL; errorlevel = 255; }
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/batch.c | 2 +- programs/cmd/builtins.c | 70 ++++++++++++++++++++-------------------- programs/cmd/directory.c | 18 +++++------ programs/cmd/wcmd.h | 8 +++++ programs/cmd/wcmdmain.c | 12 +++---- 5 files changed, 59 insertions(+), 51 deletions(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c index d85cbf810aa..15e8baa21c8 100644 --- a/programs/cmd/batch.c +++ b/programs/cmd/batch.c @@ -654,7 +654,7 @@ void WCMD_call (WCHAR *command) { if (*command != ':') { WCMD_run_program(command, TRUE); /* If the thing we try to run does not exist, call returns 1 */ - if (errorlevel) errorlevel=1; + if (errorlevel) errorlevel = ERROR_INVALID_FUNCTION; } else {
WCHAR gotoLabel[MAX_PATH]; diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 04c879bc18b..9033330e6d1 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -303,7 +303,7 @@ void WCMD_choice (const WCHAR * args) { BOOL opt_s = FALSE;
have_console = GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &oldmode); - errorlevel = 0; + errorlevel = NO_ERROR;
my_command = xstrdupW(WCMD_skip_leading_spaces((WCHAR*)args));
@@ -422,7 +422,7 @@ void WCMD_choice (const WCHAR * args) { if (!WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, 1, &count)) { free(my_command); - errorlevel = 0; + errorlevel = NO_ERROR; return; }
@@ -641,12 +641,12 @@ void WCMD_copy(WCHAR * args) { COPY_FILES *prevcopy = NULL;
/* Assume we were successful! */ - errorlevel = 0; + errorlevel = NO_ERROR;
/* If no args supplied at all, report an error */ if (param1[0] == 0x00) { WCMD_output_stderr (WCMD_LoadMessage(WCMD_NOARG)); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; return; }
@@ -726,7 +726,7 @@ void WCMD_copy(WCHAR * args) { if (*thisparam=='+') { if (lastcopyentry == NULL) { WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR)); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; goto exitreturn; } else { concatnextfilename = TRUE; @@ -781,7 +781,7 @@ void WCMD_copy(WCHAR * args) { } else { /* We have processed sources and destinations and still found more to do - invalid */ WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR)); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; goto exitreturn; } concatnextfilename = FALSE; @@ -798,7 +798,7 @@ void WCMD_copy(WCHAR * args) { /* Ensure we have at least one source file */ if (!sourcelist) { WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR)); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; goto exitreturn; }
@@ -1046,7 +1046,7 @@ void WCMD_copy(WCHAR * args) { } if (!status) { WCMD_print_error (); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; } else { WINE_TRACE("Copied successfully\n"); if (anyconcats) writtenoneconcat = TRUE; @@ -1058,7 +1058,7 @@ void WCMD_copy(WCHAR * args) { if (!destination->binarycopy && !anyconcats && !thiscopy->binarycopy) { if (!WCMD_AppendEOF(outname)) { WCMD_print_error (); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; } } } @@ -1070,7 +1070,7 @@ void WCMD_copy(WCHAR * args) { /* Error if the first file was not found */ if (!anyconcats || !writtenoneconcat) { WCMD_print_error (); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; } }
@@ -1082,7 +1082,7 @@ void WCMD_copy(WCHAR * args) { if (!errorlevel && !destination->binarycopy && anyconcats && writtenoneconcat) { if (!WCMD_AppendEOF(destination->name)) { WCMD_print_error (); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; } }
@@ -1165,7 +1165,7 @@ void WCMD_create_dir (WCHAR *args) { if (!argN) break; if (!create_full_path(thisArg)) { WCMD_print_error (); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; } } } @@ -1444,7 +1444,7 @@ BOOL WCMD_delete (WCHAR *args) { BOOL argsProcessed = FALSE; BOOL foundAny = FALSE;
- errorlevel = 0; + errorlevel = NO_ERROR;
for (argno=0; ; argno++) { BOOL found; @@ -2053,7 +2053,7 @@ void WCMD_pushd (const WCHAR *args) /* Change directory using CD code with /D parameter */ lstrcpyW(quals, L"/D"); GetCurrentDirectoryW (1024, thisdir); - errorlevel = 0; + errorlevel = NO_ERROR; WCMD_setshow_default(args); if (errorlevel) { LocalFree(curdir); @@ -2253,7 +2253,7 @@ void WCMD_move (void)
if (!status) { WCMD_print_error (); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; } } while (FindNextFileW(hff, &fd) != 0);
@@ -2368,12 +2368,12 @@ void WCMD_rename (void) WCHAR fname[MAX_PATH]; WCHAR ext[MAX_PATH];
- errorlevel = 0; + errorlevel = NO_ERROR;
/* Must be at least two args */ if (param1[0] == 0x00 || param2[0] == 0x00) { WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOARG)); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; return; }
@@ -2381,7 +2381,7 @@ void WCMD_rename (void) if ((wcschr(param2,':') != NULL) || (wcschr(param2,'\') != NULL)) { SetLastError(ERROR_INVALID_PARAMETER); WCMD_print_error(); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; return; }
@@ -2444,7 +2444,7 @@ void WCMD_rename (void)
if (!status) { WCMD_print_error (); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; } } while (FindNextFileW(hff, &fd) != 0);
@@ -2683,7 +2683,7 @@ void WCMD_setshow_default (const WCHAR *args) {
status = SetCurrentDirectoryW(string); if (!status) { - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; WCMD_print_error (); return; } else { @@ -3432,7 +3432,7 @@ void WCMD_setshow_env (WCHAR *s) { env = GetEnvironmentStringsW(); if (WCMD_setshow_sortenv( env, s ) == 0) { WCMD_output_stderr(WCMD_LoadMessage(WCMD_MISSINGENV), s); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; } return; } @@ -3444,9 +3444,9 @@ void WCMD_setshow_env (WCHAR *s) { status = SetEnvironmentVariableW(s, p); gle = GetLastError(); if ((!status) & (gle == ERROR_ENVVAR_NOT_FOUND)) { - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; } else if (!status) WCMD_print_error(); - else if (!interactive) errorlevel = 0; + else if (!interactive) errorlevel = NO_ERROR; } }
@@ -3675,7 +3675,7 @@ void WCMD_start(WCHAR *args) DWORD exit_code; WaitForSingleObject( pi.hProcess, INFINITE ); GetExitCodeProcess( pi.hProcess, &exit_code ); - errorlevel = (exit_code == STILL_ACTIVE) ? 0 : exit_code; + errorlevel = (exit_code == STILL_ACTIVE) ? NO_ERROR : exit_code; CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } @@ -3683,7 +3683,7 @@ void WCMD_start(WCHAR *args) { SetLastError(ERROR_FILE_NOT_FOUND); WCMD_print_error (); - errorlevel = 9009; + errorlevel = RETURN_CODE_CANT_LAUNCH; } free(cmdline); } @@ -3717,7 +3717,7 @@ void WCMD_type (WCHAR *args) { if (param2[0] != 0x00) writeHeaders = TRUE;
/* Loop through all args */ - errorlevel = 0; + errorlevel = NO_ERROR; while (argN) { WCHAR *thisArg = WCMD_parameter (args, argno++, &argN, FALSE, FALSE);
@@ -3733,7 +3733,7 @@ void WCMD_type (WCHAR *args) { if (h == INVALID_HANDLE_VALUE) { WCMD_print_error (); WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), thisArg); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; } else { if (writeHeaders) { WCMD_output_stderr(L"\n%1\n\n\n", thisArg); @@ -3764,7 +3764,7 @@ void WCMD_more (WCHAR *args) { DWORD count;
/* Prefix the NLS more with '-- ', then load the text */ - errorlevel = 0; + errorlevel = NO_ERROR; lstrcpyW(moreStr, L"-- "); LoadStringW(hinst, WCMD_MORESTR, &moreStr[3], ARRAY_SIZE(moreStr)-3);
@@ -3828,7 +3828,7 @@ void WCMD_more (WCHAR *args) { if (h == INVALID_HANDLE_VALUE) { WCMD_print_error (); WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), thisArg); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; } else { ULONG64 curPos = 0; ULONG64 fileLen = 0; @@ -3996,7 +3996,7 @@ void WCMD_assoc (const WCHAR *args, BOOL assoc) { HKEY readKey;
/* See if parameter includes '=' */ - errorlevel = 0; + errorlevel = NO_ERROR; newValue = wcschr(args, '='); if (newValue) accessOptions |= KEY_WRITE;
@@ -4084,7 +4084,7 @@ void WCMD_assoc (const WCHAR *args, BOOL assoc) { LoadStringW(hinst, WCMD_NOFTYPE, msgbuffer, ARRAY_SIZE(msgbuffer)); } WCMD_output_stderr(msgbuffer, keyValue); - errorlevel = 2; + errorlevel = ERROR_FILE_NOT_FOUND; }
/* Not a query - it's a set or clear of a value */ @@ -4109,7 +4109,7 @@ void WCMD_assoc (const WCHAR *args, BOOL assoc) {
} else if (assoc && rc != ERROR_FILE_NOT_FOUND) { WCMD_print_error(); - errorlevel = 2; + errorlevel = ERROR_FILE_NOT_FOUND;
} else { WCHAR msgbuffer[MAXSTRING]; @@ -4121,7 +4121,7 @@ void WCMD_assoc (const WCHAR *args, BOOL assoc) { LoadStringW(hinst, WCMD_NOFTYPE, msgbuffer, ARRAY_SIZE(msgbuffer)); } WCMD_output_stderr(msgbuffer, args); - errorlevel = 2; + errorlevel = ERROR_FILE_NOT_FOUND; }
/* It really is a set value = contents */ @@ -4137,7 +4137,7 @@ void WCMD_assoc (const WCHAR *args, BOOL assoc) {
if (rc != ERROR_SUCCESS) { WCMD_print_error(); - errorlevel = 2; + errorlevel = ERROR_FILE_NOT_FOUND; } else { WCMD_output_asis(args); WCMD_output_asis(L"="); @@ -4188,7 +4188,7 @@ void WCMD_color (void) {
/* Fail if fg == bg color */ if (((color & 0xF0) >> 4) == (color & 0x0F)) { - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; return; }
diff --git a/programs/cmd/directory.c b/programs/cmd/directory.c index afec5f6ed75..72542538d5b 100644 --- a/programs/cmd/directory.c +++ b/programs/cmd/directory.c @@ -494,7 +494,7 @@ static DIRECTORY_STACK *WCMD_list_directory (DIRECTORY_STACK *inputparms, int le if ((file_total + dir_total == 0) && (level == 0)) { SetLastError (ERROR_FILE_NOT_FOUND); WCMD_print_error (); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; }
return parms; @@ -513,7 +513,7 @@ static void WCMD_dir_trailer(const WCHAR *path) { WINE_TRACE("Writing trailer for '%s' gave %d(%ld)\n", wine_dbgstr_w(path), status, GetLastError());
- if (errorlevel==0 && !bare) { + if (errorlevel == NO_ERROR && !bare) { if (recurse) { WCMD_output (L"\n Total files listed:\n%1!8d! files%2!25s! bytes\n", file_total, WCMD_filesize64 (byte_total)); WCMD_output (L"%1!8d! directories %2!18s! bytes free\n\n", dir_total, WCMD_filesize64 (freebytes.QuadPart)); @@ -650,7 +650,7 @@ void WCMD_directory (WCHAR *args) WCHAR fname[MAX_PATH]; WCHAR ext[MAX_PATH];
- errorlevel = 0; + errorlevel = NO_ERROR;
/* Prefill quals with (uppercased) DIRCMD env var */ if (GetEnvironmentVariableW(L"DIRCMD", string, ARRAY_SIZE(string))) { @@ -736,7 +736,7 @@ void WCMD_directory (WCHAR *args) } else { SetLastError(ERROR_INVALID_PARAMETER); WCMD_print_error(); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; return; } break; @@ -756,7 +756,7 @@ void WCMD_directory (WCHAR *args) default: SetLastError(ERROR_INVALID_PARAMETER); WCMD_print_error(); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; return; } p++; @@ -787,7 +787,7 @@ void WCMD_directory (WCHAR *args) default: SetLastError(ERROR_INVALID_PARAMETER); WCMD_print_error(); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; return; }
@@ -806,7 +806,7 @@ void WCMD_directory (WCHAR *args) default: SetLastError(ERROR_INVALID_PARAMETER); WCMD_print_error(); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; return; } p = p + 1; @@ -945,7 +945,7 @@ void WCMD_directory (WCHAR *args) status = WCMD_volume (0, drive); trailerReqd = TRUE; if (!status) { - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; goto exit; } } @@ -954,7 +954,7 @@ void WCMD_directory (WCHAR *args) }
/* Clear any errors from previous invocations, and process it */ - errorlevel = 0; + errorlevel = NO_ERROR; prevEntry = thisEntry; thisEntry = WCMD_list_directory (thisEntry, 0); } diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index dcf7c7a1492..d11fcd3768f 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -173,6 +173,14 @@ void WCMD_add_dirstowalk(struct _DIRECTORY_STACK *dirsToWalk); struct _DIRECTORY_STACK *WCMD_dir_stack_create(const WCHAR *dir, const WCHAR *file); struct _DIRECTORY_STACK *WCMD_dir_stack_free(struct _DIRECTORY_STACK *dir);
+/* The return code: + * - some of them are directly mapped to kernel32's errors + * - some others are cmd.exe specific + */ +typedef int RETURN_CODE; +#define RETURN_CODE_SYNTAX_ERROR 255 +#define RETURN_CODE_CANT_LAUNCH 9009 + 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 24eff3cc68c..b3202edd0d4 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1381,7 +1381,7 @@ void WCMD_run_program (WCHAR *command, BOOL called) if (!firstParam) return;
if (!firstParam[0]) { - errorlevel = 0; + errorlevel = NO_ERROR; return; }
@@ -1580,7 +1580,7 @@ void WCMD_run_program (WCHAR *command, BOOL called) if (!interactive || (console && !HIWORD(console))) WaitForSingleObject (pe.hProcess, INFINITE); GetExitCodeProcess (pe.hProcess, &exit_code); - errorlevel = (exit_code == STILL_ACTIVE) ? 0 : exit_code; + errorlevel = (exit_code == STILL_ACTIVE) ? NO_ERROR : exit_code;
CloseHandle(pe.hProcess); CloseHandle(pe.hThread); @@ -1606,7 +1606,7 @@ void WCMD_run_program (WCHAR *command, BOOL called)
/* If a command fails to launch, it sets errorlevel 9009 - which does not seem to have any associated constant definition */ - errorlevel = 9009; + errorlevel = RETURN_CODE_CANT_LAUNCH; return;
} @@ -3096,7 +3096,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE if (!ret) { WINE_TRACE("Brackets do not match, error out without executing.\n"); - errorlevel = 255; + errorlevel = RETURN_CODE_SYNTAX_ERROR; }
return extraSpace; @@ -3385,7 +3385,7 @@ static CMD_NODE *for_control_execute_fileset(CMD_FOR_CONTROL *for_ctrl, CMD_NODE { WCMD_print_error(); WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), args); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; return NULL; /* FOR loop aborts at first failure here */ } body = for_control_execute_from_FILE(for_ctrl, input, cmdList); @@ -3405,7 +3405,7 @@ static CMD_NODE *for_control_execute_fileset(CMD_FOR_CONTROL *for_ctrl, CMD_NODE { WCMD_print_error(); WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), element); - errorlevel = 1; + errorlevel = ERROR_INVALID_FUNCTION; return NULL; /* FOR loop aborts at first failure here */ } body = for_control_execute_from_FILE(for_ctrl, input, cmdList);
From: Eric Pouech epouech@codeweavers.com
Use that return code for WCMD_exit() ang WCMD_goto().
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/batch.c | 2 +- programs/cmd/builtins.c | 28 +++++++++++++--------------- programs/cmd/wcmd.h | 6 ++++-- programs/cmd/wcmdmain.c | 7 +++++-- 4 files changed, 23 insertions(+), 20 deletions(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c index 15e8baa21c8..402a6f649d2 100644 --- a/programs/cmd/batch.c +++ b/programs/cmd/batch.c @@ -77,7 +77,7 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA /* If processing a call :label, 'goto' the label in question */ if (startLabel) { lstrcpyW(param1, startLabel); - WCMD_goto(NULL); + WCMD_goto(); }
/* diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 9033330e6d1..1c39c35ca78 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -1926,7 +1926,7 @@ void WCMD_give_help (const WCHAR *args) }
/**************************************************************************** - * WCMD_go_to + * WCMD_goto * * Batch file jump instruction. Not the most efficient algorithm ;-) * Prints error message if the specified label cannot be found - the file pointer is @@ -1934,27 +1934,24 @@ void WCMD_give_help (const WCHAR *args) * FIXME: DOS is supposed to allow labels with spaces - we don't. */
-void WCMD_goto (CMD_NODE **cmdList) { - +RETURN_CODE WCMD_goto(void) +{ WCHAR string[MAX_PATH]; WCHAR *labelend = NULL; const WCHAR labelEndsW[] = L"><|& :\t";
- /* Do not process any more parts of a processed multipart or multilines command */ - if (cmdList) *cmdList = NULL; - if (context != NULL) { WCHAR *paramStart = param1, *str;
if (param1[0] == 0x00) { WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOARG)); - return; + return ERROR_INVALID_FUNCTION; }
/* Handle special :EOF label */ if (lstrcmpiW(L":eof", param1) == 0) { context -> skip_rest = TRUE; - return; + return RETURN_CODE_ABORTED; }
/* Support goto :label as well as goto label plus remove trailing chars */ @@ -2000,7 +1997,7 @@ void WCMD_goto (CMD_NODE **cmdList) { if (labelend) *labelend = 0x00; WINE_TRACE("comparing found label %s\n", wine_dbgstr_w(str));
- if (lstrcmpiW (str, paramStart) == 0) return; + if (lstrcmpiW (str, paramStart) == 0) return RETURN_CODE_ABORTED; }
/* See if we have gone beyond the end point if second time through */ @@ -2021,7 +2018,7 @@ void WCMD_goto (CMD_NODE **cmdList) { WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOTARGET)); context -> skip_rest = TRUE; } - return; + return ERROR_INVALID_FUNCTION; }
/***************************************************************************** @@ -3966,16 +3963,17 @@ int WCMD_volume(BOOL set_label, const WCHAR *path) * */
-void WCMD_exit (CMD_NODE **cmdList) { +RETURN_CODE WCMD_exit(void) +{ int rc = wcstol(param1, NULL, 10); /* Note: wcstol of empty parameter is 0 */
- if (context && lstrcmpiW(quals, L"/B") == 0) { + if (context && lstrcmpiW(quals, L"/B") == 0) + { errorlevel = rc; context -> skip_rest = TRUE; - *cmdList = NULL; - } else { - ExitProcess(rc); + return RETURN_CODE_ABORTED; } + ExitProcess(rc); }
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index d11fcd3768f..537e078c412 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -176,10 +176,12 @@ struct _DIRECTORY_STACK *WCMD_dir_stack_free(struct _DIRECTORY_STACK *dir); /* The return code: * - some of them are directly mapped to kernel32's errors * - some others are cmd.exe specific + * - ABORTED if used to break out of FOR/IF blocks (to handle GOTO, EXIT commands) */ typedef int RETURN_CODE; #define RETURN_CODE_SYNTAX_ERROR 255 #define RETURN_CODE_CANT_LAUNCH 9009 +#define RETURN_CODE_ABORTED (-999999)
void WCMD_assoc (const WCHAR *, BOOL); void WCMD_batch (WCHAR *, WCHAR *, BOOL, WCHAR *, HANDLE); @@ -195,11 +197,11 @@ void WCMD_directory (WCHAR *); void WCMD_echo (const WCHAR *); void WCMD_endlocal (void); void WCMD_enter_paged_mode(const WCHAR *); -void WCMD_exit (CMD_NODE **cmdList); +RETURN_CODE WCMD_exit(void); void WCMD_for (WCHAR *, CMD_NODE **cmdList); BOOL WCMD_get_fullpath(const WCHAR *, SIZE_T, WCHAR *, WCHAR **); void WCMD_give_help (const WCHAR *args); -void WCMD_goto (CMD_NODE **cmdList); +RETURN_CODE WCMD_goto(void); void WCMD_if (WCHAR *, CMD_NODE **cmdList); void WCMD_leave_paged_mode(void); void WCMD_more (WCHAR *); diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index b3202edd0d4..d2edb22c9c4 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1695,6 +1695,7 @@ static BOOL set_std_redirections(CMD_REDIRECTION *redir, WCHAR *in_pipe) */ static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOOL retrycall) { + RETURN_CODE return_code = NO_ERROR; WCHAR *cmd, *parms_start; int status, cmd_index, count; WCHAR *whichcmd; @@ -1797,7 +1798,7 @@ static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOO WCMD_echo(&whichcmd[count]); break; case WCMD_GOTO: - WCMD_goto (cmdList); + return_code = WCMD_goto(); break; case WCMD_HELP: WCMD_give_help (parms_start); @@ -1891,7 +1892,7 @@ static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOO WCMD_mklink(parms_start); break; case WCMD_EXIT: - WCMD_exit (cmdList); + return_code = WCMD_exit(); break; case WCMD_FOR: case WCMD_IF: @@ -1911,6 +1912,8 @@ static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOO } cleanup: free(cmd); + if (return_code == RETURN_CODE_ABORTED && cmdList) + *cmdList = NULL; }
/*****************************************************************************
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/tests/test_builtins.cmd | 6 ++++++ programs/cmd/tests/test_builtins.cmd.exp | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index c9951a66373..d5397560bc6 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -802,6 +802,12 @@ if %WINE_FOO% == foo ( set WINE_FOO=bar if !WINE_FOO! == bar (echo bar) else echo foo ) +set WINE_FOO=32 +if %WINE_FOO% == 32 ( + set WINE_FOO=33 + call :setError 33 + if errorlevel !WINE_FOO! (echo gotitright) else echo gotitwrong +) echo %ErrorLevel% setlocal DisableDelayedExpansion echo %ErrorLevel% diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 1564e6c4611..0102c3bcbc5 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -629,8 +629,9 @@ foo foo@or_broken@!WINE_FOO! foo bar@or_broken@foo -0 -0@or_broken@1 +gotitright +33 +@todo_wine@0@or_broken@1 foo !WINE_FOO! @todo_wine@not empty
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmd.h | 4 +-- programs/cmd/wcmdmain.c | 60 ++++++++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 24 deletions(-)
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 537e078c412..e2ee0fc02ac 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -70,9 +70,7 @@ typedef struct _CMD_IF_CONDITION op; union { - /* CMD_IF_ERRORLEVEL */ - int level; - /* CMD_IF_EXIST, CMD_IF_DEFINED */ + /* CMD_IF_ERRORLEVEL, 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 diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index d2edb22c9c4..ce083e710f7 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1143,7 +1143,6 @@ 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); @@ -1182,13 +1181,9 @@ BOOL if_condition_create(WCHAR *start, WCHAR **end, CMD_IF_CONDITION *cond) } if (!wcsicmp(param_copy, L"errorlevel")) { - WCHAR *endptr; - 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; + if (cond) cond->operand = wcsdup(param_copy); } else if (!wcsicmp(param_copy, L"exist")) { @@ -1266,7 +1261,7 @@ const char *debugstr_if_condition(const CMD_IF_CONDITION *cond)
switch (cond->op) { - case CMD_IF_ERRORLEVEL: return wine_dbg_sprintf("%serrorlevel %d}}", header, cond->level); + case CMD_IF_ERRORLEVEL: return wine_dbg_sprintf("%serrorlevel %ls}}", header, cond->operand); 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); @@ -3107,6 +3102,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE
BOOL if_condition_evaluate(CMD_IF_CONDITION *cond, int *test) { + WCHAR expanded_left[MAXSTRING]; + WCHAR expanded_right[MAXSTRING]; int (WINAPI *cmp)(const WCHAR*, const WCHAR*) = cond->case_insensitive ? lstrcmpiW : lstrcmpW;
TRACE("About to evaluate condition %s\n", debugstr_if_condition(cond)); @@ -3114,35 +3111,51 @@ BOOL if_condition_evaluate(CMD_IF_CONDITION *cond, int *test) switch (cond->op) { case CMD_IF_ERRORLEVEL: - *test = errorlevel >= cond->level; + { + WCHAR *endptr; + int level; + + wcscpy(expanded_left, cond->operand); + handleExpansion(expanded_left, context != NULL, delayedsubst); + level = wcstol(expanded_left, &endptr, 10); + if (*endptr) return FALSE; + *test = errorlevel >= level; + } break; case CMD_IF_EXIST: { + size_t len; WIN32_FIND_DATAW fd; HANDLE hff; - size_t len = wcslen(cond->operand);
- if (len) + wcscpy(expanded_left, cond->operand); + handleExpansion(expanded_left, context != NULL, delayedsubst); + if ((len = wcslen(expanded_left))) { /* FindFirstFile does not like a directory path ending in '' or '/', so append a '.' */ - if (cond->operand[len - 1] == '\' || cond->operand[len - 1] == '/') + if ((expanded_left[len - 1] == '\' || expanded_left[len - 1] == '/') && len < MAXSTRING - 1) { - WCHAR *new = xrealloc((void*)cond->operand, (wcslen(cond->operand) + 2) * sizeof(WCHAR)); - wcscat(new, L"."); - cond->operand = new; + wcscat(expanded_left, L"."); } - hff = FindFirstFileW(cond->operand, &fd); + hff = FindFirstFileW(expanded_left, &fd); *test = (hff != INVALID_HANDLE_VALUE); if (*test) FindClose(hff); } } break; case CMD_IF_DEFINED: - *test = GetEnvironmentVariableW(cond->operand, NULL, 0) > 0; + wcscpy(expanded_left, cond->operand); + handleExpansion(expanded_left, context != NULL, delayedsubst); + *test = GetEnvironmentVariableW(expanded_left, NULL, 0) > 0; break; case CMD_IF_BINOP_EQUAL: + wcscpy(expanded_left, cond->left); + handleExpansion(expanded_left, context != NULL, delayedsubst); + wcscpy(expanded_right, cond->right); + handleExpansion(expanded_right, context != NULL, delayedsubst); + /* == is a special case, as it always compares strings */ - *test = (*cmp)(cond->left, cond->right) == 0; + *test = (*cmp)(expanded_left, expanded_right) == 0; break; default: { @@ -3150,13 +3163,18 @@ BOOL if_condition_evaluate(CMD_IF_CONDITION *cond, int *test) WCHAR *end_left, *end_right; int cmp_val;
+ wcscpy(expanded_left, cond->left); + handleExpansion(expanded_left, context != NULL, delayedsubst); + wcscpy(expanded_right, cond->right); + handleExpansion(expanded_right, context != NULL, delayedsubst); + /* 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 > cond->left && !*end_left && end_right > cond->right && !*end_right) + left_int = wcstol(expanded_left, &end_left, 0); + right_int = wcstol(expanded_right, &end_right, 0); + if (end_left > expanded_left && !*end_left && end_right > expanded_right && !*end_right) cmp_val = left_int - right_int; else - cmp_val = (*cmp)(cond->left, cond->right); + cmp_val = (*cmp)(expanded_left, expanded_right); switch (cond->op) { case CMD_IF_BINOP_LSS: *test = cmp_val < 0; break;