reactos/reactos/base/shell/cmd/set.c
Hermès Bélusca-Maïto 2480c56ec9 [CMD]: Some fixes for SET /A command:
- If we do arithmetics using an non-defined env-var, the latter is automatically understood to be zero.
- If one left-shifts more than 31 bits (or left-shifts a negative number of bits), the result is automaticaly set to zero (checked on Windows'cmd + with cmd_winetest + wine cmd code).

svn path=/trunk/; revision=67062
2015-04-05 16:28:10 +00:00

552 lines
12 KiB
C

/*
* SET.C - set internal command.
*
*
* History:
*
* 06/14/97 (Tim Norman)
* changed static var in set() to a cmd_alloc'd space to pass to putenv.
* need to find a better way to do this, since it seems it is wasting
* memory when variables are redefined.
*
* 07/08/1998 (John P. Price)
* removed call to show_environment in set command.
* moved test for syntax before allocating memory in set command.
* misc clean up and optimization.
*
* 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
* added config.h include
*
* 28-Jul-1998 (John P Price <linux-guru@gcfl.net>)
* added set_env function to set env. variable without needing set command
*
* 09-Dec-1998 (Eric Kohl)
* Added help text ("/?").
*
* 24-Jan-1999 (Eric Kohl)
* Fixed Win32 environment handling.
* Unicode and redirection safe!
*
* 25-Feb-1999 (Eric Kohl)
* Fixed little bug.
*
* 30-Apr-2005 (Magnus Olsen <magnus@greatlord.com>)
* Remove all hardcoded strings in En.rc
*/
#include "precomp.h"
#ifdef INCLUDE_CMD_SET
/* initial size of environment variable buffer */
#define ENV_BUFFER_SIZE 1024
static BOOL
seta_eval ( LPCTSTR expr );
static LPCTSTR
skip_ws ( LPCTSTR p )
{
while (*p && *p <= _T(' '))
p++;
return p;
}
/* Used to check for and handle:
* SET "var=value", SET /P "var=prompt", and SET /P var="prompt" */
static LPTSTR
GetQuotedString(TCHAR *p)
{
TCHAR *end;
if (*p == _T('"'))
{
p = (LPTSTR)skip_ws(p + 1);
/* If a matching quote is found, truncate the string */
end = _tcsrchr(p, _T('"'));
if (end)
*end = _T('\0');
}
return p;
}
INT cmd_set (LPTSTR param)
{
LPTSTR p;
LPTSTR lpEnv;
LPTSTR lpOutput;
if ( !_tcsncmp (param, _T("/?"), 2) )
{
ConOutResPaging(TRUE,STRING_SET_HELP);
return 0;
}
param = (LPTSTR)skip_ws(param);
/* if no parameters, show the environment */
if (param[0] == _T('\0'))
{
lpEnv = (LPTSTR)GetEnvironmentStrings ();
if (lpEnv)
{
lpOutput = lpEnv;
while (*lpOutput)
{
if (*lpOutput != _T('='))
ConOutPuts(lpOutput);
lpOutput += _tcslen(lpOutput) + 1;
ConOutChar(_T('\n'));
}
FreeEnvironmentStrings (lpEnv);
}
return 0;
}
/* the /A does *NOT* have to be followed by a whitespace */
if ( !_tcsnicmp (param, _T("/A"), 2) )
{
BOOL Success;
StripQuotes(param);
Success = seta_eval ( skip_ws(param+2) );
if (!Success)
{
/* might seem random but this is what windows xp does */
nErrorLevel = 9165;
}
return !Success;
}
if (!_tcsnicmp(param, _T("/P"), 2))
{
TCHAR value[1023];
param = GetQuotedString((LPTSTR)skip_ws(param + 2));
p = _tcschr(param, _T('='));
if (!p)
{
ConErrResPuts(STRING_SYNTAX_COMMAND_INCORRECT);
nErrorLevel = 1;
return 1;
}
*p++ = _T('\0');
ConOutPrintf(_T("%s"), GetQuotedString(p));
ConInString(value, 1023);
if (!*value || !SetEnvironmentVariable(param, value))
{
nErrorLevel = 1;
return 1;
}
return 0;
}
param = GetQuotedString(param);
p = _tcschr (param, _T('='));
if (p)
{
/* set or remove environment variable */
if (p == param)
{
/* handle set =val case */
ConErrResPuts(STRING_SYNTAX_COMMAND_INCORRECT);
nErrorLevel = 1;
return 1;
}
*p++ = _T('\0');
if (!SetEnvironmentVariable(param, *p ? p : NULL))
{
nErrorLevel = 1;
return 1;
}
}
else
{
/* display all environment variable with the given prefix */
BOOL bFound = FALSE;
while (_istspace(*param) || *param == _T(',') || *param == _T(';'))
param++;
p = _tcsrchr(param, _T(' '));
if (!p)
p = param + _tcslen(param);
*p = _T('\0');
lpEnv = GetEnvironmentStrings();
if (lpEnv)
{
lpOutput = lpEnv;
while (*lpOutput)
{
if (!_tcsnicmp(lpOutput, param, p - param))
{
ConOutPuts(lpOutput);
ConOutChar(_T('\n'));
bFound = TRUE;
}
lpOutput += _tcslen(lpOutput) + 1;
}
FreeEnvironmentStrings(lpEnv);
}
if (!bFound)
{
ConErrResPrintf (STRING_PATH_ERROR, param);
nErrorLevel = 1;
return 1;
}
}
return 0;
}
static INT
ident_len(LPCTSTR p)
{
LPCTSTR p2 = p;
if ( __iscsymf(*p) )
{
++p2;
while ( __iscsym(*p2) )
++p2;
}
return (INT)(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 INT
seta_identval(LPCTSTR ident)
{
LPCTSTR identVal = GetEnvVarOrSpecial ( ident );
if ( !identVal )
return 0;
else
return _tcstol ( identVal, NULL, 0 );
}
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:
ConErrResPuts ( STRING_INVALID_OPERAND );
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 == _T('(') )
{
INT rval;
p = skip_ws ( p + 1 );
if ( !seta_stmt ( &p, &rval ) )
return FALSE;
if ( *p++ != _T(')') )
{
ConErrResPuts ( STRING_EXPECTED_CLOSE_PAREN );
return FALSE;
}
*result = rval;
}
else if ( isdigit(*p) )
{
*result = _tcstol ( p, (LPTSTR *)&p, 0 );
}
else if ( __iscsymf(*p) )
{
LPTSTR ident;
INT identlen;
PARSE_IDENT(ident,identlen,p);
*result = seta_identval ( ident );
}
else
{
ConErrResPuts ( STRING_EXPECTED_NUMBER_OR_VARIABLE );
return FALSE;
}
*p_ = skip_ws ( 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_ltorTerm(LPCTSTR* p_, INT* result, LPCTSTR ops, BOOL (*subTerm)(LPCTSTR*,INT*))
{
LPCTSTR p = *p_;
INT lval;
if ( !subTerm ( &p, &lval ) )
return FALSE;
while ( *p && _tcschr(ops,*p) )
{
INT rval;
TCHAR op = *p;
p = skip_ws ( p+1 );
if ( !subTerm ( &p, &rval ) )
return FALSE;
if ( !calc ( &lval, op, rval ) )
return FALSE;
}
*result = lval;
*p_ = p;
return TRUE;
}
static BOOL
seta_addTerm(LPCTSTR* p_, INT* result)
{
return seta_ltorTerm ( p_, result, _T("*/%"), seta_mulTerm );
}
static BOOL
seta_logShiftTerm(LPCTSTR* p_, INT* result)
{
return seta_ltorTerm ( p_, result, _T("+-"), seta_addTerm );
}
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;
switch ( op )
{
case '<':
{
/* 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 (rval < 0 || rval >= (8 * sizeof(lval)))
lval = 0;
else
lval <<= rval;
break;
}
case '>':
lval >>= rval;
break;
default:
ConErrResPuts ( STRING_INVALID_OPERAND );
return FALSE;
}
}
*result = lval;
*p_ = p;
return TRUE;
}
static BOOL
seta_bitExclOrTerm(LPCTSTR* p_, INT* result)
{
return seta_ltorTerm ( p_, result, _T("&"), seta_bitAndTerm );
}
static BOOL
seta_bitOrTerm(LPCTSTR* p_, INT* result)
{
return seta_ltorTerm ( p_, result, _T("^"), seta_bitExclOrTerm );
}
static BOOL
seta_expr(LPCTSTR* p_, INT* result)
{
return seta_ltorTerm ( p_, result, _T("|"), seta_bitOrTerm );
}
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 )
{
p = skip_ws(p);
if ( *p == _T('=') )
op = *p, p = skip_ws(p+1);
else if ( _tcschr ( _T("*/%+-&^|"), *p ) && p[1] == _T('=') )
op = *p, p = skip_ws(p+2);
else if ( _tcschr ( _T("<>"), *p ) && *p == p[1] && p[2] == _T('=') )
op = *p, p = skip_ws(p+3);
}
/* allow to chain multiple assignments, such as: a=b=1 */
if ( ident && op )
{
INT identval;
LPTSTR buf;
if ( !seta_assignment ( &p, &exprval ) )
return FALSE;
identval = seta_identval ( ident );
switch ( op )
{
case '=':
identval = exprval;
break;
case '<':
{
/* 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 (exprval < 0 || exprval >= (8 * sizeof(identval)))
identval = 0;
else
identval <<= exprval;
break;
}
case '>':
identval >>= exprval;
break;
default:
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;
}
else
{
/* restore p in case we found an ident but not an op */
p = *p_;
if ( !seta_expr ( &p, &exprval ) )
return FALSE;
}
*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 )
{
ConErrResPuts ( STRING_SYNTAX_COMMAND_INCORRECT );
return FALSE;
}
if ( !seta_stmt ( &p, &rval ) )
return FALSE;
if ( !bc )
ConOutPrintf ( _T("%i"), rval );
return TRUE;
}
#endif