Windows-Server-2003/base/cmd/cenv.c

1638 lines
38 KiB
C

/*++
Copyright (c) 1988-1999 Microsoft Corporation
Module Name:
cenv.c
Abstract:
Environment variable support
--*/
#include "cmd.h"
struct envdata {
LPTSTR Strings;
} ;
struct envdata CmdEnv ; // Holds info to manipulate Cmd's environment
struct envdata * OriginalEnvironment; // original environment setup used with eStart
extern TCHAR PathStr[], PromptStr[] ;
extern TCHAR AppendStr[]; /* @@ */
extern CHAR InternalError[] ;
extern TCHAR Fmt16[], Fmt17[], EnvErr[] ;
extern TCHAR SetArithStr[] ;
extern TCHAR SetPromptStr[] ;
extern unsigned flgwd ;
extern TCHAR CdStr[] ;
extern TCHAR DatStr[] ;
extern TCHAR TimStr[] ;
extern TCHAR ErrStr[] ;
extern TCHAR CmdExtVerStr[] ;
extern unsigned LastRetCode;
extern BOOL CtrlCSeen;
extern UINT CurrentCP;
extern BOOLEAN PromptValid;
extern int glBatType; // to distinguish OS/2 vs DOS errorlevel behavior depending on a script file name
int SetArithWork(TCHAR *tas);
unsigned
SetLastRetCodeIfError(
unsigned RetCode
)
{
if (RetCode != 0) {
LastRetCode = RetCode;
}
return RetCode;
}
/*** ePath - Begin the execution of a Path Command
*
* Purpose:
* If the command has no argument display the current value of the PATH
* environment variable. Otherwise, change the value of the Path
* environment variable to the argument.
*
* int ePath(struct cmdnode *n)
*
* Args:
* n - the parse tree node containing the path command
*
* Returns:
* If changing the PATH variable, whatever SetEnvVar() returns.
* SUCCESS, otherwise.
*
*/
int ePath(n)
struct cmdnode *n ;
{
if (glBatType != CMD_TYPE) {
// if set command is executed from .bat file OR entered at command prompt
return( SetLastRetCodeIfError(PathWork( n, 1 )));
} else {
return( LastRetCode = PathWork( n, 1 ) );
}
}
/*** eAppend - Entry point for Append routine
*
* Purpose:
* to call Append and pass it a pointer to the command line
* arguments
*
* Args:
* a pointer to the command node structure
*
*/
int eAppend(n)
struct cmdnode *n ;
{
if (glBatType != CMD_TYPE) {
// if set command is executed from .bat file OR entered at command prompt
return( SetLastRetCodeIfError(PathWork( n, 0 )));
} else {
return( LastRetCode = PathWork( n, 0 ) );
}
}
int PathWork(n, flag)
struct cmdnode *n ;
int flag; /* 0 = AppendStr, 1 = PathStr */
{
TCHAR *tas ; /* Tokenized argument string */
TCHAR c ;
/* M014 - If the only argument is a single ";", then we have to set
* a NULL path.
*/
if ( n->argptr ) {
c = *(EatWS(n->argptr, NULL)) ;
} else {
c = NULLC;
}
if ((!c || c == NLN) && /* If args are all whitespace */
mystrchr(n->argptr, TEXT(';'))) {
return(SetEnvVar(flag ? PathStr : AppendStr, TEXT(""))) ;
} else {
tas = TokStr(n->argptr, TEXT(";"), TS_WSPACE | TS_NWSPACE) ;
if (*tas) {
return(SetEnvVar(flag ? PathStr : AppendStr, tas)) ;
}
cmd_printf(Fmt16, flag ? PathStr : AppendStr,
GetEnvVar(flag ? PathStr : AppendStr)) ;
}
return(SUCCESS) ;
}
/*** ePrompt - begin the execution of the Prompt command
*
* Purpose:
* To modifiy the Prompt environment variable.
*
* int ePrompt(struct cmdnode *n)
*
* Args:
* n - the parse tree node containing the prompt command
*
* Returns:
* Whatever SetEnvVar() returns.
*
*/
int ePrompt(n)
struct cmdnode *n ;
{
if (glBatType != CMD_TYPE) {
// if set command is executed from .bat file OR entered at command prompt
return(SetLastRetCodeIfError(SetEnvVar(PromptStr, TokStr(n->argptr, NULL, TS_WSPACE)))) ;
} else {
return(LastRetCode = SetEnvVar(PromptStr, TokStr(n->argptr, NULL, TS_WSPACE)) ) ;
}
}
/*** eSet - execute a Set command
*
* Purpose:
* To set/modify an environment or to display the current environment
* contents.
*
* int eSet(struct cmdnode *n)
*
* Args:
* n - the parse tree node containing the set command
*
* Returns:
* If setting and the command is syntactically correct, whatever SetEnvVar()
* returns. Otherwise, FAILURE.
*
* If displaying, SUCCESS is always returned.
*
*/
int eSet(n)
struct cmdnode *n ;
{
if (glBatType != CMD_TYPE) {
// if set command is executed from .bat file OR entered at command prompt
return( SetLastRetCodeIfError(SetWork( n )));
} else {
return( LastRetCode = SetWork( n ) );
}
}
/*** SetPromptUser - set environment variable to value entered by user.
*
* Purpose:
* Set environment variable to value entered by user.
*
* int SetPromptUser(TCHAR *tas)
*
* Args:
* tas - pointer to null terminated string of the form:
*
* VARNAME=promptString
*
* Returns:
* If valid expression, return SUCCESS otherwise FAILURE.
*
*/
int SetPromptUser(TCHAR *tas)
{
TCHAR *wptr;
TCHAR *tptr;
ULONG dwOutputModeOld;
ULONG dwOutputModeCur;
ULONG dwInputModeOld;
ULONG dwInputModeCur;
BOOLEAN fOutputModeSet = FALSE;
BOOLEAN fInputModeSet = FALSE;
HANDLE hndStdOut = NULL;
HANDLE hndStdIn = NULL;
DWORD cch;
TCHAR szValueBuffer[ 1024 ];
//
// Find first non-blank argument.
//
if (tas != NULL)
while (*tas && *tas <= SPACE)
tas += 1;
// If no input, declare an error
//
if (!tas || !*tas) {
PutStdErr(MSG_BAD_SYNTAX, NOARGS);
return(FAILURE) ;
}
//
// See if first argument is quoted. If so, strip off
// leading quote, spaces and trailing quote.
//
if (*tas == QUOTE) {
tas += 1;
while (*tas && *tas <= SPACE)
tas += 1;
tptr = _tcsrchr(tas, QUOTE);
if (tptr)
*tptr = NULLC;
}
//
// Find the equal sign in the argument.
//
wptr = _tcschr(tas, EQ);
//
// If no equal sign, error.
//
if (!wptr) {
PutStdErr(MSG_BAD_SYNTAX, NOARGS);
return(FAILURE) ;
}
//
// Found the equal sign, so left of equal sign is variable name
// and right of equal sign is prompt string. Dont allow user to set
// a variable name that begins with an equal sign, since those
// are reserved for drive current directories.
//
*wptr++ = NULLC;
//
// See if second argument is quoted. If so, strip off
// leading quote, spaces and trailing quote.
//
if (*wptr == QUOTE) {
wptr += 1;
while (*wptr && *wptr <= SPACE)
wptr += 1;
tptr = _tcsrchr(wptr, QUOTE);
if (tptr)
*tptr = NULLC;
}
if (*wptr == EQ) {
PutStdErr(MSG_BAD_SYNTAX, NOARGS);
return(FAILURE) ;
}
hndStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (GetConsoleMode( hndStdOut, &dwOutputModeOld) ) {
// make sure CRLF is processed correctly
dwOutputModeCur = dwOutputModeOld | ENABLE_PROCESSED_OUTPUT;
fOutputModeSet = TRUE;
SetConsoleMode(hndStdOut,dwOutputModeCur);
GetLastError();
}
hndStdIn = GetStdHandle(STD_INPUT_HANDLE);
if (GetConsoleMode( hndStdIn, &dwInputModeOld) ) {
// make sure input is processed correctly
dwInputModeCur = dwInputModeOld | ENABLE_LINE_INPUT |
ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT;
fInputModeSet = TRUE;
SetConsoleMode(hndStdIn,dwInputModeCur);
GetLastError();
}
//
// Loop till the user enters a value for the variable.
//
while (TRUE) {
PutStdOut(MSG_LITERAL_TEXT, ONEARG, wptr );
szValueBuffer[0] = NULLC;
if (ReadBufFromInput( GetStdHandle(STD_INPUT_HANDLE),
szValueBuffer,
sizeof(szValueBuffer)/sizeof(TCHAR),
&cch
) != 0 &&
cch != 0
) {
//
// Strip off any trailing CRLF
//
while (cch > 0 && szValueBuffer[cch-1] < SPACE)
cch -= 1;
break;
} else {
cch = 0;
break;
}
if (!FileIsDevice(STDIN) || !(flgwd & 1))
cmd_printf(CrLf) ;
}
if (fOutputModeSet) {
SetConsoleMode( hndStdOut, dwOutputModeOld );
}
if (fInputModeSet) {
SetConsoleMode( hndStdIn, dwInputModeOld );
}
if (cch) {
szValueBuffer[cch] = NULLC;
return(SetEnvVar(tas, szValueBuffer)) ;
} else {
return(FAILURE);
}
}
int SetWork(n)
struct cmdnode *n ;
{
TCHAR *tas ; /* Tokenized argument string */
TCHAR *wptr ; /* Work pointer */
int i ; /* Work variable */
//
// If extensions are enabled, things are different
//
if (fEnableExtensions) {
tas = n->argptr;
//
// Find first non-blank argument.
//
if (tas != NULL)
while (*tas && *tas <= SPACE)
tas += 1;
//
// No arguments, same as old behavior. Display current
// set of environment variables.
//
if (!tas || !*tas)
return(DisplayEnv( GetCapturedEnvironmentStrings( &CmdEnv ))) ;
//
// See if /A switch given. If so, let arithmetic
// expression evaluator do the work.
//
if (!_tcsnicmp(tas, SetArithStr, 2))
return SetArithWork(tas+2);
//
// See if /P switch given. If so, prompt user for value
//
if (!_tcsnicmp(tas, SetPromptStr, 2))
return SetPromptUser(tas+2);
//
// See if first argument is quoted. If so, strip off
// leading quote, spaces and trailing quote.
//
if (*tas == QUOTE) {
tas += 1;
while (*tas && *tas <= SPACE)
tas += 1;
wptr = _tcsrchr(tas, QUOTE);
if (wptr)
*wptr = NULLC;
}
//
// Dont allow user to set a variable name that begins with
// an equal sign, since those are reserved for drive current
// directories. This check will also detect missing variable
// name, e.g.
//
// set %LOG%=c:\tmp\log.txt
//
// if LOG is not defined is an invalid statement.
//
if (*tas == EQ) {
PutStdErr(MSG_BAD_SYNTAX, NOARGS);
return(FAILURE) ;
}
//
// Find the equal sign in the argument.
//
wptr = _tcschr(tas, EQ);
//
// If no equal sign, then assume argument is variable name
// and user wants to see its value. Display it.
//
if (!wptr)
return DisplayEnvVariable(tas);
//
// Found the equal sign, so left of equal sign is variable name
// and right of equal sign is value.
//
*wptr++ = NULLC;
return(SetEnvVar(tas, wptr)) ;
}
tas = TokStr(n->argptr, ONEQSTR, TS_WSPACE|TS_SDTOKENS) ;
if (!*tas)
return(DisplayEnv( GetCapturedEnvironmentStrings( &CmdEnv ))) ;
else {
for (wptr = tas, i = 0 ; *wptr ; wptr += mystrlen(wptr)+1, i++)
;
/* If too many parameters were given, the second parameter */
/* wasn't an equal sign, or they didn't specify a string */
/* return an error message. */
if ( i > 3 || *(wptr = tas+mystrlen(tas)+1) != EQ ||
!mystrlen(mystrcpy(tas, StripQuotes(tas))) ) {
/* M013 */
PutStdErr(MSG_BAD_SYNTAX, NOARGS);
return(FAILURE) ;
} else {
return(SetEnvVar(tas, wptr+2)) ;
}
}
}
/*** DisplayEnvVariable - display a specific variable from the environment
*
* Purpose:
* To display a specific variable from the current environment.
*
* int DisplayEnvVariable( tas )
*
* Returns:
* SUCCESS if all goes well
* FAILURE if it runs out of memory or cannot lock the env. segment
*/
int DisplayEnvVariable(tas)
TCHAR *tas;
{
TCHAR *envptr ;
TCHAR *vstr ;
unsigned size ;
UINT PrefixLength;
int rc;
//
// Get environment. If there's none, we're done.
//
envptr = GetCapturedEnvironmentStrings( &CmdEnv );
if (envptr == (TCHAR *)NULL) {
fprintf ( stderr, InternalError , "Null environment" ) ;
return( FAILURE ) ;
}
//
// Isolate the prefix to match against.
//
tas = EatWS(tas, NULL);
if ((vstr = mystrrchr(tas, SPACE)) != NULL) {
*vstr = NULLC;
}
PrefixLength = mystrlen(tas);
//
// Walk through the environment looking for prefixes that match.
//
rc = FAILURE;
while ((size = mystrlen(envptr)) > 0) {
//
// Stop soon if we see ^C
//
if (CtrlCSeen) {
break;
}
//
// If the prefix is long enough, then terminate the string and
// look for a prefix match. If we match, restore the string
// and display it
//
if (size >= PrefixLength) {
TCHAR SavedChar = envptr[PrefixLength];
envptr[PrefixLength] = NULLC;
if (!lstrcmpi( envptr, tas )) {
envptr[PrefixLength] = SavedChar;
cmd_printf(Fmt17, envptr );
rc = SUCCESS;
} else {
envptr[PrefixLength] = SavedChar;
}
}
//
// Advance to the next string
//
envptr += size+1 ;
}
if (rc != SUCCESS) {
PutStdErr(MSG_ENV_VAR_NOT_FOUND, ONEARG, tas);
}
return(rc) ;
}
/*** MyGetEnvVar - get a pointer to the value of an environment variable
*
* Purpose:
* Return a pointer to the value of the specified environment variable.
*
* If the variable is not found, return NULL.
*
* TCHAR *MyGetEnvVar(TCHAR *varname)
*
* Args:
* varname - the name of the variable to search for
*
* Returns:
* See above.
*
* Side Effects:
* Returned value points to within the environment block itself, so is
* not valid after a set environment variable operations is perform.
*/
const TCHAR *
MyGetEnvVarPtr(TCHAR *varname)
{
TCHAR *envptr ; /* Ptr to environment */
TCHAR *vstr ;
unsigned size ; /* Length of current env string */
unsigned n ;
if (varname == NULL) {
return( NULL ) ;
}
envptr = GetCapturedEnvironmentStrings( &CmdEnv );
if (envptr == (TCHAR *)NULL) {
return( NULL ) ;
}
varname = EatWS(varname, NULL);
if ((vstr = mystrrchr(varname, SPACE)) != NULL)
*vstr = NULLC;
n = mystrlen(varname);
while ((size = mystrlen(envptr)) > 0) { /* M015 */
if (CtrlCSeen) {
break;
}
if (!_tcsnicmp(varname, envptr, n) && envptr[n] == TEXT( '=' )) {
return envptr+n+1;
}
envptr += size+1 ;
}
return(NULL);
}
/*** DisplayEnv - display the environment
*
* Purpose:
* To display the current contents of the environment.
*
* int DisplayEnv()
*
* Returns:
* SUCCESS if all goes well
* FAILURE if it runs out of memory or cannot lock the env. segment
*/
int DisplayEnv(TCHAR *envptr)
{
unsigned size ; /* Length of current env string */
if (envptr == (TCHAR *)NULL) {
fprintf ( stderr, InternalError , "Null environment" ) ;
return( FAILURE ) ;
}
while ((size = mystrlen(envptr)) > 0) { /* M015 */
if (CtrlCSeen) {
return(FAILURE);
}
#if !DBG
// Dont show current directory variables in retail product
if (*envptr != EQ)
#endif // DBG
cmd_printf(Fmt17, envptr) ; /* M005 */
envptr += size+1 ;
}
return(SUCCESS) ;
}
/*** GetEnvVar - get the value of an environment variable
*
* Purpose:
* Return a string containing the value of the specified environment
* variable. The string value has been placed into a static buffer
* that is valid until the next GetEnvVar call.
*
* If the variable is not found, return NULL.
*
* TCHAR *GetEnvVar(TCHAR *varname)
*
* Args:
* varname - the name of the variable to search for
*
* Returns:
* See above.
*
*/
TCHAR GetEnvVarBuffer[LBUFLEN];
TCHAR *
GetEnvVar(varname)
PTCHAR varname ;
{
GetEnvVarBuffer[0] = TEXT( '\0' );
if (GetEnvironmentVariable(varname, GetEnvVarBuffer, sizeof(GetEnvVarBuffer) / sizeof(TCHAR))) {
return(GetEnvVarBuffer);
} else
if (fEnableExtensions) {
if (!_tcsicmp(varname, CdStr)) {
GetDir(GetEnvVarBuffer, GD_DEFAULT) ;
return GetEnvVarBuffer;
} else
if (!_tcsicmp(varname, ErrStr)) {
_stprintf( GetEnvVarBuffer, TEXT("%d"), LastRetCode );
return GetEnvVarBuffer;
} else
if (!_tcsicmp(varname, CmdExtVerStr)) {
_stprintf( GetEnvVarBuffer, TEXT("%d"), CMDEXTVERSION );
return GetEnvVarBuffer;
} else
if (!_tcsicmp(varname, TEXT("CMDCMDLINE"))) {
return GetCommandLine();
} else
if (!_tcsicmp(varname, DatStr)) {
GetEnvVarBuffer[ PrintDate(NULL, PD_DATE, GetEnvVarBuffer, LBUFLEN) ] = NULLC;
return GetEnvVarBuffer;
}
if ( !_tcsicmp(varname, TimStr)) {
GetEnvVarBuffer[ PrintTime(NULL, PT_TIME, GetEnvVarBuffer, LBUFLEN) ] = NULLC;
return GetEnvVarBuffer;
}
if ( !_tcsicmp(varname, TEXT("RANDOM"))) {
_stprintf( GetEnvVarBuffer, TEXT("%d"), rand() );
return GetEnvVarBuffer;
}
}
return(NULL);
}
/*** CaptureEnvironmentStrings - make writeable copy of environment
*
* Purpose:
* Allocate memory and create copy of environment strings
*
* Args:
* None
*
* Returns:
* Allocated copy of environment strings or NULL
*/
LPTSTR CaptureEnvironmentStrings( VOID )
{
LPTSTR EnvStrings = GetEnvironmentStrings( );
LPTSTR Copy = NULL;
if (EnvStrings != NULL) {
ULONG Size = GetEnvCb( EnvStrings );
//
// Allocate and copy strings
//
Copy = HeapAlloc( GetProcessHeap( ), HEAP_ZERO_MEMORY, Size );
if (Copy != NULL) {
memcpy( Copy, EnvStrings, Size );
}
FreeEnvironmentStrings( EnvStrings );
}
return Copy;
}
/*** InitEnv - Set up CMD's copy of the environment
*
* Purpose:
* Creates the copy of CMD's environment.
*
* Args:
* None
*
* Returns:
* None
*/
void InitEnv( void )
{
CmdEnv.Strings = CaptureEnvironmentStrings( );
OriginalEnvironment = CopyEnv();
}
LPWSTR GetCapturedEnvironmentStrings( struct envdata *Environment )
{
return Environment->Strings;
}
/*** SetEnvVar - controls adding/changing an environment variable
*
* Purpose:
* Add/replace an environment variable. Grow it if necessary.
*
* int SetEnvVar(TCHAR *varname, TCHAR *varvalue, struct envdata *env)
*
* Args:
* varname - name of the variable being added/replaced
* varvalue - value of the variable being added/replaced
* env - environment info structure being used
*
* Returns:
* SUCCESS if the variable could be added/replaced.
* FAILURE otherwise.
*
*/
int SetEnvVar(varname, varvalue)
TCHAR *varname ;
TCHAR *varvalue ;
{
int retvalue;
MEMORY_BASIC_INFORMATION MemoryInfo;
PromptValid = FALSE; // Force it to be recalculated
if (!_tcslen(varvalue)) {
varvalue = NULL; // null to remove from env
}
retvalue = SetEnvironmentVariable(varname, varvalue);
HeapFree( GetProcessHeap( ), 0, CmdEnv.Strings );
CmdEnv.Strings = CaptureEnvironmentStrings();
return !retvalue;
}
/*** MoveEnv - Move the contents of the environment (M008 - New function)
*
* Purpose:
* Used by CopyEnv, this function moves the existing
* environment contents to the new location.
*
* MoveEnv(unsigned thndl, unsigned shndl, unsigned cnt)
*
* Args:
* thndl - Handle of target environment
* shndl - Handle of source environment
* cnt - byte count to move
*
* Returns:
* TRUE if no errors
* FALSE otherwise
*
*/
BOOL MoveEnv(tenvptr, senvptr, cnt)
TCHAR *senvptr ; /* Ptr into source env seg */
TCHAR *tenvptr ; /* Ptr into target env seg */
ULONG cnt ;
{
if ((tenvptr == NULL) ||
(senvptr == NULL)) {
fprintf(stderr, InternalError, "Null environment") ;
return(FALSE) ;
}
memcpy(tenvptr, senvptr, cnt) ; /* M015 */
return(TRUE) ;
}
/*** FreeEnv - free an environment created by CopyEnv
*
* Purpose:
* Free all memory associated with a copied environment
*
* Returns:
* nothing
*
*/
void FreeEnv( struct envdata *Environment )
{
HeapFree( GetProcessHeap( ), 0, Environment->Strings );
HeapFree( GetProcessHeap( ), 0, Environment );
}
/*** CopyEnv - make a copy of the current environment
*
* Purpose:
* Make a copy of CmdEnv and put the new handle into the newly
* created envdata structure. This routine is only called by
* eSetlocal and init.
*
* struct envdata *CopyEnv()
*
* Returns:
* A pointer to the environment information structure.
* Returns NULL if unable to allocate enough memory
*
* Notes:
* - M001 - This function was disabled, now reenabled.
* - The current environment is copied as a snapshot of how it looked
* before SETLOCAL was executed.
*
*/
struct envdata *CopyEnv() {
struct envdata *cce ; /* New env info structure */
cce = (struct envdata *) HeapAlloc( GetProcessHeap( ), HEAP_ZERO_MEMORY, sizeof( *cce ));
if (cce == NULL) {
return NULL;
}
cce->Strings = CaptureEnvironmentStrings( );
if (cce->Strings == NULL) {
HeapFree( GetProcessHeap( ), 0, cce );
PutStdErr( MSG_OUT_OF_ENVIRON_SPACE, NOARGS );
return NULL;
}
return cce;
}
/*** ResetEnv - restore the environment
*
* Purpose:
* Restore the environment to the way it was before the execution of
* the SETLOCAL command. This function only called by eEndlocal.
*
* ResetEnv(struct envdata *env)
*
* Args:
* env - structure containing handle, size and max dimensions of an
* environment.
*
* Notes:
* - M001 - This function was disabled, but has been reenabled.
* - M001 - This function used to test for OLD/NEW style batch files
* and delete the copy or the original environment as
* appropriate. It now always deletes the original.
* - M014 - Note that the modified local environment will never be
* shrunk, so we can assume it will hold the old one.
*
*/
void ResetEnv( struct envdata *env)
{
SetEnvironmentStrings( env->Strings );
HeapFree( GetProcessHeap( ), 0, CmdEnv.Strings );
CmdEnv.Strings = CaptureEnvironmentStrings();
#if 0
PTCHAR EnvString;
PTCHAR Name;
PTCHAR Value;
//
// Delete everything in current environment
//
EnvString = GetCapturedEnvironmentStrings( &CmdEnv );
Name = EnvString;
while (*Name != TEXT( '\0' )) {
//
// Find equal sign
//
Value = Name + 1;
while (*Value != TEXT( '\0' ) && *Value != TEXT( '=' )) {
Value++;
}
if (*Value == TEXT( '\0' )) {
//
// SetEnvironmentVariable will succeed in deleting this
//
SetEnvironmentVariable( Name, NULL );
} else {
*Value = TEXT( '\0' );
SetEnvironmentVariable( Name, NULL );
*Value = TEXT( '=' );
}
Name += _tcslen( Name ) + 1;
}
//
// Add everything in env back into the environment
//
Name = env->Strings;
while (*Name != TEXT( '\0' )) {
//
// Find equal sign
//
Value = Name + 1;
while (*Value != TEXT( '\0' ) && *Value != TEXT( '=' )) {
Value++;
}
if (*Value == TEXT( '\0' )) {
//
// I have no clue how to add this in
//
SetEnvironmentVariable( Name, NULL );
} else {
*Value = TEXT( '\0' );
SetEnvironmentVariable( Name, Value + 1 );
*Value = TEXT( '=' );
}
Name += _tcslen( Name ) + 1;
}
HeapFree( GetProcessHeap( ), 0, CmdEnv.Strings );
CmdEnv.Strings = CaptureEnvironmentStrings();
#endif
}
ULONG
GetEnvCb( TCHAR *penv )
{
TCHAR *Scan = penv;
if (penv == NULL) {
return(0);
}
//
// NUL string terminates environment
//
while (*Scan) {
//
// Skip over string and NUL
//
while (*Scan++) {
}
}
Scan++;
return (Scan - penv) * sizeof( TCHAR );
}
//
// expr -> assign [, assign]* ,
//
// assign -> orlogexpr |
// VAR ASSIGNOP assign <op>=
//
// orlogexpr -> xorlogexpr [| xorlogexpr]* |
//
// xorlogexpr -> andlogexpr [^ andlogexpr]* ^
//
// andlogexpr -> shiftexpr [& shiftexpr]* &
//
// shiftexpr -> addexpr [SHIFTOP addexpr]* <<, >>
//
// addexpr -> multexpr [ADDOP multexpr]* +, -
//
// multexpr -> unaryexpr [MULOP unaryexpr]* *, /, %
//
// unaryexpr -> ( expr ) | ()
// UNARYOP unaryexpr +, -, !, ~
//
TCHAR szOps[] = TEXT("<>+-*/%()|^&=,");
TCHAR szUnaryOps[] = TEXT("+-~!");
typedef struct {
PTCHAR Token;
LONG Value;
DWORD Error;
} PARSESTATE, *PPARSESTATE;
VOID
APerformUnaryOperation( PPARSESTATE State, TCHAR Op, LONG Value )
{
switch (Op) {
case TEXT( '+' ):
State->Value = Value;
break;
case TEXT( '-' ):
State->Value = -Value;
break;
case TEXT( '~' ):
State->Value = ~Value;
break;
case TEXT( '!' ):
State->Value = !Value;
break;
default:
printf( "APerformUnaryOperation: '%c'\n", Op);
break;
}
}
VOID
APerformArithmeticOperation( PPARSESTATE State, TCHAR Op, LONG Left, LONG Right )
{
switch (Op) {
case TEXT( '<' ):
State->Value = (Right >= 8 * sizeof( Left << Right))
? 0
: (Left << Right);
break;
case TEXT( '>' ):
State->Value = (Right >= 8 * sizeof( Left >> Right ))
? (Left < 0 ? -1 : 0)
: (Left >> Right);
break;
case TEXT( '+' ):
State->Value = Left + Right;
break;
case TEXT( '-' ):
State->Value = Left - Right;
break;
case TEXT( '*' ):
State->Value = Left * Right;
break;
case TEXT( '/' ):
if (Right == 0) {
State->Error = MSG_SET_A_DIVIDE_BY_ZERO;
} else {
State->Value = Left / Right;
}
break;
case TEXT( '%' ):
if (Right == 0) {
State->Error = MSG_SET_A_DIVIDE_BY_ZERO;
} else {
State->Value = Left % Right;
}
break;
case TEXT( '|' ):
State->Value = Left | Right;
break;
case TEXT( '^' ):
State->Value = Left ^ Right;
break;
case TEXT( '&' ):
State->Value = Left & Right;
break;
case TEXT( '=' ):
State->Value = Right;
break;
default:
printf( "APerformArithmeticOperation: '%c'\n", Op);
}
}
//
// Return the numeric value of an environment variable (or 0)
//
LONG
AGetValue( PTCHAR Start, PTCHAR End )
{
TCHAR c = *End;
const TCHAR *Value;
PTCHAR Dummy;
*End = NULLC;
Value = MyGetEnvVarPtr( Start );
*End = c;
if (Value == NULL) {
return 0;
}
return _tcstol( Value, &Dummy, 0);
}
DWORD
ASetValue( PTCHAR Start, PTCHAR End, LONG Value )
{
TCHAR Result[32];
TCHAR c = *End;
DWORD Return = SUCCESS;
*End = NULLC;
_sntprintf( Result, 32, TEXT("%d"), Value ) ;
Result[31] = TEXT( '\0' );
if (SetEnvVar( Start, Result) != SUCCESS) {
Return = GetLastError();
}
*End = c;
return Return;
}
//
// Forward decls
//
PARSESTATE AParseAddExpr( PARSESTATE State );
PARSESTATE AParseAndLogExpr( PARSESTATE State );
PARSESTATE AParseAssign( PARSESTATE State );
PARSESTATE AParseExpr( PARSESTATE State );
PARSESTATE AParseMultExpr( PARSESTATE State );
PARSESTATE AParseOrLogExpr( PARSESTATE State );
PARSESTATE AParseShiftExpr( PARSESTATE State );
PARSESTATE AParseUnaryExpr( PARSESTATE State );
PARSESTATE AParseXorLogExpr( PARSESTATE State );
//
// Skip whitespace and return next character
//
BOOL ASkipWhiteSpace( PPARSESTATE State )
{
while (*State->Token != NULLC && *State->Token <= SPACE) {
State->Token++;
}
return *State->Token != NULLC;
}
TCHAR ANextChar( PPARSESTATE State )
{
ASkipWhiteSpace( State );
return *State->Token;
}
BOOL AParseVariable( PPARSESTATE State, PTCHAR *FirstChar, PTCHAR *EndOfName )
{
TCHAR c = ANextChar( State );
//
// Next char is a digit or operator, can't be a variable
//
if (c == NULLC
|| _istdigit( c )
|| _tcschr( szOps, c ) != NULL
|| _tcschr( szUnaryOps, c ) != NULL) {
return FALSE;
}
*FirstChar = State->Token;
//
// find end of variable
//
while (*State->Token &&
*State->Token > SPACE &&
!_tcschr( szUnaryOps, *State->Token ) &&
!_tcschr( szOps, *State->Token ) ) {
State->Token += 1;
}
*EndOfName = State->Token;
return TRUE;
}
// expr -> assign [, assign]*
PARSESTATE AParseExpr( PARSESTATE State )
{
State = AParseAssign( State );
while (State.Error == SUCCESS) {
if (ANextChar( &State ) != TEXT( ',' )) {
break;
}
State.Token++;
State = AParseAssign( State );
}
return State;
}
// assign -> VAR ASSIGNOP assign |
// orlogexpr
PARSESTATE AParseAssign( PARSESTATE State )
{
TCHAR c = ANextChar( &State );
PARSESTATE SavedState;
SavedState = State;
if (c == NULLC) {
State.Error = MSG_SET_A_MISSING_OPERAND;
return State;
}
//
// See if we have VAR ASSIGNOP
//
do {
PTCHAR FirstChar;
PTCHAR EndOfName;
TCHAR OpChar;
LONG OldValue;
//
// Parse off variable
//
if (!AParseVariable( &State, &FirstChar, &EndOfName )) {
break;
}
//
// Look for <op>=
//
OpChar = ANextChar( &State );
if (OpChar == NULLC) {
break;
}
if (OpChar != TEXT( '=' )) {
if (_tcschr( szOps, OpChar ) == NULL) {
break;
}
State.Token++;
if (OpChar == TEXT( '<' ) || OpChar == TEXT( '>')) {
if (ANextChar( &State ) != OpChar) {
break;
}
State.Token++;
}
}
if (ANextChar( &State ) != TEXT( '=' )) {
break;
}
State.Token++;
//
// OpChar is the sort of operation to apply before assignment
// State has been advance to, hopefully, another assign. Parse it
// and see where we get
//
State = AParseAssign( State );
if (State.Error != SUCCESS) {
return State;
}
OldValue = AGetValue( FirstChar, EndOfName );
//
// Perform the operation and the assignment
//
APerformArithmeticOperation( &State, OpChar, OldValue, State.Value );
if (State.Error != SUCCESS) {
return State;
}
State.Error = ASetValue( FirstChar, EndOfName, State.Value );
return State;
} while ( FALSE );
//
// Must be orlogexpr. Go back and parse over
//
return AParseOrLogExpr( SavedState );
}
// orlogexpr -> xorlogexpr [| xorlogexpr]* |
PARSESTATE
AParseOrLogExpr( PARSESTATE State )
{
State = AParseXorLogExpr( State );
while (State.Error == SUCCESS) {
TCHAR Op = ANextChar( &State );
LONG Value = State.Value;
if (Op != TEXT( '|' )) {
break;
}
State.Token++;
State = AParseXorLogExpr( State );
APerformArithmeticOperation( &State, Op, Value, State.Value );
}
return State;
}
// xorlogexpr -> andlogexpr [^ andlogexpr]* ^
PARSESTATE
AParseXorLogExpr( PARSESTATE State )
{
State = AParseAndLogExpr( State );
while (State.Error == SUCCESS) {
TCHAR Op = ANextChar( &State );
LONG Value = State.Value;
if (Op != TEXT( '^' )) {
break;
}
State.Token++;
State = AParseAndLogExpr( State );
APerformArithmeticOperation( &State, Op, Value, State.Value );
}
return State;
}
// andlogexpr -> shiftexpr [& shiftexpr]* &
PARSESTATE
AParseAndLogExpr( PARSESTATE State )
{
State = AParseShiftExpr( State );
while (State.Error == SUCCESS) {
TCHAR Op = ANextChar( &State );
LONG Value = State.Value;
if (Op != TEXT( '&' )) {
break;
}
State.Token++;
State = AParseShiftExpr( State );
APerformArithmeticOperation( &State, Op, Value, State.Value );
}
return State;
}
// shiftexpr -> addexpr [SHIFTOP addexpr]* <<, >>
PARSESTATE
AParseShiftExpr( PARSESTATE State )
{
State = AParseAddExpr( State );
while (State.Error == SUCCESS) {
TCHAR Op = ANextChar( &State );
LONG Value = State.Value;
if (Op != TEXT( '<' ) && Op != TEXT( '>' )) {
break;
}
State.Token++;
if (Op != ANextChar( &State )) {
State.Error = MSG_SET_A_MISSING_OPERATOR;
return State;
}
State.Token++;
State = AParseAddExpr( State );
APerformArithmeticOperation( &State, Op, Value, State.Value );
}
return State;
}
// addexpr -> multexpr [ADDOP multexpr]* +, -
PARSESTATE
AParseAddExpr( PARSESTATE State )
{
State = AParseMultExpr( State );
while (State.Error == SUCCESS) {
TCHAR Op = ANextChar( &State );
LONG Value = State.Value;
if (Op != TEXT( '+' ) && Op != TEXT( '-' )) {
break;
}
State.Token++;
State = AParseMultExpr( State );
APerformArithmeticOperation( &State, Op, Value, State.Value );
}
return State;
}
// multexpr -> unaryexpr [MULOP unaryexpr]* *, /, %
PARSESTATE
AParseMultExpr( PARSESTATE State )
{
State = AParseUnaryExpr( State );
while (State.Error == SUCCESS) {
TCHAR Op = ANextChar( &State );
LONG Value = State.Value;
if (Op != TEXT( '*' ) && Op != TEXT( '/' ) && Op != TEXT( '%' )) {
break;
}
State.Token++;
State = AParseUnaryExpr( State );
APerformArithmeticOperation( &State, Op, Value, State.Value );
}
return State;
}
// unaryexpr -> UNARYOP unaryexpr +, -, !, ~
// ( expr ) | ()
// NUMBER
// LITERAL
PARSESTATE
AParseUnaryExpr( PARSESTATE State )
{
TCHAR c = ANextChar( &State );
PTCHAR FirstChar;
PTCHAR EndOfName;
if (c == NULLC) {
State.Error = MSG_SET_A_MISSING_OPERAND;
return State;
}
// ( expr )
if (c == TEXT( '(' )) {
State.Token++;
State = AParseExpr( State );
if (State.Error != SUCCESS) {
return State;
}
c = ANextChar( &State );
if (c != TEXT( ')' )) {
State.Error = MSG_SET_A_MISMATCHED_PARENS;
} else {
State.Token++;
}
return State;
}
// UNARYOP unaryexpr
if (_tcschr( szUnaryOps, c ) != NULL) {
State.Token++;
State = AParseUnaryExpr( State );
if (State.Error != SUCCESS) {
return State;
}
APerformUnaryOperation( &State, c, State.Value );
return State;
}
// NUMBER
if (_istdigit(c)) {
errno = 0;
State.Value = _tcstol( State.Token, &State.Token, 0 );
if (State.Value == LONG_MAX && errno == ERANGE) {
State.Error = MSG_SET_NUMBER_TOO_LARGE;
} else if (_istdigit( *State.Token ) || _istalpha( *State.Token )) {
State.Error = MSG_SET_A_INVALID_NUMBER;
}
return State;
}
// Must be literal
if (!AParseVariable( &State, &FirstChar, &EndOfName )) {
State.Error = MSG_SET_A_MISSING_OPERAND;
return State;
}
State.Value = AGetValue( FirstChar, EndOfName );
return State;
}
/*** SetArithWork - set environment variable to value of arithmetic expression
*
* Purpose:
* Set environment variable to value of arithmetic expression
*
* int SetArithWork(TCHAR *tas)
*
* Args:
* tas - pointer to null terminated string of the form:
*
* VARNAME=expression
*
* Returns:
* If valid expression, return SUCCESS otherwise FAILURE.
*
*/
int SetArithWork(TCHAR *tas)
{
PARSESTATE State;
//
// If no input, declare an error
//
if (!tas || !*tas) {
PutStdErr(MSG_BAD_SYNTAX, NOARGS);
return(FAILURE) ;
}
//
// Set up for parsing
//
State.Token = StripQuotes( tas );
State.Value = 0;
State.Error = SUCCESS;
State = AParseExpr( State );
if (State.Error == SUCCESS && ANextChar( &State ) != NULLC) {
State.Error = MSG_SET_A_MISSING_OPERATOR;
}
if (State.Error != SUCCESS) {
PutStdErr( State.Error, NOARGS );
//printf( "%ws\n", tas );
//printf( "%*s\n", State.Token - tas + 1, "^" );
} else if (!CurrentBatchFile) {
cmd_printf( TEXT("%d"), State.Value ) ;
}
return State.Error;
}