/*++ Copyright (c) 1988-1999 Microsoft Corporation Module Name: cenv.c Abstract: Environment variable support --*/ #include "cmd.h" struct envdata CmdEnv ; // Holds info to manipulate Cmd's environment struct envdata * penvOrig; // 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(""), &CmdEnv)) ; } else { tas = TokStr(n->argptr, TEXT(";"), TS_WSPACE | TS_NWSPACE) ; if (*tas) { return(SetEnvVar(flag ? PathStr : AppendStr, tas, &CmdEnv)) ; } cmd_printf(Fmt16, flag ? PathStr : AppendStr, GetEnvVar(flag ? PathStr : AppendStr), &CmdEnv) ; } 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), &CmdEnv))) ; } else { return(LastRetCode = SetEnvVar(PromptStr, TokStr(n->argptr, NULL, TS_WSPACE), &CmdEnv) ) ; } } /*** 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, &CmdEnv)) ; } 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()) ; // // 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, &CmdEnv)) ; } tas = TokStr(n->argptr, ONEQSTR, TS_WSPACE|TS_SDTOKENS) ; if (!*tas) return(DisplayEnv()) ; 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, &CmdEnv)) ; } } } /*** 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 = GetEnvironmentStrings(); 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) { return(FAILURE); } // // 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 = GetEnvironmentStrings(); 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) { return(NULL); } 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 ; /* Ptr to environment */ unsigned size ; /* Length of current env string */ envptr = GetEnvironmentStrings(); 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) ; } /*** 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, env) TCHAR *varname ; TCHAR *varvalue ; struct envdata *env ; { int retvalue; PromptValid = FALSE; // Force it to be recalculated DBG_UNREFERENCED_PARAMETER( env ); if (!_tcslen(varvalue)) { varvalue = NULL; // null to remove from env } retvalue = SetEnvironmentVariable(varname, varvalue); if (CmdEnv.handle != GetEnvironmentStrings()) { MEMORY_BASIC_INFORMATION MemoryInfo; CmdEnv.handle = GetEnvironmentStrings(); CmdEnv.cursize = GetEnvCb(CmdEnv.handle); if (VirtualQuery( CmdEnv.handle, &MemoryInfo, sizeof( MemoryInfo ) ) == sizeof( MemoryInfo )) { CmdEnv.maxsize = (UINT)MemoryInfo.RegionSize; } else { CmdEnv.maxsize = CmdEnv.cursize; } } else { CmdEnv.cursize = GetEnvCb(CmdEnv.handle); } return !retvalue; } /*** 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]; PTCHAR 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); } /*** 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. * - M008 - This function's copy code was moved to new function MoveEnv. * */ 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->cursize = CmdEnv.cursize ; cce->maxsize = CmdEnv.maxsize ; cce->handle = VirtualAlloc( NULL, cce->maxsize, MEM_COMMIT, PAGE_READWRITE ); if (cce->handle == NULL) { HeapFree( GetProcessHeap( ), 0, cce ); PutStdErr( MSG_OUT_OF_ENVIRON_SPACE, NOARGS ); return NULL; } if (!MoveEnv( cce->handle, CmdEnv.handle, GetEnvCb( CmdEnv.handle ))) { VirtualFree( cce->handle, 0, MEM_RELEASE ); HeapFree( GetProcessHeap( ), 0, cce ); 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) { ULONG cursize; cursize = GetEnvCb( env->handle ); if (MoveEnv( CmdEnv.handle, env->handle, cursize )) { CmdEnv.cursize = cursize ; } VirtualFree( env->handle, 0, MEM_RELEASE ); HeapFree( GetProcessHeap( ), 0, env ); } /*** 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 * */ 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) ; } ULONG GetEnvCb( TCHAR *penv ) { ULONG cb = 0; if (penv == NULL) { return (0); } while ( (*penv) || (*(penv+1))) { cb++; penv++; } return (cb+2) * sizeof(TCHAR); } // // expr -> assign [, assign]* , // // assign -> orlogexpr | // VAR ASSIGNOP assign = // // 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 ) ; if (SetEnvVar( Start, Result, &CmdEnv ) != 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 = // 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)) { State.Value = _tcstoul( State.Token, &State.Token, 0 ); if (State.Value == ULONG_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; }