mirror of
https://github.com/reactos/reactos.git
synced 2025-02-20 15:35:04 +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
1 changed files with 147 additions and 24 deletions
|
@ -112,14 +112,22 @@ INT cmd_set(LPTSTR param)
|
|||
if (!_tcsnicmp(param, _T("/A"), 2))
|
||||
{
|
||||
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);
|
||||
Success = seta_eval(skip_ws(param + 2));
|
||||
if (!Success)
|
||||
{
|
||||
#if 0
|
||||
/* Might seem random but this is what windows xp does -- This is a message ID */
|
||||
retval = 9165;
|
||||
#endif
|
||||
retval = nErrorLevel;
|
||||
nErrorLevel = nOldErrorLevel;
|
||||
}
|
||||
// return !Success;
|
||||
else
|
||||
{
|
||||
retval = 0;
|
||||
|
@ -287,12 +295,33 @@ calc(INT* lval, TCHAR op, INT rval)
|
|||
case '*':
|
||||
*lval *= rval;
|
||||
break;
|
||||
|
||||
case '/':
|
||||
{
|
||||
if (rval == 0)
|
||||
{
|
||||
// FIXME: Localize
|
||||
ConErrPuts(_T("Division by zero error.\n"));
|
||||
nErrorLevel = 0x400023D1; // 1073750993;
|
||||
return FALSE;
|
||||
}
|
||||
*lval /= rval;
|
||||
break;
|
||||
}
|
||||
|
||||
case '%':
|
||||
{
|
||||
if (rval == 0)
|
||||
{
|
||||
// FIXME: Localize
|
||||
ConErrPuts(_T("Division by zero error.\n"));
|
||||
nErrorLevel = 0x400023D1; // 1073750993;
|
||||
return FALSE;
|
||||
}
|
||||
*lval %= rval;
|
||||
break;
|
||||
}
|
||||
|
||||
case '+':
|
||||
*lval += rval;
|
||||
break;
|
||||
|
@ -308,8 +337,10 @@ calc(INT* lval, TCHAR op, INT rval)
|
|||
case '|':
|
||||
*lval |= rval;
|
||||
break;
|
||||
|
||||
default:
|
||||
ConErrResPuts(STRING_INVALID_OPERAND);
|
||||
nErrorLevel = 0x400023CE; // 1073750990;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
|
@ -322,23 +353,47 @@ static BOOL
|
|||
seta_unaryTerm(LPCTSTR* p_, INT* result)
|
||||
{
|
||||
LPCTSTR p = *p_;
|
||||
INT rval;
|
||||
|
||||
if (*p == _T('('))
|
||||
{
|
||||
INT rval;
|
||||
p = skip_ws(p + 1);
|
||||
if (!seta_stmt(&p, &rval))
|
||||
return FALSE;
|
||||
if (*p++ != _T(')'))
|
||||
{
|
||||
ConErrResPuts(STRING_EXPECTED_CLOSE_PAREN);
|
||||
nErrorLevel = 0x400023CC; // 1073750988;
|
||||
return FALSE;
|
||||
}
|
||||
*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))
|
||||
{
|
||||
|
@ -350,6 +405,7 @@ seta_unaryTerm(LPCTSTR* p_, INT* result)
|
|||
else
|
||||
{
|
||||
ConErrResPuts(STRING_EXPECTED_NUMBER_OR_VARIABLE);
|
||||
nErrorLevel = 0x400023CD; // 1073750989;
|
||||
return FALSE;
|
||||
}
|
||||
*p_ = skip_ws(p);
|
||||
|
@ -363,24 +419,36 @@ seta_mulTerm(LPCTSTR* p_, INT* result)
|
|||
TCHAR op = 0;
|
||||
INT rval;
|
||||
|
||||
if (_tcschr(_T("!~-"), *p))
|
||||
if (_tcschr(_T("!~-+"), *p))
|
||||
{
|
||||
op = *p;
|
||||
p = skip_ws(p + 1);
|
||||
|
||||
if (!seta_mulTerm(&p, &rval))
|
||||
return FALSE;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case '!':
|
||||
rval = !rval;
|
||||
break;
|
||||
case '~':
|
||||
rval = ~rval;
|
||||
break;
|
||||
case '-':
|
||||
rval = -rval;
|
||||
break;
|
||||
#if 0
|
||||
case '+':
|
||||
rval = rval;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (!seta_unaryTerm(&p, &rval))
|
||||
return FALSE;
|
||||
switch (op)
|
||||
else
|
||||
{
|
||||
case '!':
|
||||
rval = !rval;
|
||||
break;
|
||||
case '~':
|
||||
rval = ~rval;
|
||||
break;
|
||||
case '-':
|
||||
rval = -rval;
|
||||
break;
|
||||
if (!seta_unaryTerm(&p, &rval))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*result = rval;
|
||||
|
@ -442,12 +510,18 @@ seta_bitAndTerm(LPCTSTR* p_, INT* result)
|
|||
return FALSE;
|
||||
|
||||
/* Handle << >> operators */
|
||||
while (*p && _tcschr(_T("<>"), *p) && p[0] == p[1])
|
||||
while (*p && _tcschr(_T("<>"), *p))
|
||||
{
|
||||
INT rval;
|
||||
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 */
|
||||
if (!seta_logShiftTerm(&p, &rval))
|
||||
|
@ -473,6 +547,7 @@ seta_bitAndTerm(LPCTSTR* p_, INT* result)
|
|||
|
||||
default:
|
||||
ConErrResPuts(STRING_INVALID_OPERAND);
|
||||
nErrorLevel = 0x400023CE; // 1073750990;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
@ -512,17 +587,56 @@ seta_assignment(LPCTSTR* p_, INT* result)
|
|||
if (identlen)
|
||||
{
|
||||
p = skip_ws(p);
|
||||
|
||||
/* Handle = assignment */
|
||||
if (*p == _T('='))
|
||||
op = *p, p = skip_ws(p + 1);
|
||||
{
|
||||
op = *p;
|
||||
p = skip_ws(p + 1);
|
||||
}
|
||||
/* Handle *= /= %= += -= &= ^= |= assignments */
|
||||
else if (_tcschr(_T("*/%+-&^|"), *p) && p[1] == _T('='))
|
||||
op = *p, p = skip_ws(p + 2);
|
||||
else if (_tcschr(_T("*/%+-&^|"), *p))
|
||||
{
|
||||
op = *p;
|
||||
|
||||
/* 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) && *p == p[1] && p[2] == _T('='))
|
||||
op = *p, p = skip_ws(p + 3);
|
||||
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 */
|
||||
if (ident && op)
|
||||
{
|
||||
|
@ -613,11 +727,20 @@ seta_eval(LPCTSTR p)
|
|||
if (!*p)
|
||||
{
|
||||
ConErrResPuts(STRING_SYNTAX_COMMAND_INCORRECT);
|
||||
nErrorLevel = 1;
|
||||
return FALSE;
|
||||
}
|
||||
if (!seta_stmt(&p, &rval))
|
||||
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 */
|
||||
if (!bc)
|
||||
ConOutPrintf(_T("%i"), rval);
|
||||
|
|
Loading…
Reference in a new issue