1) strip ^ escapes from text before processing set command
2) recursive descent implementation of set /a - should fix bug 728
(In case anyone checks I realized there were some gaping holes in the
implementation I posted to the ml, so this one is modified from it. This
code could stand to benefit from a comprehensive feature/regression
test.)
Modified: trunk/reactos/subsys/system/cmd/set.c
_____
Modified: trunk/reactos/subsys/system/cmd/set.c
--- trunk/reactos/subsys/system/cmd/set.c 2005-09-18 03:26:52 UTC
(rev 17905)
+++ trunk/reactos/subsys/system/cmd/set.c 2005-09-18 03:52:34 UTC
(rev 17906)
@@ -35,6 +35,8 @@
*/
#include <precomp.h>
+#include <malloc.h>
+#include <stdio.h>
#include "resource.h"
#ifdef INCLUDE_CMD_SET
@@ -43,18 +45,35 @@
/* initial size of environment variable buffer */
#define ENV_BUFFER_SIZE 1024
+static BOOL
+seta_eval ( LPCTSTR expr );
+static LPCTSTR
+skip_ws ( LPCTSTR p )
+{
+ return p + strspn ( p, " \t" );
+}
+
INT cmd_set (LPTSTR cmd, LPTSTR param)
{
TCHAR szMsg[RC_STRING_MAX_SIZE];
+ INT i;
LPTSTR p;
- if (!_tcsncmp (param, _T("/?"), 2))
+ if ( !_tcsncmp (param, _T("/?"), 2) )
{
ConOutResPaging(TRUE,STRING_SET_HELP);
return 0;
}
+ /* remove escapes */
+ if ( param[0] ) for ( i = 0; param[i+1]; i++ )
+ {
+ if ( param[i] == '^' && strchr("<|>",param[i+1]) )
+ {
+ memmove ( ¶m[i], ¶m[i+1],
_tcslen(¶m[i]) * sizeof(TCHAR) );
+ }
+ }
/* if no parameters, show the environment */
if (param[0] == _T('\0'))
@@ -83,6 +102,12 @@
return 0;
}
+ if ( !_tcsnicmp (param, _T("/A"), 2) )
+ {
+ // TODO FIXME - what are we supposed to return?
+ return seta_eval ( skip_ws(param+2) );
+ }
+
p = _tcschr (param, _T('='));
if (p)
{
@@ -124,4 +149,404 @@
return 0;
}
+static INT
+ident_len ( LPCTSTR p )
+{
+ LPCTSTR p2 = p;
+ if ( __iscsymf(*p) )
+ {
+ ++p2;
+ while ( __iscsym(*p2) )
+ ++p2;
+ }
+ return p2-p;
+}
+
+#define PARSE_IDENT(ident,identlen,p) \
+ identlen = ident_len(p); \
+ ident = (LPTSTR)alloca ( ( identlen + 1 ) * sizeof(TCHAR) ); \
+ memmove ( ident, p, identlen * sizeof(TCHAR) ); \
+ ident[identlen] = 0; \
+ p += identlen;
+
+static BOOL
+seta_identval ( LPCTSTR ident, INT* result )
+{
+ // get size of buffer for env var
+ LPTSTR buf;
+ DWORD dwBuffer = GetEnvironmentVariable ( ident, NULL, 0 );
+ // if GetEnvironmentVariable() fails, it returns 0
+ if ( !dwBuffer )
+ {
+ // TODO FIXME - is it correct to report a value of 0 for
non-existant variables?
+ *result = 0;
+ return FALSE;
+ }
+ buf = (LPTSTR)alloca ( dwBuffer * sizeof(TCHAR) );
+ if ( !buf )
+ {
+ // TODO FIXME - is it correct to report 0 when report
resources low... should we error somehow?
+ *result = 0;
+ return FALSE;
+ }
+ GetEnvironmentVariable ( ident, buf, dwBuffer );
+ *result = atoi ( buf );
+ return TRUE;
+}
+
+static BOOL
+calc ( INT* lval, TCHAR op, INT rval )
+{
+ switch ( op )
+ {
+ case '*':
+ *lval *= rval;
+ break;
+ case '/':
+ *lval /= rval;
+ break;
+ case '%':
+ *lval %= rval;
+ break;
+ case '+':
+ *lval += rval;
+ break;
+ case '-':
+ *lval -= rval;
+ break;
+ case '&':
+ *lval &= rval;
+ break;
+ case '^':
+ *lval ^= rval;
+ break;
+ case '|':
+ *lval |= rval;
+ break;
+ default:
+ printf ( "Invalid operand.\n" );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static BOOL
+seta_stmt ( LPCTSTR* p_, INT* result );
+
+static BOOL
+seta_unaryTerm ( LPCTSTR* p_, INT* result )
+{
+ LPCTSTR p = *p_;
+ if ( *p == '(' )
+ {
+ INT rval;
+ p = skip_ws ( p + 1 );
+ if ( !seta_stmt ( &p, &rval ) )
+ return FALSE;
+ if ( *p != ')' )
+ {
+ _tprintf ( _T("Expected ')'\n") );
+ return FALSE;
+ }
+ *result = rval;
+ p = skip_ws ( p + 1 );
+ }
+ else if ( isdigit(*p) )
+ {
+ *result = atoi ( p );
+ p = skip_ws ( p + strspn ( p, "1234567890" ) );
+ }
+ else if ( __iscsymf(*p) )
+ {
+ LPTSTR ident;
+ INT identlen;
+ PARSE_IDENT(ident,identlen,p);
+ if ( !seta_identval ( ident, result ) )
+ return FALSE;
+ }
+ else
+ {
+ _tprintf ( _T("Expected number or variable name\n") );
+ return FALSE;
+ }
+ *p_ = p;
+ return TRUE;
+}
+
+static BOOL
+seta_mulTerm ( LPCTSTR* p_, INT* result )
+{
+ LPCTSTR p = *p_;
+ TCHAR op = 0;
+ INT rval;
+ if ( _tcschr(_T("!~-"),*p) )
+ {
+ op = *p;
+ p = skip_ws ( p + 1 );
+ }
+ if ( !seta_unaryTerm ( &p, &rval ) )
+ return FALSE;
+ switch ( op )
+ {
+ case '!':
+ rval = !rval;
+ break;
+ case '~':
+ rval = ~rval;
+ break;
+ case '-':
+ rval = -rval;
+ break;
+ }
+
+ *result = rval;
+ *p_ = p;
+ return TRUE;
+}
+
+static BOOL
+seta_addTerm ( LPCTSTR* p_, INT* result )
+{
+ LPCTSTR p = *p_;
+ INT lval;
+ if ( !seta_mulTerm ( &p, &lval ) )
+ return FALSE;
+ while ( *p && _tcschr(_T("*/%"),*p) )
+ {
+ INT rval;
+ TCHAR op = *p;
+
+ p = skip_ws ( p+1 );
+
+ if ( !seta_mulTerm ( &p, &rval ) )
+ return FALSE;
+
+ if ( !calc ( &lval, op, rval ) )
+ return FALSE;
+ }
+
+ *result = lval;
+ *p_ = p;
+ return TRUE;
+}
+
+static BOOL
+seta_logShiftTerm ( LPCTSTR* p_, INT* result )
+{
+ LPCTSTR p = *p_;
+ INT lval;
+ if ( !seta_addTerm ( &p, &lval ) )
+ return FALSE;
+ while ( *p && _tcschr(_T("+-"),*p) )
+ {
+ INT rval;
+ TCHAR op = *p;
+
+ p = skip_ws ( p+1 );
+
+ if ( !seta_addTerm ( &p, &rval ) )
+ return FALSE;
+
+ if ( !calc ( &lval, op, rval ) )
+ return FALSE;
+ }
+
+ *result = lval;
+ *p_ = p;
+ return TRUE;
+}
+
+static BOOL
+seta_bitAndTerm ( LPCTSTR* p_, INT* result )
+{
+ LPCTSTR p = *p_;
+ INT lval;
+ if ( !seta_logShiftTerm ( &p, &lval ) )
+ return FALSE;
+ while ( *p && _tcschr(_T("<>"),*p) && p[0] == p[1] )
+ {
+ INT rval;
+ TCHAR op = *p;
+
+ p = skip_ws ( p+2 );
+
+ if ( !seta_logShiftTerm ( &p, &rval ) )
+ return FALSE;
+
+ if ( !calc ( &lval, op, rval ) )
+ return FALSE;
+ }
+
+ *result = lval;
+ *p_ = p;
+ return TRUE;
+}
+
+static BOOL
+seta_bitExclOrTerm ( LPCTSTR* p_, INT* result )
+{
+ LPCTSTR p = *p_;
+ INT lval;
+ if ( !seta_bitAndTerm ( &p, &lval ) )
+ return FALSE;
+ while ( *p == _T('&') )
+ {
+ INT rval;
+
+ p = skip_ws ( p+1 );
+
+ if ( !seta_bitAndTerm ( &p, &rval ) )
+ return FALSE;
+
+ lval &= rval;
+ }
+
+ *result = lval;
+ *p_ = p;
+ return TRUE;
+}
+
+static BOOL
+seta_bitOrTerm ( LPCTSTR* p_, INT* result )
+{
+ LPCTSTR p = *p_;
+ INT lval;
+ if ( !seta_bitExclOrTerm ( &p, &lval ) )
+ return FALSE;
+ while ( *p == _T('^') )
+ {
+ INT rval;
+
+ p = skip_ws ( p+1 );
+
+ if ( !seta_bitExclOrTerm ( &p, &rval ) )
+ return FALSE;
+
+ lval ^= rval;
+ }
+
+ *result = lval;
+ *p_ = p;
+ return TRUE;
+}
+
+static BOOL
+seta_expr ( LPCTSTR* p_, INT* result )
+{
+ LPCTSTR p = *p_;
+ INT lval;
+ if ( !seta_bitOrTerm ( &p, &lval ) )
+ return FALSE;
+ while ( *p == _T('|') )
+ {
+ INT rval;
+
+ p = skip_ws ( p+1 );
+
+ if ( !seta_bitOrTerm ( &p, &rval ) )
+ return FALSE;
+
+ lval |= rval;
+ }
+
+ *result = lval;
+ *p_ = p;
+ return TRUE;
+}
+
+static BOOL
+seta_assignment ( LPCTSTR* p_, INT* result )
+{
+ LPCTSTR p = *p_;
+ LPTSTR ident;
+ TCHAR op = 0;
+ INT identlen, exprval;
+
+ PARSE_IDENT(ident,identlen,p);
+ if ( identlen )
+ {
+ if ( *p == _T('=') )
+ op = *p, p++;
+ else if ( _tcschr ( _T("*/%+-&^|"), *p ) && p[1] ==
_T('=') )
+ op = *p, p += 2;
+ else if ( _tcschr ( _T("<>"), *p ) && *p == p[1] &&
p[2]
== _T('=') )
+ op = *p, p += 3;
+ else
+ {
+ _tprintf ( _T("Missing operand.\n") );
+ return FALSE;
+ }
+ p = skip_ws ( p );
+ }
+
+ /* allow to chain multiple assignments, such as: a=b=1 */
+ if ( ident && op )
+ {
+ if ( !seta_assignment ( &p, &exprval ) )
+ return FALSE;
+ }
+ else
+ {
+ if ( !seta_expr ( &p, &exprval ) )
+ return FALSE;
+ }
+
+ if ( identlen )
+ {
+ INT identval;
+ LPTSTR buf;
+
+ if ( !seta_identval ( ident, &identval ) )
+ identval = 0;
+ if ( op == '=' )
+ identval = exprval;
+ else if ( !calc ( &identval, op, exprval ) )
+ return FALSE;
+ buf = (LPTSTR)alloca ( 32 * sizeof(TCHAR) );
+ _sntprintf ( buf, 32, _T("%i"), identval );
+ SetEnvironmentVariable ( ident, buf ); // TODO FIXME -
check return value
+ exprval = identval;
+ }
+
+ *result = exprval;
+ *p_ = p;
+ return TRUE;
+}
+
+static BOOL
+seta_stmt ( LPCTSTR* p_, INT* result )
+{
+ LPCTSTR p = *p_;
+ INT rval;
+
+ if ( !seta_assignment ( &p, &rval ) )
+ return FALSE;
+ while ( *p == _T(',') )
+ {
+ p = skip_ws ( p+1 );
+
+ if ( !seta_assignment ( &p, &rval ) )
+ return FALSE;
+ }
+
+ *result = rval;
+ *p_ = p;
+ return TRUE;
+}
+
+static BOOL
+seta_eval ( LPCTSTR p )
+{
+ INT rval;
+ if ( !*p )
+ {
+ _tprintf ( _T("The syntax of the command is
incorrect.\n") );
+ return FALSE;
+ }
+ if ( !seta_stmt ( &p, &rval ) )
+ return FALSE;
+ _tprintf ( _T("%i\n"), rval );
+ return TRUE;
+}
+
#endif