mirror of
https://github.com/reactos/reactos.git
synced 2024-09-28 05:26:58 +00:00
[CMD] SET: Diverse fixes for the arithmetic-expression parser (/A option).
- Detect whether a division by zero is done, and fail if so. - Detect whether an invalid number is provided: * If _tcstol() fails with errno == ERANGE, we've got an overflow or underflow. * If the next character where _tcstol() is not a whitespace but is a character compatible with the first character of an identifier, the number is invalid. - Add + to the list of existing unary operators (!,~,-), and parse them where many of these are present. Indeed, expressions like: +3, -+-+3, !!-+3 (or with other unary ops, etc.) are valid. - Operators constituted of more than one characters, can contain whitespace separating their constituting characters. Thus, "a + = 3" is equivalent to "a += 3" (and the same for -=, *=, /=, %=, &=, |= and ^=), and "a < < 3" is equivalent to "a << 3" (and the same for >>, <<= and >>=). - After evaluating everything, if unparsed data remains, fail and bail out. - Return Windows' CMD-compatible errorlevels. See https://ss64.com/nt/set.html for more details. Fixes some cmd_winetests.
This commit is contained in:
parent
f5cf67f455
commit
17e094cd34
|
@ -112,14 +112,22 @@ INT cmd_set(LPTSTR param)
|
||||||
if (!_tcsnicmp(param, _T("/A"), 2))
|
if (!_tcsnicmp(param, _T("/A"), 2))
|
||||||
{
|
{
|
||||||
BOOL Success;
|
BOOL Success;
|
||||||
|
|
||||||
|
/* Save error level since seta_eval() modifies it, as
|
||||||
|
* we need to set it later according to specific rules. */
|
||||||
|
INT nOldErrorLevel = nErrorLevel;
|
||||||
|
|
||||||
StripQuotes(param);
|
StripQuotes(param);
|
||||||
Success = seta_eval(skip_ws(param + 2));
|
Success = seta_eval(skip_ws(param + 2));
|
||||||
if (!Success)
|
if (!Success)
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
/* Might seem random but this is what windows xp does -- This is a message ID */
|
/* Might seem random but this is what windows xp does -- This is a message ID */
|
||||||
retval = 9165;
|
retval = 9165;
|
||||||
|
#endif
|
||||||
|
retval = nErrorLevel;
|
||||||
|
nErrorLevel = nOldErrorLevel;
|
||||||
}
|
}
|
||||||
// return !Success;
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
@ -287,12 +295,33 @@ calc(INT* lval, TCHAR op, INT rval)
|
||||||
case '*':
|
case '*':
|
||||||
*lval *= rval;
|
*lval *= rval;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '/':
|
case '/':
|
||||||
|
{
|
||||||
|
if (rval == 0)
|
||||||
|
{
|
||||||
|
// FIXME: Localize
|
||||||
|
ConErrPuts(_T("Division by zero error.\n"));
|
||||||
|
nErrorLevel = 0x400023D1; // 1073750993;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
*lval /= rval;
|
*lval /= rval;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case '%':
|
case '%':
|
||||||
|
{
|
||||||
|
if (rval == 0)
|
||||||
|
{
|
||||||
|
// FIXME: Localize
|
||||||
|
ConErrPuts(_T("Division by zero error.\n"));
|
||||||
|
nErrorLevel = 0x400023D1; // 1073750993;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
*lval %= rval;
|
*lval %= rval;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case '+':
|
case '+':
|
||||||
*lval += rval;
|
*lval += rval;
|
||||||
break;
|
break;
|
||||||
|
@ -308,8 +337,10 @@ calc(INT* lval, TCHAR op, INT rval)
|
||||||
case '|':
|
case '|':
|
||||||
*lval |= rval;
|
*lval |= rval;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ConErrResPuts(STRING_INVALID_OPERAND);
|
ConErrResPuts(STRING_INVALID_OPERAND);
|
||||||
|
nErrorLevel = 0x400023CE; // 1073750990;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -322,23 +353,47 @@ static BOOL
|
||||||
seta_unaryTerm(LPCTSTR* p_, INT* result)
|
seta_unaryTerm(LPCTSTR* p_, INT* result)
|
||||||
{
|
{
|
||||||
LPCTSTR p = *p_;
|
LPCTSTR p = *p_;
|
||||||
|
INT rval;
|
||||||
|
|
||||||
if (*p == _T('('))
|
if (*p == _T('('))
|
||||||
{
|
{
|
||||||
INT rval;
|
|
||||||
p = skip_ws(p + 1);
|
p = skip_ws(p + 1);
|
||||||
if (!seta_stmt(&p, &rval))
|
if (!seta_stmt(&p, &rval))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
if (*p++ != _T(')'))
|
if (*p++ != _T(')'))
|
||||||
{
|
{
|
||||||
ConErrResPuts(STRING_EXPECTED_CLOSE_PAREN);
|
ConErrResPuts(STRING_EXPECTED_CLOSE_PAREN);
|
||||||
|
nErrorLevel = 0x400023CC; // 1073750988;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
*result = rval;
|
*result = rval;
|
||||||
}
|
}
|
||||||
else if (isdigit(*p))
|
else if (_istdigit(*p))
|
||||||
{
|
{
|
||||||
*result = _tcstol(p, (LPTSTR*)&p, 0);
|
errno = 0;
|
||||||
|
rval = _tcstol(p, (LPTSTR*)&p, 0);
|
||||||
|
|
||||||
|
/* Check for overflow / underflow */
|
||||||
|
if (errno == ERANGE)
|
||||||
|
{
|
||||||
|
// FIXME: Localize
|
||||||
|
ConErrPuts(_T("Invalid number. Numbers are limited to 32-bits of precision.\n"));
|
||||||
|
nErrorLevel = 0x400023D0; // 1073750992;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* _tcstol() stopped at the first non-digit character. If it's not a whitespace,
|
||||||
|
* or if it's the start of a possible identifier, this means the number being
|
||||||
|
* interpreted was invalid.
|
||||||
|
*/
|
||||||
|
else if (*p && !_istspace(*p) && __iscsymf(*p))
|
||||||
|
{
|
||||||
|
// FIXME: Localize
|
||||||
|
ConErrPuts(_T("Invalid number. Numeric constants are either decimal (42), hexadecimal (0x2A), or octal (052).\n"));
|
||||||
|
nErrorLevel = 0x400023CF; // 1073750991;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
*result = rval;
|
||||||
}
|
}
|
||||||
else if (__iscsymf(*p))
|
else if (__iscsymf(*p))
|
||||||
{
|
{
|
||||||
|
@ -350,6 +405,7 @@ seta_unaryTerm(LPCTSTR* p_, INT* result)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ConErrResPuts(STRING_EXPECTED_NUMBER_OR_VARIABLE);
|
ConErrResPuts(STRING_EXPECTED_NUMBER_OR_VARIABLE);
|
||||||
|
nErrorLevel = 0x400023CD; // 1073750989;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
*p_ = skip_ws(p);
|
*p_ = skip_ws(p);
|
||||||
|
@ -363,13 +419,14 @@ seta_mulTerm(LPCTSTR* p_, INT* result)
|
||||||
TCHAR op = 0;
|
TCHAR op = 0;
|
||||||
INT rval;
|
INT rval;
|
||||||
|
|
||||||
if (_tcschr(_T("!~-"), *p))
|
if (_tcschr(_T("!~-+"), *p))
|
||||||
{
|
{
|
||||||
op = *p;
|
op = *p;
|
||||||
p = skip_ws(p + 1);
|
p = skip_ws(p + 1);
|
||||||
}
|
|
||||||
if (!seta_unaryTerm(&p, &rval))
|
if (!seta_mulTerm(&p, &rval))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
case '!':
|
case '!':
|
||||||
|
@ -381,6 +438,17 @@ seta_mulTerm(LPCTSTR* p_, INT* result)
|
||||||
case '-':
|
case '-':
|
||||||
rval = -rval;
|
rval = -rval;
|
||||||
break;
|
break;
|
||||||
|
#if 0
|
||||||
|
case '+':
|
||||||
|
rval = rval;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!seta_unaryTerm(&p, &rval))
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
*result = rval;
|
*result = rval;
|
||||||
|
@ -442,12 +510,18 @@ seta_bitAndTerm(LPCTSTR* p_, INT* result)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
/* Handle << >> operators */
|
/* Handle << >> operators */
|
||||||
while (*p && _tcschr(_T("<>"), *p) && p[0] == p[1])
|
while (*p && _tcschr(_T("<>"), *p))
|
||||||
{
|
{
|
||||||
INT rval;
|
INT rval;
|
||||||
TCHAR op = *p;
|
TCHAR op = *p;
|
||||||
|
|
||||||
p = skip_ws(p + 2);
|
/* Check whether the next non-whitespace character is the same operator */
|
||||||
|
p = skip_ws(p + 1);
|
||||||
|
if (*p != op)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Skip it */
|
||||||
|
p = skip_ws(p + 1);
|
||||||
|
|
||||||
/* Evaluate the immediate right-hand side */
|
/* Evaluate the immediate right-hand side */
|
||||||
if (!seta_logShiftTerm(&p, &rval))
|
if (!seta_logShiftTerm(&p, &rval))
|
||||||
|
@ -473,6 +547,7 @@ seta_bitAndTerm(LPCTSTR* p_, INT* result)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ConErrResPuts(STRING_INVALID_OPERAND);
|
ConErrResPuts(STRING_INVALID_OPERAND);
|
||||||
|
nErrorLevel = 0x400023CE; // 1073750990;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -512,17 +587,56 @@ seta_assignment(LPCTSTR* p_, INT* result)
|
||||||
if (identlen)
|
if (identlen)
|
||||||
{
|
{
|
||||||
p = skip_ws(p);
|
p = skip_ws(p);
|
||||||
|
|
||||||
/* Handle = assignment */
|
/* Handle = assignment */
|
||||||
if (*p == _T('='))
|
if (*p == _T('='))
|
||||||
op = *p, p = skip_ws(p + 1);
|
{
|
||||||
|
op = *p;
|
||||||
|
p = skip_ws(p + 1);
|
||||||
|
}
|
||||||
/* Handle *= /= %= += -= &= ^= |= assignments */
|
/* Handle *= /= %= += -= &= ^= |= assignments */
|
||||||
else if (_tcschr(_T("*/%+-&^|"), *p) && p[1] == _T('='))
|
else if (_tcschr(_T("*/%+-&^|"), *p))
|
||||||
op = *p, p = skip_ws(p + 2);
|
{
|
||||||
/* Handle <<= >>= assignments */
|
op = *p;
|
||||||
else if (_tcschr(_T("<>"), *p) && *p == p[1] && p[2] == _T('='))
|
|
||||||
op = *p, p = skip_ws(p + 3);
|
/* Find the '=', there may be some spaces before it */
|
||||||
|
p = skip_ws(p + 1);
|
||||||
|
if (*p != _T('='))
|
||||||
|
{
|
||||||
|
op = 0;
|
||||||
|
goto evaluate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Skip it */
|
||||||
|
p = skip_ws(p + 1);
|
||||||
|
}
|
||||||
|
/* Handle <<= >>= assignments */
|
||||||
|
else if (_tcschr(_T("<>"), *p))
|
||||||
|
{
|
||||||
|
op = *p;
|
||||||
|
|
||||||
|
/* Check whether the next non-whitespace character is the same operator */
|
||||||
|
p = skip_ws(p + 1);
|
||||||
|
if (*p != op)
|
||||||
|
{
|
||||||
|
op = 0;
|
||||||
|
goto evaluate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the '=', there may be some spaces before it */
|
||||||
|
p = skip_ws(p + 1);
|
||||||
|
if (*p != _T('='))
|
||||||
|
{
|
||||||
|
op = 0;
|
||||||
|
goto evaluate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip it */
|
||||||
|
p = skip_ws(p + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluate:
|
||||||
/* Allow to chain multiple assignments, such as: a=b=1 */
|
/* Allow to chain multiple assignments, such as: a=b=1 */
|
||||||
if (ident && op)
|
if (ident && op)
|
||||||
{
|
{
|
||||||
|
@ -613,11 +727,20 @@ seta_eval(LPCTSTR p)
|
||||||
if (!*p)
|
if (!*p)
|
||||||
{
|
{
|
||||||
ConErrResPuts(STRING_SYNTAX_COMMAND_INCORRECT);
|
ConErrResPuts(STRING_SYNTAX_COMMAND_INCORRECT);
|
||||||
|
nErrorLevel = 1;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
if (!seta_stmt(&p, &rval))
|
if (!seta_stmt(&p, &rval))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
/* If unparsed data remains, fail and bail out */
|
||||||
|
if (*p)
|
||||||
|
{
|
||||||
|
ConErrResPuts(STRING_SYNTAX_COMMAND_INCORRECT); // Actually syntax error / missing operand.
|
||||||
|
nErrorLevel = 0x400023CE; // 1073750990;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Echo the result of the evaluation only in interactive (non-batch) mode */
|
/* Echo the result of the evaluation only in interactive (non-batch) mode */
|
||||||
if (!bc)
|
if (!bc)
|
||||||
ConOutPrintf(_T("%i"), rval);
|
ConOutPrintf(_T("%i"), rval);
|
||||||
|
|
Loading…
Reference in a new issue