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 | 229 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 268 insertions(+), 195 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index b4c00de1e2b..76f75bb2d96 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; - int param_int = wcstol(param, &endptr, 10); - if (endptr == param || *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", debugstr_if_condition(&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..def31d7e2d3 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 *debugstr_if_condition(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 30362c4f963..5f1a94b39b3 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1035,6 +1035,151 @@ static CMD_NODE *node_create_binary(CMD_OPERATOR op, CMD_NODE *l, CMD_NODE *r) return new; }
+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; + 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 *debugstr_if_condition(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 void init_msvcrt_io_block(STARTUPINFOW* st) { STARTUPINFOW st_p; @@ -2092,20 +2237,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", @@ -2115,7 +2252,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;
@@ -2503,6 +2639,77 @@ 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; + + TRACE("About to evaluate condition %s\n", debugstr_if_condition(cond)); + *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; + } + 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; + break; + case CMD_IF_BINOP_EQUAL: + /* == is a special case, as it always compares strings */ + *test = (*cmp)(cond->left, cond->right) == 0; + break; + default: + { + int left_int, right_int; + WCHAR *end_left, *end_right; + 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 > cond->left && !*end_left && end_right > cond->right && !*end_right) + cmp_val = left_int - right_int; + else + cmp_val = (*cmp)(cond->left, cond->right); + 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("Unexpected comparison operator %u\n", cond->op); + return FALSE; + } + } + break; + } + if (cond->negated) *test ^= 1; + return TRUE; +} + /*************************************************************************** * WCMD_process_commands *