[PATCH 3/3] [resend] [cmd] Add set /a support to wine
Ann and Jason Edmeades
jason at edmeades.me.uk
Sun Dec 16 18:12:40 CST 2012
Resend#1 - Just rebased on changes to earlier patches
This patch adds set /a support, and flags the tests that exercise
it as fixed. It was initially based on a patch from Jack Kutilek
(attached to bug 21174) but to be honest I ended up rewriting
almost all of it. I did try to break it down, but the difference
from all the operators working and only one is tiny, and I could
not identify a better way of breaking it up.
For reference, once this goes in, the only chunk of functionality
I am aware of that is missing from cmd.exe is delayed expansion,
so if anyone has any other bugs with functionality in cmd.exe
please raise them with a component of 'cmd' :-)
[Fixes bug 21174 and associated tests]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-patches/attachments/20121217/d5034165/attachment-0001.html>
-------------- next part --------------
From 8d30db11c243121074a3122453e06964570366c0 Mon Sep 17 00:00:00 2001
From: Jason Edmeades <jason at edmeades.me.uk>
Date: Thu, 13 Dec 2012 21:51:31 +0000
Subject: [PATCH 3/3] [resend] [cmd] Add set /a support to wine
Resend#1 - Just rebased on changes to earlier patches
This patch adds set /a support, and flags the tests that exercise
it as fixed. It was initially based on a patch from Jack Kutilek
(attached to bug 21174) but to be honest I ended up rewriting
almost all of it. I did try to break it down, but the difference
from all the operators working and only one is tiny, and I could
not identify a better way of breaking it up.
For reference, once this goes in, the only chunk of functionality
I am aware of that is missing from cmd.exe is delayed expansion,
so if anyone has any other bugs with functionality in cmd.exe
please raise them with a component of 'cmd' :-)
[Fixes bug 21174 and associated tests]
---
programs/cmd/builtins.c | 614 +++++++++++++++++++++++++++++-
programs/cmd/cmd.rc | 5 +
programs/cmd/tests/test_builtins.cmd.exp | 306 +++++++--------
programs/cmd/wcmd.h | 5 +
4 files changed, 776 insertions(+), 154 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index 8715c51..4809f75 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -111,6 +111,62 @@ static HINSTANCE hinst;
struct env_stack *saved_environment;
static BOOL verify_mode = FALSE;
+/* set /a routines work from single character operators, but some of the
+ operators are multiple character ones, especially the assignment ones.
+ Temporarily represent these using the values below on the operator stack */
+#define OP_POSITIVE 'P'
+#define OP_NEGATIVE 'N'
+#define OP_ASSSIGNMUL 'a'
+#define OP_ASSSIGNDIV 'b'
+#define OP_ASSSIGNMOD 'c'
+#define OP_ASSSIGNADD 'd'
+#define OP_ASSSIGNSUB 'e'
+#define OP_ASSSIGNAND 'f'
+#define OP_ASSSIGNNOT 'g'
+#define OP_ASSSIGNOR 'h'
+#define OP_ASSSIGNSHL 'i'
+#define OP_ASSSIGNSHR 'j'
+
+/* This maintains a stack of operators, holding both the operator precedence
+ and the single character representation of the operator in question */
+typedef struct _OPSTACK
+{
+ int precedence;
+ WCHAR op;
+ struct _OPSTACK *next;
+} OPSTACK;
+
+/* This maintains a stack of values, where each value can either be a
+ numeric value, or a string represeting an environment variable */
+typedef struct _VARSTACK
+{
+ BOOL isnum;
+ WCHAR *variable;
+ int value;
+ struct _VARSTACK *next;
+} VARSTACK;
+
+/* This maintains a mapping between the calculated operator and the
+ single character representation for the assignment operators. */
+static struct
+{
+ WCHAR op;
+ WCHAR calculatedop;
+} calcassignments[] =
+{
+ {'*', OP_ASSSIGNMUL},
+ {'/', OP_ASSSIGNDIV},
+ {'%', OP_ASSSIGNMOD},
+ {'+', OP_ASSSIGNADD},
+ {'-', OP_ASSSIGNSUB},
+ {'&', OP_ASSSIGNAND},
+ {'^', OP_ASSSIGNNOT},
+ {'|', OP_ASSSIGNOR},
+ {'<', OP_ASSSIGNSHL},
+ {'>', OP_ASSSIGNSHR},
+ {' ',' '}
+};
+
/**************************************************************************
* WCMD_ask_confirm
*
@@ -3404,6 +3460,521 @@ static int WCMD_setshow_sortenv(const WCHAR *s, const WCHAR *stub)
}
/****************************************************************************
+ * WCMD_getprecedence
+ * Return the precedence of a particular operator
+ */
+static int WCMD_getprecedence(const WCHAR in)
+{
+ switch (in) {
+ case '!':
+ case '~':
+ case OP_POSITIVE:
+ case OP_NEGATIVE:
+ return 8;
+ case '*':
+ case '/':
+ case '%':
+ return 7;
+ case '+':
+ case '-':
+ return 6;
+ case '<':
+ case '>':
+ return 5;
+ case '&':
+ return 4;
+ case '^':
+ return 3;
+ case '|':
+ return 2;
+ case '=':
+ case OP_ASSSIGNMUL:
+ case OP_ASSSIGNDIV:
+ case OP_ASSSIGNMOD:
+ case OP_ASSSIGNADD:
+ case OP_ASSSIGNSUB:
+ case OP_ASSSIGNAND:
+ case OP_ASSSIGNNOT:
+ case OP_ASSSIGNOR:
+ case OP_ASSSIGNSHL:
+ case OP_ASSSIGNSHR:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/****************************************************************************
+ * WCMD_pushnumber
+ * Push either a number or name (environment variable) onto the supplied
+ * stack
+ */
+static void WCMD_pushnumber(WCHAR *var, int num, VARSTACK **varstack) {
+ VARSTACK *thisstack = heap_alloc(sizeof(VARSTACK));
+ thisstack->isnum = (var == NULL);
+ if (var) {
+ thisstack->variable = var;
+ WINE_TRACE("Pushed variable %s\n", wine_dbgstr_w(var));
+ } else {
+ thisstack->value = num;
+ WINE_TRACE("Pushed number %d\n", num);
+ }
+ thisstack->next = *varstack;
+ *varstack = thisstack;
+}
+
+/****************************************************************************
+ * WCMD_peeknumber
+ * Returns the value of the top number or environment variable on the stack
+ * and leaves the item on the stack.
+ */
+static int WCMD_peeknumber(VARSTACK **varstack) {
+ int result = 0;
+ VARSTACK *thisvar;
+
+ if (varstack) {
+ thisvar = *varstack;
+ if (!thisvar->isnum) {
+ WCHAR tmpstr[MAXSTRING];
+ if (GetEnvironmentVariableW(thisvar->variable, tmpstr, MAXSTRING)) {
+ result = strtoulW(tmpstr,NULL,0);
+ }
+ WINE_TRACE("Envvar %s converted to %d\n", wine_dbgstr_w(thisvar->variable), result);
+ } else {
+ result = thisvar->value;
+ }
+ }
+ WINE_TRACE("Peeked number %d\n", result);
+ return result;
+}
+
+/****************************************************************************
+ * WCMD_popnumber
+ * Returns the value of the top number or environment variable on the stack
+ * and removes the item from the stack.
+ */
+static int WCMD_popnumber(VARSTACK **varstack) {
+ int result = 0;
+ VARSTACK *thisvar;
+
+ if (varstack) {
+ thisvar = *varstack;
+ result = WCMD_peeknumber(varstack);
+ if (!thisvar->isnum) heap_free(thisvar->variable);
+ *varstack = thisvar->next;
+ heap_free(thisvar);
+ }
+ WINE_TRACE("Popped number %d\n", result);
+ return result;
+}
+
+/****************************************************************************
+ * WCMD_pushoperator
+ * Push an operator onto the supplied stack
+ */
+static void WCMD_pushoperator(WCHAR op, int precedence, OPSTACK **opstack) {
+ OPSTACK *thisstack = heap_alloc(sizeof(OPSTACK));
+ thisstack->precedence = precedence;
+ thisstack->op = op;
+ thisstack->next = *opstack;
+ WINE_TRACE("Pushed operator %c\n", op);
+ *opstack = thisstack;
+}
+
+/****************************************************************************
+ * WCMD_popoperator
+ * Returns the operator from the top of the stack and removes the item from
+ * the stack.
+ */
+static WCHAR WCMD_popoperator(OPSTACK **opstack) {
+ WCHAR result = 0;
+ OPSTACK *thisop;
+
+ if (opstack) {
+ thisop = *opstack;
+ result = thisop->op;
+ *opstack = thisop->next;
+ heap_free(thisop);
+ }
+ WINE_TRACE("Popped operator %c\n", result);
+ return result;
+}
+
+/****************************************************************************
+ * WCMD_reduce
+ * Actions the top operator on the stack against the first and sometimes
+ * second value on the variable stack, and pushes the result
+ * Returns non-zero on error.
+ */
+static int WCMD_reduce(OPSTACK **opstack, VARSTACK **varstack) {
+ OPSTACK *thisop;
+ int var1,var2;
+ int rc = 0;
+
+ if (!*opstack || !*varstack) {
+ WINE_TRACE("No operators for the reduce\n");
+ return WCMD_NOOPERATOR;
+ }
+
+ /* Remove the top operator */
+ thisop = *opstack;
+ *opstack = (*opstack)->next;
+ WINE_TRACE("Reducing the stacks - processing operator %c\n", thisop->op);
+
+ /* One variable operators */
+ var1 = WCMD_popnumber(varstack);
+ switch (thisop->op) {
+ case '!': WCMD_pushnumber(NULL, !var1, varstack);
+ break;
+ case '~': WCMD_pushnumber(NULL, ~var1, varstack);
+ break;
+ case OP_POSITIVE: WCMD_pushnumber(NULL, var1, varstack);
+ break;
+ case OP_NEGATIVE: WCMD_pushnumber(NULL, -var1, varstack);
+ break;
+ }
+
+ /* Two variable operators */
+ if (!*varstack) {
+ WINE_TRACE("No operands left for the reduce?\n");
+ return WCMD_NOOPERAND;
+ }
+ switch (thisop->op) {
+ case '!':
+ case '~':
+ case OP_POSITIVE:
+ case OP_NEGATIVE:
+ break; /* Handled above */
+ case '*': var2 = WCMD_popnumber(varstack);
+ WCMD_pushnumber(NULL, var2*var1, varstack);
+ break;
+ case '/': var2 = WCMD_popnumber(varstack);
+ if (var1 == 0) return WCMD_DIVIDEBYZERO;
+ WCMD_pushnumber(NULL, var2/var1, varstack);
+ break;
+ case '+': var2 = WCMD_popnumber(varstack);
+ WCMD_pushnumber(NULL, var2+var1, varstack);
+ break;
+ case '-': var2 = WCMD_popnumber(varstack);
+ WCMD_pushnumber(NULL, var2-var1, varstack);
+ break;
+ case '&': var2 = WCMD_popnumber(varstack);
+ WCMD_pushnumber(NULL, var2&var1, varstack);
+ break;
+ case '%': var2 = WCMD_popnumber(varstack);
+ WCMD_pushnumber(NULL, var2%var1, varstack);
+ break;
+ case '^': var2 = WCMD_popnumber(varstack);
+ WCMD_pushnumber(NULL, var2^var1, varstack);
+ break;
+ case '<': var2 = WCMD_popnumber(varstack);
+ /* Shift left has to be a positive number, 0-31 otherwise 0 is returned,
+ which differs from the compiler (for example gcc) so being explicit. */
+ if (var1 < 0 || var1 >= (8 * sizeof(INT))) {
+ WCMD_pushnumber(NULL, 0, varstack);
+ } else {
+ WCMD_pushnumber(NULL, var2<<var1, varstack);
+ }
+ break;
+ case '>': var2 = WCMD_popnumber(varstack);
+ WCMD_pushnumber(NULL, var2>>var1, varstack);
+ break;
+ case '|': var2 = WCMD_popnumber(varstack);
+ WCMD_pushnumber(NULL, var2|var1, varstack);
+ break;
+
+ case OP_ASSSIGNMUL:
+ case OP_ASSSIGNDIV:
+ case OP_ASSSIGNMOD:
+ case OP_ASSSIGNADD:
+ case OP_ASSSIGNSUB:
+ case OP_ASSSIGNAND:
+ case OP_ASSSIGNNOT:
+ case OP_ASSSIGNOR:
+ case OP_ASSSIGNSHL:
+ case OP_ASSSIGNSHR:
+ {
+ int i = 0;
+
+ /* The left of an equals must be one variable */
+ if (!(*varstack) || (*varstack)->isnum) {
+ return WCMD_NOOPERAND;
+ }
+
+ /* Make the number stack grow by inserting the value of the variable */
+ var2 = WCMD_peeknumber(varstack);
+ WCMD_pushnumber(NULL, var2, varstack);
+ WCMD_pushnumber(NULL, var1, varstack);
+
+ /* Make the operand stack grow by pushing the assign operator plus the
+ operator to perform */
+ while (calcassignments[i].op != ' ' &&
+ calcassignments[i].calculatedop != thisop->op) {
+ i++;
+ }
+ if (calcassignments[i].calculatedop == ' ') {
+ WINE_ERR("Unexpected operator %c\n", thisop->op);
+ return WCMD_NOOPERATOR;
+ }
+ WCMD_pushoperator('=', WCMD_getprecedence('='), opstack);
+ WCMD_pushoperator(calcassignments[i].op,
+ WCMD_getprecedence(calcassignments[i].op), opstack);
+ break;
+ }
+
+ case '=':
+ {
+ WCHAR intFormat[] = {'%','d','\0'};
+ WCHAR result[MAXSTRING];
+
+ /* Build the result, then push it onto the stack */
+ sprintfW(result, intFormat, var1);
+ WINE_TRACE("Assigning %s a value %s\n", wine_dbgstr_w((*varstack)->variable),
+ wine_dbgstr_w(result));
+ SetEnvironmentVariableW((*varstack)->variable, result);
+ var2 = WCMD_popnumber(varstack);
+ WCMD_pushnumber(NULL, var1, varstack);
+ break;
+ }
+
+ default: WINE_ERR("Unrecognized operator %c\n", thisop->op);
+ }
+
+ heap_free(thisop);
+ return rc;
+}
+
+
+/****************************************************************************
+ * WCMD_handleExpression
+ * Handles an expression provided to set /a - If it finds brackets, it uses
+ * recursion to process the parts in brackets.
+ */
+static int WCMD_handleExpression(WCHAR **expr, int *ret, int depth)
+{
+ static const WCHAR mathDelims[] = {' ','\t','(',')','!','~','-','*','/','%',
+ '+','<','>','&','^','|','=',',','\0' };
+ int rc = 0;
+ WCHAR *pos;
+ BOOL lastwasnumber = FALSE; /* FALSE makes a minus at the start of the expression easier to handle */
+ OPSTACK *opstackhead = NULL;
+ VARSTACK *varstackhead = NULL;
+ WCHAR foundhalf = 0;
+
+ /* Initialize */
+ WINE_TRACE("Handling expression '%s'\n", wine_dbgstr_w(*expr));
+ pos = *expr;
+
+ /* Iterate through until whole expression is processed */
+ while (pos && *pos) {
+ BOOL treatasnumber;
+
+ /* Skip whitespace to get to the next character to process*/
+ while (*pos && (*pos==' ' || *pos=='\t')) pos++;
+ if (!*pos) goto exprreturn;
+
+ /* If we have found anything other than an operator then its a number/variable */
+ if (strchrW(mathDelims, *pos) == NULL) {
+ WCHAR *parmstart, *parm, *dupparm;
+ WCHAR *nextpos;
+
+ /* Cannot have an expression with var/number twice, without an operator
+ inbetween, nor or number following a half constructed << or >> operator */
+ if (lastwasnumber || foundhalf) {
+ rc = WCMD_NOOPERATOR;
+ goto exprerrorreturn;
+ }
+ lastwasnumber = TRUE;
+
+ if (isdigitW(*pos)) {
+ /* For a number - just push it onto the stack */
+ int num = strtoulW(pos, &nextpos, 0);
+ WCMD_pushnumber(NULL, num, &varstackhead);
+ pos = nextpos;
+
+ /* Verify the number was validly formed */
+ if (*nextpos && (strchrW(mathDelims, *nextpos) == NULL)) {
+ rc = WCMD_BADHEXOCT;
+ goto exprerrorreturn;
+ }
+ } else {
+
+ /* For a variable - just push it onto the stack */
+ parm = WCMD_parameter_with_delims(pos, 0, &parmstart, FALSE, FALSE, mathDelims);
+ dupparm = heap_strdupW(parm);
+ WCMD_pushnumber(dupparm, 0, &varstackhead);
+ pos = parmstart + strlenW(dupparm);
+ }
+ continue;
+ }
+
+ /* We have found an operator. Some operators are one character, some two, and the minus
+ and plus signs need special processing as they can be either operators or just influence
+ the parameter which follows them */
+ if (foundhalf && (*pos != foundhalf)) {
+ /* Badly constructed operator pair */
+ rc = WCMD_NOOPERATOR;
+ goto exprerrorreturn;
+ }
+
+ treatasnumber = FALSE; /* We are processing an operand */
+ switch (*pos) {
+
+ /* > and < are special as they are double character operators (and spaces can be between them!)
+ If we see these for the first time, set a flag, and second time around we continue.
+ Note these double character operators are stored as just one of the characters on the stack */
+ case '>':
+ case '<': if (!foundhalf) {
+ foundhalf = *pos;
+ pos++;
+ break;
+ }
+ /* We have found the rest, so clear up the knowledge of the half completed part and
+ drop through to normal operator processing */
+ foundhalf = 0;
+ /* drop through */
+
+ case '=': if (*pos=='=') {
+ /* = is special cased as if the last was an operator then we may have e.g. += or
+ *= etc which we need to handle by replacing the operator that is on the stack
+ with a calculated assignment equivalent */
+ if (!lastwasnumber && opstackhead) {
+ int i = 0;
+ while (calcassignments[i].op != ' ' && calcassignments[i].op != opstackhead->op) {
+ i++;
+ }
+ if (calcassignments[i].op == ' ') {
+ rc = WCMD_NOOPERAND;
+ goto exprerrorreturn;
+ } else {
+ /* Remove the operator on the stack, it will be replaced with a ?= equivalent
+ when the general operator handling happens further down. */
+ *pos = calcassignments[i].calculatedop;
+ WCMD_popoperator(&opstackhead);
+ }
+ }
+ }
+ /* Drop though */
+
+ /* + and - are slightly special as they can be a numeric prefix, if they follow an operator
+ so if they do, convert the +/- (arithmetic) to +/- (numeric prefix for positive/negative) */
+ case '+': if (!lastwasnumber && *pos=='+') *pos = OP_POSITIVE;
+ /* drop through */
+ case '-': if (!lastwasnumber && *pos=='-') *pos = OP_NEGATIVE;
+ /* drop through */
+
+ /* Normal operators - push onto stack unless precedence means we have to calculate it now */
+ case '!': /* drop through */
+ case '~': /* drop through */
+ case '/': /* drop through */
+ case '%': /* drop through */
+ case '&': /* drop through */
+ case '^': /* drop through */
+ case '*': /* drop through */
+ case '|':
+ /* General code for handling most of the operators - look at the
+ precedence of the top item on the stack, and see if we need to
+ action the stack before we push something else onto it. */
+ {
+ int precedence = WCMD_getprecedence(*pos);
+ WINE_TRACE("Found operator %c precedence %d (head is %d)\n", *pos,
+ precedence, !opstackhead?-1:opstackhead->precedence);
+
+ /* In general, for things with the same precedence, reduce immediately
+ except for assignments and unary operators which do not */
+ while (!rc && opstackhead &&
+ ((opstackhead->precedence > precedence) ||
+ ((opstackhead->precedence == precedence) &&
+ (precedence != 1) && (precedence != 8)))) {
+ rc = WCMD_reduce(&opstackhead, &varstackhead);
+ }
+ if (rc) goto exprerrorreturn;
+ WCMD_pushoperator(*pos, precedence, &opstackhead);
+ pos++;
+ break;
+ }
+
+ /* comma means start a new expression, ie calculate what we have */
+ case ',':
+ {
+ int prevresult = -1;
+ WINE_TRACE("Found expression delimiter - reducing exising stacks\n");
+ while (!rc && opstackhead) {
+ rc = WCMD_reduce(&opstackhead, &varstackhead);
+ }
+ if (rc) goto exprerrorreturn;
+ /* If we have anything other than one number left, error
+ otherwise throw the number away */
+ if (!varstackhead || varstackhead->next) {
+ rc = WCMD_NOOPERATOR;
+ goto exprerrorreturn;
+ }
+ prevresult = WCMD_popnumber(&varstackhead);
+ WINE_TRACE("Expression resolved to %d\n", prevresult);
+ heap_free(varstackhead);
+ varstackhead = NULL;
+ pos++;
+ break;
+ }
+
+ /* Open bracket - use iteration to parse the inner expression, then continue */
+ case '(' : {
+ int exprresult = 0;
+ pos++;
+ rc = WCMD_handleExpression(&pos, &exprresult, depth+1);
+ if (rc) goto exprerrorreturn;
+ WCMD_pushnumber(NULL, exprresult, &varstackhead);
+ break;
+ }
+
+ /* Close bracket - we have finished this depth, calculate and return */
+ case ')' : {
+ pos++;
+ treatasnumber = TRUE; /* Things in brackets result in a number */
+ if (depth == 0) {
+ rc = WCMD_BADPAREN;
+ goto exprerrorreturn;
+ }
+ goto exprreturn;
+ }
+
+ default:
+ WINE_ERR("Unrecognized operator %c\n", *pos);
+ pos++;
+ }
+ lastwasnumber = treatasnumber;
+ }
+
+exprreturn:
+ *expr = pos;
+
+ /* We need to reduce until we have a single number (or variable) on the
+ stack and set the return value to that */
+ while (!rc && opstackhead) {
+ rc = WCMD_reduce(&opstackhead, &varstackhead);
+ }
+ if (rc) goto exprerrorreturn;
+
+ /* If we have anything other than one number left, error
+ otherwise throw the number away */
+ if (!varstackhead || varstackhead->next) {
+ rc = WCMD_NOOPERATOR;
+ goto exprerrorreturn;
+ }
+
+ /* Now get the number (and convert if its just a variable name) */
+ *ret = WCMD_popnumber(&varstackhead);
+
+exprerrorreturn:
+ /* Free all remaining memory */
+ while (opstackhead) WCMD_popoperator(&opstackhead);
+ while (varstackhead) WCMD_popnumber(&varstackhead);
+
+ WINE_TRACE("Returning result %d, rc %d\n", *ret, rc);
+ return rc;
+}
+
+/****************************************************************************
* WCMD_setshow_env
*
* Set/Show the environment variables
@@ -3415,6 +3986,8 @@ void WCMD_setshow_env (WCHAR *s) {
WCHAR *p;
int status;
static const WCHAR parmP[] = {'/','P','\0'};
+ static const WCHAR parmA[] = {'/','A','\0'};
+ WCHAR string[MAXSTRING];
if (param1[0] == 0x00 && quals[0] == 0x00) {
env = GetEnvironmentStringsW();
@@ -3426,7 +3999,6 @@ void WCMD_setshow_env (WCHAR *s) {
if (CompareStringW(LOCALE_USER_DEFAULT,
NORM_IGNORECASE | SORT_STRINGSORT,
s, 2, parmP, -1) == CSTR_EQUAL) {
- WCHAR string[MAXSTRING];
DWORD count;
s += 2;
@@ -3454,6 +4026,46 @@ void WCMD_setshow_env (WCHAR *s) {
status = SetEnvironmentVariableW(s, string);
}
+ /* See if /A supplied, and if so calculate the results of all the expressions */
+ } else if (CompareStringW(LOCALE_USER_DEFAULT,
+ NORM_IGNORECASE | SORT_STRINGSORT,
+ s, 2, parmA, -1) == CSTR_EQUAL) {
+ /* /A supplied, so evaluate expressions and set variables appropriately */
+ /* Syntax is set /a var=1,var2=var+4 etc, and it echos back the result */
+ /* of the final computation */
+ int result = 0;
+ int rc = 0;
+ WCHAR *thisexpr;
+ WCHAR *src,*dst;
+
+ /* Remove all quotes before doing any calculations */
+ thisexpr = heap_alloc((strlenW(s+2)+1) * sizeof(WCHAR));
+ src = s+2;
+ dst = thisexpr;
+ while (*src) {
+ if (*src != '"') *dst++ = *src;
+ src++;
+ }
+ *dst = 0;
+
+ /* Now calculate the results of the expression */
+ src = thisexpr;
+ rc = WCMD_handleExpression(&src, &result, 0);
+ heap_free(thisexpr);
+
+ /* If parsing failed, issue the error message */
+ if (rc > 0) {
+ WCMD_output_stderr(WCMD_LoadMessage(rc));
+ return;
+ }
+
+ /* If we have no context (interactive or cmd.exe /c) print the final result */
+ if (!context) {
+ static const WCHAR fmt[] = {'%','d','\0'};
+ sprintfW(string, fmt, result);
+ WCMD_output_asis(string);
+ }
+
} else {
DWORD gle;
diff --git a/programs/cmd/cmd.rc b/programs/cmd/cmd.rc
index 184aaa8..817dde4 100644
--- a/programs/cmd/cmd.rc
+++ b/programs/cmd/cmd.rc
@@ -354,4 +354,9 @@ Enter HELP <command> for further information on any of the above commands.\n"
WCMD_YESNO, " (Yes|No)"
WCMD_YESNOALL, " (Yes|No|All)"
WCMD_NO_COMMAND_FOUND, "Can't recognize '%1' as an internal or external command, or batch script.\n"
+ WCMD_DIVIDEBYZERO, "Division by zero error.\n"
+ WCMD_NOOPERAND, "Expected an operand.\n"
+ WCMD_NOOPERATOR, "Expected an operator.\n"
+ WCMD_BADPAREN, "Mismatch in parenthesis.\n"
+ WCMD_BADHEXOCT, "Badly formed number - must be one of decimal (12),\n hexadecimal (0x34) or octal (056).\n"
}
diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp
index a9c0d1c..b35692b 100644
--- a/programs/cmd/tests/test_builtins.cmd.exp
+++ b/programs/cmd/tests/test_builtins.cmd.exp
@@ -717,172 +717,172 @@ ErrorLevel 0
3
--- set /a
------ individual operations
- at todo_wine@WINE_foo correctly 3
- at todo_wine@WINE_foo correctly -1
- at todo_wine@WINE_foo correctly 3
- at todo_wine@WINE_foo correctly 6
- at todo_wine@WINE_foo correctly 10
- at todo_wine@WINE_foo correctly 4
- at todo_wine@WINE_foo correctly 4
- at todo_wine@WINE_foo correctly -4
- at todo_wine@WINE_foo correctly 0
- at todo_wine@WINE_foo correctly 2
- at todo_wine@WINE_foo correctly 2
- at todo_wine@WINE_foo correctly -2
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_foo correctly 4
- at todo_wine@WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly 1073741824 [0]
- at todo_wine@WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly -1073741824 [0]
- at todo_wine@WINE_foo correctly -4
- at todo_wine@WINE_foo correctly 9
- at todo_wine@WINE_foo correctly 2
- at todo_wine@WINE_foo correctly 0
- at todo_wine@WINE_foo correctly -1
- at todo_wine@WINE_foo correctly -3
- at todo_wine@WINE_foo correctly 0
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_foo correctly 4
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_foo correctly 5
- at todo_wine@WINE_foo correctly 5
- at todo_wine@WINE_foo correctly 7
- at todo_wine@WINE_foo correctly 5
- at todo_wine@WINE_foo correctly 5
- at todo_wine@WINE_foo correctly 5
- at todo_wine@WINE_foo correctly 4
- at todo_wine@WINE_foo correctly 6
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_foo correctly 4
+WINE_foo correctly 3
+WINE_foo correctly -1
+WINE_foo correctly 3
+WINE_foo correctly 6
+WINE_foo correctly 10
+WINE_foo correctly 4
+WINE_foo correctly 4
+WINE_foo correctly -4
+WINE_foo correctly 0
+WINE_foo correctly 2
+WINE_foo correctly 2
+WINE_foo correctly -2
+WINE_foo correctly 1
+WINE_foo correctly 4
+WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly 1073741824 [0]
+WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly -1073741824 [0]
+WINE_foo correctly -4
+WINE_foo correctly 9
+WINE_foo correctly 2
+WINE_foo correctly 0
+WINE_foo correctly -1
+WINE_foo correctly -3
+WINE_foo correctly 0
+WINE_foo correctly 1
+WINE_foo correctly 1
+WINE_foo correctly 4
+WINE_foo correctly 1
+WINE_foo correctly 5
+WINE_foo correctly 5
+WINE_foo correctly 7
+WINE_foo correctly 5
+WINE_foo correctly 5
+WINE_foo correctly 5
+WINE_foo correctly 4
+WINE_foo correctly 6
+WINE_foo correctly 1
+WINE_foo correctly 4
------ precedence and grouping
- at todo_wine@WINE_foo correctly 10
- at todo_wine@WINE_foo correctly 18 at or_broken@ERROR: WINE_foo incorrectly [18]
- at todo_wine@WINE_foo correctly 2 at or_broken@ERROR: WINE_foo incorrectly 0 [2]
- at todo_wine@WINE_foo correctly 2 at or_broken@ERROR: WINE_foo incorrectly [2]
- at todo_wine@WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly 4 [0]
- at todo_wine@WINE_foo correctly 4 at or_broken@ERROR: WINE_foo incorrectly [4]
- at todo_wine@WINE_foo correctly 3 at or_broken@ERROR: WINE_foo incorrectly [3]
- at todo_wine@WINE_foo correctly 3 at or_broken@ERROR: WINE_foo incorrectly [3]
+WINE_foo correctly 10
+WINE_foo correctly 18 at or_broken@ERROR: WINE_foo incorrectly [18]
+WINE_foo correctly 2 at or_broken@ERROR: WINE_foo incorrectly 0 [2]
+WINE_foo correctly 2 at or_broken@ERROR: WINE_foo incorrectly [2]
+WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly 4 [0]
+WINE_foo correctly 4 at or_broken@ERROR: WINE_foo incorrectly [4]
+WINE_foo correctly 3 at or_broken@ERROR: WINE_foo incorrectly [3]
+WINE_foo correctly 3 at or_broken@ERROR: WINE_foo incorrectly [3]
------ octal and hexadecimal
- at todo_wine@WINE_foo correctly 18
- at todo_wine@WINE_foo correctly 18
- at todo_wine@WINE_foo correctly 15
- at todo_wine@WINE_foo correctly 3
+WINE_foo correctly 18
+WINE_foo correctly 18
+WINE_foo correctly 15
+WINE_foo correctly 3
------ variables
- at todo_wine@WINE_foo correctly 3 at or_broken@ERROR: WINE_foo incorrectly 0 [3]
- at todo_wine@WINE_bar correctly 3 at or_broken@ERROR: WINE_bar incorrectly [3]
- at todo_wine@WINE_foo correctly 3 at or_broken@ERROR: WINE_foo incorrectly 0 [3]
- at todo_wine@WINE_bar correctly 4 at or_broken@ERROR: WINE_bar incorrectly [4]
- at todo_wine@WINE_foo correctly 3 at or_broken@ERROR: WINE_foo incorrectly 0 [3]
- at todo_wine@WINE_bar correctly 3 at or_broken@ERROR: WINE_bar incorrectly [3]
- at todo_wine@WINE_baz correctly 3 at or_broken@ERROR: WINE_baz incorrectly [4]
- at todo_wine@WINE_foo correctly 9 at or_broken@ERROR: WINE_foo incorrectly 3 [9]
- at todo_wine@WINE_bar correctly 9 at or_broken@ERROR: WINE_bar incorrectly 3 [9]
- at todo_wine@WINE_foo correctly 0
- at todo_wine@WINE_foo correctly 4
+WINE_foo correctly 3 at or_broken@ERROR: WINE_foo incorrectly 0 [3]
+WINE_bar correctly 3 at or_broken@ERROR: WINE_bar incorrectly [3]
+WINE_foo correctly 3 at or_broken@ERROR: WINE_foo incorrectly 0 [3]
+WINE_bar correctly 4 at or_broken@ERROR: WINE_bar incorrectly [4]
+WINE_foo correctly 3 at or_broken@ERROR: WINE_foo incorrectly 0 [3]
+WINE_bar correctly 3 at or_broken@ERROR: WINE_bar incorrectly [3]
+WINE_baz correctly 4 at or_broken@ERROR: WINE_baz incorrectly [4]
+WINE_foo correctly 9 at or_broken@ERROR: WINE_foo incorrectly 3 [9]
+WINE_bar correctly 9 at or_broken@ERROR: WINE_bar incorrectly 3 [9]
+WINE_foo correctly 0
+WINE_foo correctly 4
WINE_bar correctly 4
- at todo_wine@WINE_foo correctly -7 at or_broken@ERROR: WINE_foo incorrectly 4 [-7]
- at todo_wine@WINE_bar correctly -7 at or_broken@ERROR: WINE_bar incorrectly 4 [-7]
- at todo_wine@WINE_foo correctly -1 at or_broken@ERROR: WINE_foo incorrectly -7 [-1]
- at todo_wine@WINE_bar correctly -1 at or_broken@ERROR: WINE_bar incorrectly -7 [-1]
- at todo_wine@WINE_foo correctly 5 at or_broken@ERROR: WINE_foo incorrectly 0 [5]
- at todo_wine@WINE_bar correctly 1 at or_broken@ERROR: WINE_bar incorrectly [1]
- at todo_wine@WINE_foo correctly 4 at or_broken@ERROR: WINE_foo incorrectly 1 [4]
- at todo_wine@WINE_bar correctly 4 at or_broken@ERROR: WINE_bar incorrectly 1 [4]
- at todo_wine@WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly 4 [1]
- at todo_wine@WINE_bar correctly 1 at or_broken@ERROR: WINE_bar incorrectly 4 [1]
- at todo_wine@WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly 1 [0]
- at todo_wine@WINE_bar correctly 0 at or_broken@ERROR: WINE_bar incorrectly 1 [0]
- at todo_wine@WINE_foo correctly 5 at or_broken@ERROR: WINE_foo incorrectly 0 [5]
- at todo_wine@WINE_bar correctly 7 at or_broken@ERROR: WINE_bar incorrectly [7]
- at todo_wine@WINE_foo correctly 5 at or_broken@ERROR: WINE_foo incorrectly 0 [5]
- at todo_wine@WINE_bar correctly 7 at or_broken@ERROR: WINE_bar incorrectly [7]
- at todo_wine@WINE_foo correctly 19 at or_broken@ERROR: WINE_foo incorrectly 0 [19]
- at todo_wine@WINE_bar correctly 3 at or_broken@ERROR: WINE_bar incorrectly [3]
- at todo_wine@WINE_baz correctly 19
+WINE_foo correctly -7 at or_broken@ERROR: WINE_foo incorrectly 4 [-7]
+WINE_bar correctly -7 at or_broken@ERROR: WINE_bar incorrectly 4 [-7]
+WINE_foo correctly -1 at or_broken@ERROR: WINE_foo incorrectly -7 [-1]
+WINE_bar correctly -1 at or_broken@ERROR: WINE_bar incorrectly -7 [-1]
+WINE_foo correctly 5 at or_broken@ERROR: WINE_foo incorrectly 0 [5]
+WINE_bar correctly 1 at or_broken@ERROR: WINE_bar incorrectly [1]
+WINE_foo correctly 4 at or_broken@ERROR: WINE_foo incorrectly 1 [4]
+WINE_bar correctly 4 at or_broken@ERROR: WINE_bar incorrectly 1 [4]
+WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly 4 [1]
+WINE_bar correctly 1 at or_broken@ERROR: WINE_bar incorrectly 4 [1]
+WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly 1 [0]
+WINE_bar correctly 0 at or_broken@ERROR: WINE_bar incorrectly 1 [0]
+WINE_foo correctly 5 at or_broken@ERROR: WINE_foo incorrectly 0 [5]
+WINE_bar correctly 7 at or_broken@ERROR: WINE_bar incorrectly [7]
+WINE_foo correctly 5 at or_broken@ERROR: WINE_foo incorrectly 0 [5]
+WINE_bar correctly 7 at or_broken@ERROR: WINE_bar incorrectly [7]
+WINE_foo correctly 19 at or_broken@ERROR: WINE_foo incorrectly 0 [19]
+WINE_bar correctly 3 at or_broken@ERROR: WINE_bar incorrectly [3]
+WINE_baz correctly 4
--- quotes
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_bar correctly 2
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_bar correctly 2
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_bar correctly 2
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_bar correctly 2
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_bar correctly 2
- at todo_wine@WINE_baz correctly 1
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_bar correctly 2
- at todo_wine@WINE_baz correctly 1
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_bar correctly 2
- at todo_wine@WINE_baz correctly 1
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_bar correctly 2
- at todo_wine@WINE_baz correctly 1
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_bar correctly 2
- at todo_wine@WINE_baz correctly 1
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_bar correctly 5
- at todo_wine@WINE_baz correctly 1
- at todo_wine@WINE_foo correctly 18
+WINE_foo correctly 1
+WINE_foo correctly 1
+WINE_foo correctly 1
+WINE_bar correctly 2
+WINE_foo correctly 1
+WINE_bar correctly 2
+WINE_foo correctly 1
+WINE_bar correctly 2
+WINE_foo correctly 1
+WINE_bar correctly 2
+WINE_foo correctly 1
+WINE_bar correctly 2
+WINE_baz correctly 3
+WINE_foo correctly 1
+WINE_bar correctly 2
+WINE_baz correctly 3
+WINE_foo correctly 1
+WINE_bar correctly 2
+WINE_baz correctly 3
+WINE_foo correctly 1
+WINE_bar correctly 2
+WINE_baz correctly 3
+WINE_foo correctly 1
+WINE_bar correctly 2
+WINE_baz correctly 3
+WINE_foo correctly 1
+WINE_foo correctly 1
+WINE_bar correctly 5
+WINE_baz correctly 2
+WINE_foo correctly 18
WINE_foo correctly 3
- at todo_wine@WINE_bar correctly 7 at or_broken@ERROR: WINE_bar incorrectly 4 [7]
+WINE_bar correctly 7 at or_broken@ERROR: WINE_bar incorrectly 4 [7]
--- whitespace are ignored between double char operators
- at todo_wine@WINE_foo correctly 10 at or_broken@ERROR: WINE_foo incorrectly 4 [10]
- at todo_wine@WINE_bar correctly 50 at or_broken@ERROR: WINE_bar incorrectly 5 [50]
- at todo_wine@WINE_foo correctly 772 at or_broken@ERROR: WINE_foo incorrectly 4 [772]
- at todo_wine@WINE_bar correctly 7720 at or_broken@ERROR: WINE_bar incorrectly 5 [7720]
+WINE_foo correctly 10 at or_broken@ERROR: WINE_foo incorrectly 4 [10]
+WINE_bar correctly 50 at or_broken@ERROR: WINE_bar incorrectly 5 [50]
+WINE_foo correctly 772 at or_broken@ERROR: WINE_foo incorrectly 4 [772]
+WINE_bar correctly 7720 at or_broken@ERROR: WINE_bar incorrectly 5 [7720]
--- invalid operator sequence
4
4
----- negative prefix
- at todo_wine@WINE_foo correctly -1
- at todo_wine@WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly [1]
- at todo_wine@WINE_foo correctly 6
- at todo_wine@WINE_foo correctly 0
- at todo_wine@WINE_foo correctly 6
- at todo_wine@WINE_foo correctly 2
- at todo_wine@WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly [0]
- at todo_wine@WINE_foo correctly 5
- at todo_wine@WINE_foo correctly 3 at or_broken@ERROR: WINE_foo incorrectly -5 [3]
+WINE_foo correctly -1
+WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly [1]
+WINE_foo correctly 6
+WINE_foo correctly 0
+WINE_foo correctly 6
+WINE_foo correctly 2
+WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly [0]
+WINE_foo correctly 5
+WINE_foo correctly 3 at or_broken@ERROR: WINE_foo incorrectly -5 [3]
----- assignment tests involving the end destination
- at todo_wine@WINE_foo correctly 11 at or_broken@ERROR: WINE_foo incorrectly 3 [11]
- at todo_wine@WINE_foo correctly 6 at or_broken@ERROR: WINE_foo incorrectly 2 [6]
- at todo_wine@WINE_bar correctly 9 at or_broken@ERROR: WINE_bar incorrectly [9]
- at todo_wine@WINE_foo correctly 6 at or_broken@ERROR: WINE_foo incorrectly 2 [6]
- at todo_wine@WINE_bar correctly 10 at or_broken@ERROR: WINE_bar incorrectly [10]
- at todo_wine@WINE_baz correctly 6 at or_broken@ERROR: WINE_baz incorrectly [7]
- at todo_wine@WINE_foo correctly 7 at or_broken@ERROR: WINE_foo incorrectly 2 [7]
- at todo_wine@WINE_bar correctly 7 at or_broken@ERROR: WINE_bar incorrectly 2 [7]
+WINE_foo correctly 11 at or_broken@ERROR: WINE_foo incorrectly 3 [11]
+WINE_foo correctly 6 at or_broken@ERROR: WINE_foo incorrectly 2 [6]
+WINE_bar correctly 9 at or_broken@ERROR: WINE_bar incorrectly [9]
+WINE_foo correctly 6 at or_broken@ERROR: WINE_foo incorrectly 2 [6]
+WINE_bar correctly 10 at or_broken@ERROR: WINE_bar incorrectly [10]
+WINE_baz correctly 7 at or_broken@ERROR: WINE_baz incorrectly [7]
+WINE_foo correctly 7 at or_broken@ERROR: WINE_foo incorrectly 2 [7]
+WINE_bar correctly 7 at or_broken@ERROR: WINE_bar incorrectly 2 [7]
----- equal precedence on stack
- at todo_wine@WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly 0 [1]
- at todo_wine@WINE_foo correctly 0
- at todo_wine@WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly [1]
- at todo_wine@WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly [0]
- at todo_wine@WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly [1]
- at todo_wine@WINE_foo correctly -1 at or_broken@ERROR: WINE_foo incorrectly [-1]
- at todo_wine@WINE_foo correctly -1 at or_broken@ERROR: WINE_foo incorrectly [-1]
- at todo_wine@WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly [1]
- at todo_wine@WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly [0]
- at todo_wine@WINE_foo correctly -1
- at todo_wine@WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly -1 [0]
- at todo_wine@WINE_foo correctly 0
- at todo_wine@WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly 0 [1]
- at todo_wine@WINE_foo correctly -1 at or_broken@ERROR: WINE_foo incorrectly 0 [-1]
- at todo_wine@WINE_foo correctly 25 at or_broken@ERROR: WINE_foo incorrectly 20 [25]
- at todo_wine@WINE_foo correctly 20
- at todo_wine@WINE_foo correctly 1
- at todo_wine@WINE_foo correctly 4
- at todo_wine@WINE_foo correctly 7
- at todo_wine@WINE_foo correctly 8 at or_broken@ERROR: WINE_foo incorrectly 4 [8]
- at todo_wine@WINE_foo correctly 6 at or_broken@ERROR: WINE_foo incorrectly 5 [6]
- at todo_wine@WINE_bar correctly 6 at or_broken@ERROR: WINE_bar incorrectly 5 [6]
+WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly 0 [1]
+WINE_foo correctly 0
+WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly [1]
+WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly [0]
+WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly [1]
+WINE_foo correctly -1 at or_broken@ERROR: WINE_foo incorrectly [-1]
+WINE_foo correctly -1 at or_broken@ERROR: WINE_foo incorrectly [-1]
+WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly [1]
+WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly [0]
+WINE_foo correctly -1
+WINE_foo correctly 0 at or_broken@ERROR: WINE_foo incorrectly -1 [0]
+WINE_foo correctly 0
+WINE_foo correctly 1 at or_broken@ERROR: WINE_foo incorrectly 0 [1]
+WINE_foo correctly -1 at or_broken@ERROR: WINE_foo incorrectly 0 [-1]
+WINE_foo correctly 25 at or_broken@ERROR: WINE_foo incorrectly 20 [25]
+WINE_foo correctly 20
+WINE_foo correctly 1
+WINE_foo correctly 4
+WINE_foo correctly 7
+WINE_foo correctly 8 at or_broken@ERROR: WINE_foo incorrectly 4 [8]
+WINE_foo correctly 6 at or_broken@ERROR: WINE_foo incorrectly 5 [6]
+WINE_bar correctly 6 at or_broken@ERROR: WINE_bar incorrectly 5 [6]
--- for /F
------ string argument
a
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h
index 3cc4c0b..278a578 100644
--- a/programs/cmd/wcmd.h
+++ b/programs/cmd/wcmd.h
@@ -317,3 +317,8 @@ extern WCHAR version_string[];
#define WCMD_YESNO 1038
#define WCMD_YESNOALL 1039
#define WCMD_NO_COMMAND_FOUND 1040
+#define WCMD_DIVIDEBYZERO 1041
+#define WCMD_NOOPERAND 1042
+#define WCMD_NOOPERATOR 1043
+#define WCMD_BADPAREN 1044
+#define WCMD_BADHEXOCT 1045
--
1.7.9.5
More information about the wine-patches
mailing list