Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1518 lines
37 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. cenv.c
  5. Abstract:
  6. Environment variable support
  7. --*/
  8. #include "cmd.h"
  9. struct envdata CmdEnv ; // Holds info to manipulate Cmd's environment
  10. struct envdata * penvOrig; // original environment setup used with eStart
  11. extern TCHAR PathStr[], PromptStr[] ;
  12. extern TCHAR AppendStr[]; /* @@ */
  13. extern CHAR InternalError[] ;
  14. extern TCHAR Fmt16[], Fmt17[], EnvErr[] ;
  15. extern TCHAR SetArithStr[] ;
  16. extern TCHAR SetPromptStr[] ;
  17. extern unsigned flgwd ;
  18. extern TCHAR CdStr[] ;
  19. extern TCHAR DatStr[] ;
  20. extern TCHAR TimStr[] ;
  21. extern TCHAR ErrStr[] ;
  22. extern TCHAR CmdExtVerStr[] ;
  23. extern unsigned LastRetCode;
  24. extern BOOL CtrlCSeen;
  25. extern UINT CurrentCP;
  26. extern BOOLEAN PromptValid;
  27. extern int glBatType; // to distinguish OS/2 vs DOS errorlevel behavior depending on a script file name
  28. int SetArithWork(TCHAR *tas);
  29. unsigned
  30. SetLastRetCodeIfError(
  31. unsigned RetCode
  32. )
  33. {
  34. if (RetCode != 0) {
  35. LastRetCode = RetCode;
  36. }
  37. return RetCode;
  38. }
  39. /*** ePath - Begin the execution of a Path Command
  40. *
  41. * Purpose:
  42. * If the command has no argument display the current value of the PATH
  43. * environment variable. Otherwise, change the value of the Path
  44. * environment variable to the argument.
  45. *
  46. * int ePath(struct cmdnode *n)
  47. *
  48. * Args:
  49. * n - the parse tree node containing the path command
  50. *
  51. * Returns:
  52. * If changing the PATH variable, whatever SetEnvVar() returns.
  53. * SUCCESS, otherwise.
  54. *
  55. */
  56. int ePath(n)
  57. struct cmdnode *n ;
  58. {
  59. if (glBatType != CMD_TYPE) {
  60. // if set command is executed from .bat file OR entered at command prompt
  61. return( SetLastRetCodeIfError(PathWork( n, 1 )));
  62. }
  63. else {
  64. return( LastRetCode = PathWork( n, 1 ) );
  65. }
  66. }
  67. /*** eAppend - Entry point for Append routine
  68. *
  69. * Purpose:
  70. * to call Append and pass it a pointer to the command line
  71. * arguments
  72. *
  73. * Args:
  74. * a pointer to the command node structure
  75. *
  76. */
  77. int eAppend(n)
  78. struct cmdnode *n ;
  79. {
  80. if (glBatType != CMD_TYPE) {
  81. // if set command is executed from .bat file OR entered at command prompt
  82. return( SetLastRetCodeIfError(PathWork( n, 0 )));
  83. }
  84. else {
  85. return( LastRetCode = PathWork( n, 0 ) );
  86. }
  87. }
  88. int PathWork(n, flag)
  89. struct cmdnode *n ;
  90. int flag; /* 0 = AppendStr, 1 = PathStr */
  91. {
  92. TCHAR *tas ; /* Tokenized argument string */
  93. TCHAR c ;
  94. /* M014 - If the only argument is a single ";", then we have to set
  95. * a NULL path.
  96. */
  97. if ( n->argptr ) {
  98. c = *(EatWS(n->argptr, NULL)) ;
  99. } else {
  100. c = NULLC;
  101. }
  102. if ((!c || c == NLN) && /* If args are all whitespace */
  103. mystrchr(n->argptr, TEXT(';'))) {
  104. return(SetEnvVar(flag ? PathStr : AppendStr, TEXT(""), &CmdEnv)) ;
  105. } else {
  106. tas = TokStr(n->argptr, TEXT(";"), TS_WSPACE | TS_NWSPACE) ;
  107. if (*tas)
  108. {
  109. return(SetEnvVar(flag ? PathStr : AppendStr, tas, &CmdEnv)) ;
  110. }
  111. cmd_printf(Fmt16, flag ? PathStr : AppendStr,
  112. GetEnvVar(flag ? PathStr : AppendStr), &CmdEnv) ;
  113. }
  114. return(SUCCESS) ;
  115. }
  116. /*** ePrompt - begin the execution of the Prompt command
  117. *
  118. * Purpose:
  119. * To modifiy the Prompt environment variable.
  120. *
  121. * int ePrompt(struct cmdnode *n)
  122. *
  123. * Args:
  124. * n - the parse tree node containing the prompt command
  125. *
  126. * Returns:
  127. * Whatever SetEnvVar() returns.
  128. *
  129. */
  130. int ePrompt(n)
  131. struct cmdnode *n ;
  132. {
  133. if (glBatType != CMD_TYPE) {
  134. // if set command is executed from .bat file OR entered at command prompt
  135. return(SetLastRetCodeIfError(SetEnvVar(PromptStr, TokStr(n->argptr, NULL, TS_WSPACE), &CmdEnv))) ;
  136. }
  137. else {
  138. return(LastRetCode = SetEnvVar(PromptStr, TokStr(n->argptr, NULL, TS_WSPACE), &CmdEnv) ) ;
  139. }
  140. }
  141. /*** eSet - execute a Set command
  142. *
  143. * Purpose:
  144. * To set/modify an environment or to display the current environment
  145. * contents.
  146. *
  147. * int eSet(struct cmdnode *n)
  148. *
  149. * Args:
  150. * n - the parse tree node containing the set command
  151. *
  152. * Returns:
  153. * If setting and the command is syntactically correct, whatever SetEnvVar()
  154. * returns. Otherwise, FAILURE.
  155. *
  156. * If displaying, SUCCESS is always returned.
  157. *
  158. */
  159. int eSet(n)
  160. struct cmdnode *n ;
  161. {
  162. if (glBatType != CMD_TYPE) {
  163. // if set command is executed from .bat file OR entered at command prompt
  164. return( SetLastRetCodeIfError(SetWork( n )));
  165. }
  166. else {
  167. return( LastRetCode = SetWork( n ) );
  168. }
  169. }
  170. /*** SetPromptUser - set environment variable to value entered by user.
  171. *
  172. * Purpose:
  173. * Set environment variable to value entered by user.
  174. *
  175. * int SetPromptUser(TCHAR *tas)
  176. *
  177. * Args:
  178. * tas - pointer to null terminated string of the form:
  179. *
  180. * VARNAME=promptString
  181. *
  182. * Returns:
  183. * If valid expression, return SUCCESS otherwise FAILURE.
  184. *
  185. */
  186. int SetPromptUser(TCHAR *tas)
  187. {
  188. TCHAR *wptr;
  189. TCHAR *tptr;
  190. ULONG dwOutputModeOld;
  191. ULONG dwOutputModeCur;
  192. ULONG dwInputModeOld;
  193. ULONG dwInputModeCur;
  194. BOOLEAN fOutputModeSet = FALSE;
  195. BOOLEAN fInputModeSet = FALSE;
  196. HANDLE hndStdOut = NULL;
  197. HANDLE hndStdIn = NULL;
  198. DWORD cch;
  199. TCHAR szValueBuffer[ 1024 ];
  200. //
  201. // Find first non-blank argument.
  202. //
  203. if (tas != NULL)
  204. while (*tas && *tas <= SPACE)
  205. tas += 1;
  206. // If no input, declare an error
  207. //
  208. if (!tas || !*tas) {
  209. PutStdErr(MSG_BAD_SYNTAX, NOARGS);
  210. return(FAILURE) ;
  211. }
  212. //
  213. // See if first argument is quoted. If so, strip off
  214. // leading quote, spaces and trailing quote.
  215. //
  216. if (*tas == QUOTE) {
  217. tas += 1;
  218. while (*tas && *tas <= SPACE)
  219. tas += 1;
  220. tptr = _tcsrchr(tas, QUOTE);
  221. if (tptr)
  222. *tptr = NULLC;
  223. }
  224. //
  225. // Find the equal sign in the argument.
  226. //
  227. wptr = _tcschr(tas, EQ);
  228. //
  229. // If no equal sign, error.
  230. //
  231. if (!wptr) {
  232. PutStdErr(MSG_BAD_SYNTAX, NOARGS);
  233. return(FAILURE) ;
  234. }
  235. //
  236. // Found the equal sign, so left of equal sign is variable name
  237. // and right of equal sign is prompt string. Dont allow user to set
  238. // a variable name that begins with an equal sign, since those
  239. // are reserved for drive current directories.
  240. //
  241. *wptr++ = NULLC;
  242. //
  243. // See if second argument is quoted. If so, strip off
  244. // leading quote, spaces and trailing quote.
  245. //
  246. if (*wptr == QUOTE) {
  247. wptr += 1;
  248. while (*wptr && *wptr <= SPACE)
  249. wptr += 1;
  250. tptr = _tcsrchr(wptr, QUOTE);
  251. if (tptr)
  252. *tptr = NULLC;
  253. }
  254. if (*wptr == EQ) {
  255. PutStdErr(MSG_BAD_SYNTAX, NOARGS);
  256. return(FAILURE) ;
  257. }
  258. hndStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
  259. if (GetConsoleMode( hndStdOut, &dwOutputModeOld) ) {
  260. // make sure CRLF is processed correctly
  261. dwOutputModeCur = dwOutputModeOld | ENABLE_PROCESSED_OUTPUT;
  262. fOutputModeSet = TRUE;
  263. SetConsoleMode(hndStdOut,dwOutputModeCur);
  264. GetLastError();
  265. }
  266. hndStdIn = GetStdHandle(STD_INPUT_HANDLE);
  267. if (GetConsoleMode( hndStdIn, &dwInputModeOld) ) {
  268. // make sure input is processed correctly
  269. dwInputModeCur = dwInputModeOld | ENABLE_LINE_INPUT |
  270. ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT;
  271. fInputModeSet = TRUE;
  272. SetConsoleMode(hndStdIn,dwInputModeCur);
  273. GetLastError();
  274. }
  275. //
  276. // Loop till the user enters a value for the variable.
  277. //
  278. while (TRUE) {
  279. PutStdOut(MSG_LITERAL_TEXT, ONEARG, wptr );
  280. szValueBuffer[0] = NULLC;
  281. if (ReadBufFromInput( GetStdHandle(STD_INPUT_HANDLE),
  282. szValueBuffer,
  283. sizeof(szValueBuffer)/sizeof(TCHAR),
  284. &cch
  285. ) != 0 &&
  286. cch != 0
  287. ) {
  288. //
  289. // Strip off any trailing CRLF
  290. //
  291. while (cch > 0 && szValueBuffer[cch-1] < SPACE)
  292. cch -= 1;
  293. break;
  294. }
  295. else {
  296. cch = 0;
  297. break;
  298. }
  299. if (!FileIsDevice(STDIN) || !(flgwd & 1))
  300. cmd_printf(CrLf) ;
  301. }
  302. if (fOutputModeSet) {
  303. SetConsoleMode( hndStdOut, dwOutputModeOld );
  304. }
  305. if (fInputModeSet) {
  306. SetConsoleMode( hndStdIn, dwInputModeOld );
  307. }
  308. if (cch) {
  309. szValueBuffer[cch] = NULLC;
  310. return(SetEnvVar(tas, szValueBuffer, &CmdEnv)) ;
  311. } else {
  312. return(FAILURE);
  313. }
  314. }
  315. int SetWork(n)
  316. struct cmdnode *n ;
  317. {
  318. TCHAR *tas ; /* Tokenized argument string */
  319. TCHAR *wptr ; /* Work pointer */
  320. int i ; /* Work variable */
  321. //
  322. // If extensions are enabled, things are different
  323. //
  324. if (fEnableExtensions) {
  325. tas = n->argptr;
  326. //
  327. // Find first non-blank argument.
  328. //
  329. if (tas != NULL)
  330. while (*tas && *tas <= SPACE)
  331. tas += 1;
  332. //
  333. // No arguments, same as old behavior. Display current
  334. // set of environment variables.
  335. //
  336. if (!tas || !*tas)
  337. return(DisplayEnv()) ;
  338. //
  339. // See if /A switch given. If so, let arithmetic
  340. // expression evaluator do the work.
  341. //
  342. if (!_tcsnicmp(tas, SetArithStr, 2))
  343. return SetArithWork(tas+2);
  344. //
  345. // See if /P switch given. If so, prompt user for value
  346. //
  347. if (!_tcsnicmp(tas, SetPromptStr, 2))
  348. return SetPromptUser(tas+2);
  349. //
  350. // See if first argument is quoted. If so, strip off
  351. // leading quote, spaces and trailing quote.
  352. //
  353. if (*tas == QUOTE) {
  354. tas += 1;
  355. while (*tas && *tas <= SPACE)
  356. tas += 1;
  357. wptr = _tcsrchr(tas, QUOTE);
  358. if (wptr)
  359. *wptr = NULLC;
  360. }
  361. //
  362. // Dont allow user to set a variable name that begins with
  363. // an equal sign, since those are reserved for drive current
  364. // directories. This check will also detect missing variable
  365. // name, e.g.
  366. //
  367. // set %LOG%=c:\tmp\log.txt
  368. //
  369. // if LOG is not defined is an invalid statement.
  370. //
  371. if (*tas == EQ) {
  372. PutStdErr(MSG_BAD_SYNTAX, NOARGS);
  373. return(FAILURE) ;
  374. }
  375. //
  376. // Find the equal sign in the argument.
  377. //
  378. wptr = _tcschr(tas, EQ);
  379. //
  380. // If no equal sign, then assume argument is variable name
  381. // and user wants to see its value. Display it.
  382. //
  383. if (!wptr)
  384. return DisplayEnvVariable(tas);
  385. //
  386. // Found the equal sign, so left of equal sign is variable name
  387. // and right of equal sign is value.
  388. //
  389. *wptr++ = NULLC;
  390. return(SetEnvVar(tas, wptr, &CmdEnv)) ;
  391. }
  392. tas = TokStr(n->argptr, ONEQSTR, TS_WSPACE|TS_SDTOKENS) ;
  393. if (!*tas)
  394. return(DisplayEnv()) ;
  395. else {
  396. for (wptr = tas, i = 0 ; *wptr ; wptr += mystrlen(wptr)+1, i++)
  397. ;
  398. /* If too many parameters were given, the second parameter */
  399. /* wasn't an equal sign, or they didn't specify a string */
  400. /* return an error message. */
  401. if ( i > 3 || *(wptr = tas+mystrlen(tas)+1) != EQ ||
  402. !mystrlen(mystrcpy(tas, StripQuotes(tas))) ) {
  403. /* M013 */ PutStdErr(MSG_BAD_SYNTAX, NOARGS);
  404. return(FAILURE) ;
  405. } else {
  406. return(SetEnvVar(tas, wptr+2, &CmdEnv)) ;
  407. }
  408. }
  409. }
  410. /*** DisplayEnvVariable - display a specific variable from the environment
  411. *
  412. * Purpose:
  413. * To display a specific variable from the current environment.
  414. *
  415. * int DisplayEnvVariable( tas )
  416. *
  417. * Returns:
  418. * SUCCESS if all goes well
  419. * FAILURE if it runs out of memory or cannot lock the env. segment
  420. */
  421. int DisplayEnvVariable(tas)
  422. TCHAR *tas;
  423. {
  424. TCHAR *envptr ;
  425. TCHAR *vstr ;
  426. unsigned size ;
  427. UINT PrefixLength;
  428. int rc;
  429. //
  430. // Get environment. If there's none, we're done.
  431. //
  432. envptr = GetEnvironmentStrings();
  433. if (envptr == (TCHAR *)NULL) {
  434. fprintf ( stderr, InternalError , "Null environment" ) ;
  435. return ( FAILURE ) ;
  436. }
  437. //
  438. // Isolate the prefix to match against.
  439. //
  440. tas = EatWS(tas, NULL);
  441. if ((vstr = mystrrchr(tas, SPACE)) != NULL) {
  442. *vstr = NULLC;
  443. }
  444. PrefixLength = mystrlen(tas);
  445. //
  446. // Walk through the environment looking for prefixes that match.
  447. //
  448. rc = FAILURE;
  449. while ((size = mystrlen(envptr)) > 0) {
  450. //
  451. // Stop soon if we see ^C
  452. //
  453. if (CtrlCSeen) {
  454. return(FAILURE);
  455. }
  456. //
  457. // If the prefix is long enough, then terminate the string and
  458. // look for a prefix match. If we match, restore the string
  459. // and display it
  460. //
  461. if (size >= PrefixLength) {
  462. TCHAR SavedChar = envptr[PrefixLength];
  463. envptr[PrefixLength] = NULLC;
  464. if (!lstrcmpi( envptr, tas )) {
  465. envptr[PrefixLength] = SavedChar;
  466. cmd_printf(Fmt17, envptr );
  467. rc = SUCCESS;
  468. } else {
  469. envptr[PrefixLength] = SavedChar;
  470. }
  471. }
  472. //
  473. // Advance to the next string
  474. //
  475. envptr += size+1 ;
  476. }
  477. if (rc != SUCCESS) {
  478. PutStdErr(MSG_ENV_VAR_NOT_FOUND, ONEARG, tas);
  479. }
  480. return(rc) ;
  481. }
  482. /*** MyGetEnvVar - get a pointer to the value of an environment variable
  483. *
  484. * Purpose:
  485. * Return a pointer to the value of the specified environment variable.
  486. *
  487. * If the variable is not found, return NULL.
  488. *
  489. * TCHAR *MyGetEnvVar(TCHAR *varname)
  490. *
  491. * Args:
  492. * varname - the name of the variable to search for
  493. *
  494. * Returns:
  495. * See above.
  496. *
  497. * Side Effects:
  498. * Returned value points to within the environment block itself, so is
  499. * not valid after a set environment variable operations is perform.
  500. */
  501. const TCHAR *
  502. MyGetEnvVarPtr(TCHAR *varname)
  503. {
  504. TCHAR *envptr ; /* Ptr to environment */
  505. TCHAR *vstr ;
  506. unsigned size ; /* Length of current env string */
  507. unsigned n ;
  508. if (varname == NULL) {
  509. return ( NULL ) ;
  510. }
  511. envptr = GetEnvironmentStrings();
  512. if (envptr == (TCHAR *)NULL) {
  513. return ( NULL ) ;
  514. }
  515. varname = EatWS(varname, NULL);
  516. if ((vstr = mystrrchr(varname, SPACE)) != NULL)
  517. *vstr = NULLC;
  518. n = mystrlen(varname);
  519. while ((size = mystrlen(envptr)) > 0) { /* M015 */
  520. if (CtrlCSeen) {
  521. return(NULL);
  522. }
  523. if (!_tcsnicmp(varname, envptr, n) && envptr[n] == TEXT( '=' )) {
  524. return envptr+n+1;
  525. }
  526. envptr += size+1 ;
  527. }
  528. return(NULL);
  529. }
  530. /*** DisplayEnv - display the environment
  531. *
  532. * Purpose:
  533. * To display the current contents of the environment.
  534. *
  535. * int DisplayEnv()
  536. *
  537. * Returns:
  538. * SUCCESS if all goes well
  539. * FAILURE if it runs out of memory or cannot lock the env. segment
  540. */
  541. int DisplayEnv()
  542. {
  543. TCHAR *envptr ; /* Ptr to environment */
  544. unsigned size ; /* Length of current env string */
  545. envptr = GetEnvironmentStrings();
  546. if (envptr == (TCHAR *)NULL) {
  547. fprintf ( stderr, InternalError , "Null environment" ) ;
  548. return( FAILURE ) ;
  549. }
  550. while ((size = mystrlen(envptr)) > 0) { /* M015 */
  551. if (CtrlCSeen) {
  552. return(FAILURE);
  553. }
  554. #if !DBG
  555. // Dont show current directory variables in retail product
  556. if (*envptr != EQ)
  557. #endif // DBG
  558. cmd_printf(Fmt17, envptr) ; /* M005 */
  559. envptr += size+1 ;
  560. }
  561. return(SUCCESS) ;
  562. }
  563. /*** SetEnvVar - controls adding/changing an environment variable
  564. *
  565. * Purpose:
  566. * Add/replace an environment variable. Grow it if necessary.
  567. *
  568. * int SetEnvVar(TCHAR *varname, TCHAR *varvalue, struct envdata *env)
  569. *
  570. * Args:
  571. * varname - name of the variable being added/replaced
  572. * varvalue - value of the variable being added/replaced
  573. * env - environment info structure being used
  574. *
  575. * Returns:
  576. * SUCCESS if the variable could be added/replaced.
  577. * FAILURE otherwise.
  578. *
  579. */
  580. int SetEnvVar(varname, varvalue, env)
  581. TCHAR *varname ;
  582. TCHAR *varvalue ;
  583. struct envdata *env ;
  584. {
  585. int retvalue;
  586. PromptValid = FALSE; // Force it to be recalculated
  587. DBG_UNREFERENCED_PARAMETER( env );
  588. if (!_tcslen(varvalue)) {
  589. varvalue = NULL; // null to remove from env
  590. }
  591. retvalue = SetEnvironmentVariable(varname, varvalue);
  592. if (CmdEnv.handle != GetEnvironmentStrings()) {
  593. MEMORY_BASIC_INFORMATION MemoryInfo;
  594. CmdEnv.handle = GetEnvironmentStrings();
  595. CmdEnv.cursize = GetEnvCb(CmdEnv.handle);
  596. if (VirtualQuery( CmdEnv.handle, &MemoryInfo, sizeof( MemoryInfo ) ) == sizeof( MemoryInfo )) {
  597. CmdEnv.maxsize = (UINT)MemoryInfo.RegionSize;
  598. }
  599. else {
  600. CmdEnv.maxsize = CmdEnv.cursize;
  601. }
  602. }
  603. else {
  604. CmdEnv.cursize = GetEnvCb(CmdEnv.handle);
  605. }
  606. return !retvalue;
  607. }
  608. /*** GetEnvVar - get the value of an environment variable
  609. *
  610. * Purpose:
  611. * Return a string containing the value of the specified environment
  612. * variable. The string value has been placed into a static buffer
  613. * that is valid until the next GetEnvVar call.
  614. *
  615. * If the variable is not found, return NULL.
  616. *
  617. * TCHAR *GetEnvVar(TCHAR *varname)
  618. *
  619. * Args:
  620. * varname - the name of the variable to search for
  621. *
  622. * Returns:
  623. * See above.
  624. *
  625. */
  626. TCHAR GetEnvVarBuffer[LBUFLEN];
  627. PTCHAR GetEnvVar(varname)
  628. PTCHAR varname ;
  629. {
  630. GetEnvVarBuffer[0] = TEXT( '\0' );
  631. if (GetEnvironmentVariable(varname, GetEnvVarBuffer, sizeof(GetEnvVarBuffer) / sizeof(TCHAR))) {
  632. return(GetEnvVarBuffer);
  633. }
  634. else
  635. if (fEnableExtensions) {
  636. if (!_tcsicmp(varname, CdStr)) {
  637. GetDir(GetEnvVarBuffer, GD_DEFAULT) ;
  638. return GetEnvVarBuffer;
  639. }
  640. else
  641. if (!_tcsicmp(varname, ErrStr)) {
  642. _stprintf( GetEnvVarBuffer, TEXT("%d"), LastRetCode );
  643. return GetEnvVarBuffer;
  644. }
  645. else
  646. if (!_tcsicmp(varname, CmdExtVerStr)) {
  647. _stprintf( GetEnvVarBuffer, TEXT("%d"), CMDEXTVERSION );
  648. return GetEnvVarBuffer;
  649. }
  650. else
  651. if (!_tcsicmp(varname, TEXT("CMDCMDLINE"))) {
  652. return GetCommandLine();
  653. }
  654. else
  655. if (!_tcsicmp(varname, DatStr)) {
  656. GetEnvVarBuffer[ PrintDate(NULL, PD_DATE, GetEnvVarBuffer, LBUFLEN) ] = NULLC;
  657. return GetEnvVarBuffer;
  658. }
  659. if( !_tcsicmp(varname, TimStr)) {
  660. GetEnvVarBuffer[ PrintTime(NULL, PT_TIME, GetEnvVarBuffer, LBUFLEN) ] = NULLC;
  661. return GetEnvVarBuffer;
  662. }
  663. if( !_tcsicmp(varname, TEXT("RANDOM"))) {
  664. _stprintf( GetEnvVarBuffer, TEXT("%d"), rand() );
  665. return GetEnvVarBuffer;
  666. }
  667. }
  668. return(NULL);
  669. }
  670. /*** CopyEnv - make a copy of the current environment
  671. *
  672. * Purpose:
  673. * Make a copy of CmdEnv and put the new handle into the newly
  674. * created envdata structure. This routine is only called by
  675. * eSetlocal and init.
  676. *
  677. * struct envdata *CopyEnv()
  678. *
  679. * Returns:
  680. * A pointer to the environment information structure.
  681. * Returns NULL if unable to allocate enough memory
  682. *
  683. * Notes:
  684. * - M001 - This function was disabled, now reenabled.
  685. * - The current environment is copied as a snapshot of how it looked
  686. * before SETLOCAL was executed.
  687. * - M008 - This function's copy code was moved to new function MoveEnv.
  688. *
  689. */
  690. struct envdata *CopyEnv()
  691. {
  692. struct envdata *cce ; /* New env info structure */
  693. cce = (struct envdata *) HeapAlloc( GetProcessHeap( ), HEAP_ZERO_MEMORY, sizeof( *cce ));
  694. if (cce == NULL) {
  695. return NULL;
  696. }
  697. cce->cursize = CmdEnv.cursize ;
  698. cce->maxsize = CmdEnv.maxsize ;
  699. cce->handle = VirtualAlloc( NULL,
  700. cce->maxsize,
  701. MEM_COMMIT,
  702. PAGE_READWRITE
  703. );
  704. if (cce->handle == NULL) {
  705. HeapFree( GetProcessHeap( ), 0, cce );
  706. PutStdErr( MSG_OUT_OF_ENVIRON_SPACE, NOARGS );
  707. return NULL;
  708. }
  709. if (!MoveEnv( cce->handle, CmdEnv.handle, GetEnvCb( CmdEnv.handle ))) {
  710. VirtualFree( cce->handle, 0, MEM_RELEASE );
  711. HeapFree( GetProcessHeap( ), 0, cce );
  712. return NULL;
  713. }
  714. return cce;
  715. }
  716. /*** ResetEnv - restore the environment
  717. *
  718. * Purpose:
  719. * Restore the environment to the way it was before the execution of
  720. * the SETLOCAL command. This function only called by eEndlocal.
  721. *
  722. * ResetEnv(struct envdata *env)
  723. *
  724. * Args:
  725. * env - structure containing handle, size and max dimensions of an
  726. * environment.
  727. *
  728. * Notes:
  729. * - M001 - This function was disabled, but has been reenabled.
  730. * - M001 - This function used to test for OLD/NEW style batch files
  731. * and delete the copy or the original environment as
  732. * appropriate. It now always deletes the original.
  733. * - M014 - Note that the modified local environment will never be
  734. * shrunk, so we can assume it will hold the old one.
  735. *
  736. */
  737. void ResetEnv( struct envdata *env)
  738. {
  739. ULONG cursize;
  740. cursize = GetEnvCb( env->handle );
  741. if (MoveEnv( CmdEnv.handle, env->handle, cursize )) {
  742. CmdEnv.cursize = cursize ;
  743. }
  744. VirtualFree( env->handle, 0, MEM_RELEASE );
  745. HeapFree( GetProcessHeap( ), 0, env );
  746. }
  747. /*** MoveEnv - Move the contents of the environment (M008 - New function)
  748. *
  749. * Purpose:
  750. * Used by CopyEnv, this function moves the existing
  751. * environment contents to the new location.
  752. *
  753. * MoveEnv(unsigned thndl, unsigned shndl, unsigned cnt)
  754. *
  755. * Args:
  756. * thndl - Handle of target environment
  757. * shndl - Handle of source environment
  758. * cnt - byte count to move
  759. *
  760. * Returns:
  761. * TRUE if no errors
  762. * FALSE otherwise
  763. *
  764. */
  765. MoveEnv(tenvptr, senvptr, cnt)
  766. TCHAR *senvptr ; /* Ptr into source env seg */
  767. TCHAR *tenvptr ; /* Ptr into target env seg */
  768. ULONG cnt ;
  769. {
  770. if ((tenvptr == NULL) ||
  771. (senvptr == NULL)) {
  772. fprintf(stderr, InternalError, "Null environment") ;
  773. return(FALSE) ;
  774. }
  775. memcpy(tenvptr, senvptr, cnt) ; /* M015 */
  776. return(TRUE) ;
  777. }
  778. ULONG
  779. GetEnvCb( TCHAR *penv ) {
  780. ULONG cb = 0;
  781. if (penv == NULL) {
  782. return (0);
  783. }
  784. while ( (*penv) || (*(penv+1))) {
  785. cb++;
  786. penv++;
  787. }
  788. return (cb+2) * sizeof(TCHAR);
  789. }
  790. //
  791. // expr -> assign [, assign]* ,
  792. //
  793. // assign -> orlogexpr |
  794. // VAR ASSIGNOP assign <op>=
  795. //
  796. // orlogexpr -> xorlogexpr [| xorlogexpr]* |
  797. //
  798. // xorlogexpr -> andlogexpr [^ andlogexpr]* ^
  799. //
  800. // andlogexpr -> shiftexpr [& shiftexpr]* &
  801. //
  802. // shiftexpr -> addexpr [SHIFTOP addexpr]* <<, >>
  803. //
  804. // addexpr -> multexpr [ADDOP multexpr]* +, -
  805. //
  806. // multexpr -> unaryexpr [MULOP unaryexpr]* *, /, %
  807. //
  808. // unaryexpr -> ( expr ) | ()
  809. // UNARYOP unaryexpr +, -, !, ~
  810. //
  811. TCHAR szOps[] = TEXT("<>+-*/%()|^&=,");
  812. TCHAR szUnaryOps[] = TEXT("+-~!");
  813. typedef struct {
  814. PTCHAR Token;
  815. LONG Value;
  816. DWORD Error;
  817. } PARSESTATE, *PPARSESTATE;
  818. VOID
  819. APerformUnaryOperation( PPARSESTATE State, TCHAR Op, LONG Value )
  820. {
  821. switch (Op) {
  822. case TEXT( '+' ):
  823. State->Value = Value;
  824. break;
  825. case TEXT( '-' ):
  826. State->Value = -Value;
  827. break;
  828. case TEXT( '~' ):
  829. State->Value = ~Value;
  830. break;
  831. case TEXT( '!' ):
  832. State->Value = !Value;
  833. break;
  834. default:
  835. printf( "APerformUnaryOperation: '%c'\n", Op);
  836. break;
  837. }
  838. }
  839. VOID
  840. APerformArithmeticOperation( PPARSESTATE State, TCHAR Op, LONG Left, LONG Right )
  841. {
  842. switch (Op) {
  843. case TEXT( '<' ):
  844. State->Value = (Right >= 8 * sizeof( Left << Right))
  845. ? 0
  846. : (Left << Right);
  847. break;
  848. case TEXT( '>' ):
  849. State->Value = (Right >= 8 * sizeof( Left >> Right ))
  850. ? (Left < 0 ? -1 : 0)
  851. : (Left >> Right);
  852. break;
  853. case TEXT( '+' ):
  854. State->Value = Left + Right;
  855. break;
  856. case TEXT( '-' ):
  857. State->Value = Left - Right;
  858. break;
  859. case TEXT( '*' ):
  860. State->Value = Left * Right;
  861. break;
  862. case TEXT( '/' ):
  863. if (Right == 0) {
  864. State->Error = MSG_SET_A_DIVIDE_BY_ZERO;
  865. } else {
  866. State->Value = Left / Right;
  867. }
  868. break;
  869. case TEXT( '%' ):
  870. if (Right == 0) {
  871. State->Error = MSG_SET_A_DIVIDE_BY_ZERO;
  872. } else {
  873. State->Value = Left % Right;
  874. }
  875. break;
  876. case TEXT( '|' ):
  877. State->Value = Left | Right;
  878. break;
  879. case TEXT( '^' ):
  880. State->Value = Left ^ Right;
  881. break;
  882. case TEXT( '&' ):
  883. State->Value = Left & Right;
  884. break;
  885. case TEXT( '=' ):
  886. State->Value = Right;
  887. break;
  888. default:
  889. printf( "APerformArithmeticOperation: '%c'\n", Op);
  890. }
  891. }
  892. //
  893. // Return the numeric value of an environment variable (or 0)
  894. //
  895. LONG
  896. AGetValue( PTCHAR Start, PTCHAR End )
  897. {
  898. TCHAR c = *End;
  899. const TCHAR *Value;
  900. PTCHAR Dummy;
  901. *End = NULLC;
  902. Value = MyGetEnvVarPtr( Start );
  903. *End = c;
  904. if (Value == NULL) {
  905. return 0;
  906. }
  907. return _tcstol( Value, &Dummy, 0);
  908. }
  909. DWORD
  910. ASetValue( PTCHAR Start, PTCHAR End, LONG Value )
  911. {
  912. TCHAR Result[32];
  913. TCHAR c = *End;
  914. DWORD Return = SUCCESS;
  915. *End = NULLC;
  916. _sntprintf( Result, 32, TEXT("%d"), Value ) ;
  917. if (SetEnvVar( Start, Result, &CmdEnv ) != SUCCESS) {
  918. Return = GetLastError();
  919. }
  920. *End = c;
  921. return Return;
  922. }
  923. //
  924. // Forward decls
  925. //
  926. PARSESTATE AParseAddExpr( PARSESTATE State );
  927. PARSESTATE AParseAndLogExpr( PARSESTATE State );
  928. PARSESTATE AParseAssign( PARSESTATE State );
  929. PARSESTATE AParseExpr( PARSESTATE State );
  930. PARSESTATE AParseMultExpr( PARSESTATE State );
  931. PARSESTATE AParseOrLogExpr( PARSESTATE State );
  932. PARSESTATE AParseShiftExpr( PARSESTATE State );
  933. PARSESTATE AParseUnaryExpr( PARSESTATE State );
  934. PARSESTATE AParseXorLogExpr( PARSESTATE State );
  935. //
  936. // Skip whitespace and return next character
  937. //
  938. BOOL ASkipWhiteSpace( PPARSESTATE State )
  939. {
  940. while (*State->Token != NULLC && *State->Token <= SPACE) {
  941. State->Token++;
  942. }
  943. return *State->Token != NULLC;
  944. }
  945. TCHAR ANextChar( PPARSESTATE State )
  946. {
  947. ASkipWhiteSpace( State );
  948. return *State->Token;
  949. }
  950. BOOL AParseVariable( PPARSESTATE State, PTCHAR *FirstChar, PTCHAR *EndOfName )
  951. {
  952. TCHAR c = ANextChar( State );
  953. //
  954. // Next char is a digit or operator, can't be a variable
  955. //
  956. if (c == NULLC
  957. || _istdigit( c )
  958. || _tcschr( szOps, c ) != NULL
  959. || _tcschr( szUnaryOps, c ) != NULL) {
  960. return FALSE;
  961. }
  962. *FirstChar = State->Token;
  963. //
  964. // find end of variable
  965. //
  966. while (*State->Token &&
  967. *State->Token > SPACE &&
  968. !_tcschr( szUnaryOps, *State->Token ) &&
  969. !_tcschr( szOps, *State->Token ) ) {
  970. State->Token += 1;
  971. }
  972. *EndOfName = State->Token;
  973. return TRUE;
  974. }
  975. // expr -> assign [, assign]*
  976. PARSESTATE AParseExpr( PARSESTATE State )
  977. {
  978. State = AParseAssign( State );
  979. while (State.Error == SUCCESS) {
  980. if (ANextChar( &State ) != TEXT( ',' )) {
  981. break;
  982. }
  983. State.Token++;
  984. State = AParseAssign( State );
  985. }
  986. return State;
  987. }
  988. // assign -> VAR ASSIGNOP assign |
  989. // orlogexpr
  990. PARSESTATE AParseAssign( PARSESTATE State )
  991. {
  992. TCHAR c = ANextChar( &State );
  993. PARSESTATE SavedState;
  994. SavedState = State;
  995. if (c == NULLC) {
  996. State.Error = MSG_SET_A_MISSING_OPERAND;
  997. return State;
  998. }
  999. //
  1000. // See if we have VAR ASSIGNOP
  1001. //
  1002. do {
  1003. PTCHAR FirstChar;
  1004. PTCHAR EndOfName;
  1005. TCHAR OpChar;
  1006. LONG OldValue;
  1007. //
  1008. // Parse off variable
  1009. //
  1010. if (!AParseVariable( &State, &FirstChar, &EndOfName )) {
  1011. break;
  1012. }
  1013. //
  1014. // Look for <op>=
  1015. //
  1016. OpChar = ANextChar( &State );
  1017. if (OpChar == NULLC) {
  1018. break;
  1019. }
  1020. if (OpChar != TEXT( '=' )) {
  1021. if (_tcschr( szOps, OpChar ) == NULL) {
  1022. break;
  1023. }
  1024. State.Token++;
  1025. if (OpChar == TEXT( '<' ) || OpChar == TEXT( '>')) {
  1026. if (ANextChar( &State ) != OpChar) {
  1027. break;
  1028. }
  1029. State.Token++;
  1030. }
  1031. }
  1032. if (ANextChar( &State ) != TEXT( '=' )) {
  1033. break;
  1034. }
  1035. State.Token++;
  1036. //
  1037. // OpChar is the sort of operation to apply before assignment
  1038. // State has been advance to, hopefully, another assign. Parse it
  1039. // and see where we get
  1040. //
  1041. State = AParseAssign( State );
  1042. if (State.Error != SUCCESS) {
  1043. return State;
  1044. }
  1045. OldValue = AGetValue( FirstChar, EndOfName );
  1046. //
  1047. // Perform the operation and the assignment
  1048. //
  1049. APerformArithmeticOperation( &State, OpChar, OldValue, State.Value );
  1050. if (State.Error != SUCCESS) {
  1051. return State;
  1052. }
  1053. State.Error = ASetValue( FirstChar, EndOfName, State.Value );
  1054. return State;
  1055. } while ( FALSE );
  1056. //
  1057. // Must be orlogexpr. Go back and parse over
  1058. //
  1059. return AParseOrLogExpr( SavedState );
  1060. }
  1061. // orlogexpr -> xorlogexpr [| xorlogexpr]* |
  1062. PARSESTATE
  1063. AParseOrLogExpr( PARSESTATE State )
  1064. {
  1065. State = AParseXorLogExpr( State );
  1066. while (State.Error == SUCCESS) {
  1067. TCHAR Op = ANextChar( &State );
  1068. LONG Value = State.Value;
  1069. if (Op != TEXT( '|' )) {
  1070. break;
  1071. }
  1072. State.Token++;
  1073. State = AParseXorLogExpr( State );
  1074. APerformArithmeticOperation( &State, Op, Value, State.Value );
  1075. }
  1076. return State;
  1077. }
  1078. // xorlogexpr -> andlogexpr [^ andlogexpr]* ^
  1079. PARSESTATE
  1080. AParseXorLogExpr( PARSESTATE State )
  1081. {
  1082. State = AParseAndLogExpr( State );
  1083. while (State.Error == SUCCESS) {
  1084. TCHAR Op = ANextChar( &State );
  1085. LONG Value = State.Value;
  1086. if (Op != TEXT( '^' )) {
  1087. break;
  1088. }
  1089. State.Token++;
  1090. State = AParseAndLogExpr( State );
  1091. APerformArithmeticOperation( &State, Op, Value, State.Value );
  1092. }
  1093. return State;
  1094. }
  1095. // andlogexpr -> shiftexpr [& shiftexpr]* &
  1096. PARSESTATE
  1097. AParseAndLogExpr( PARSESTATE State )
  1098. {
  1099. State = AParseShiftExpr( State );
  1100. while (State.Error == SUCCESS) {
  1101. TCHAR Op = ANextChar( &State );
  1102. LONG Value = State.Value;
  1103. if (Op != TEXT( '&' )) {
  1104. break;
  1105. }
  1106. State.Token++;
  1107. State = AParseShiftExpr( State );
  1108. APerformArithmeticOperation( &State, Op, Value, State.Value );
  1109. }
  1110. return State;
  1111. }
  1112. // shiftexpr -> addexpr [SHIFTOP addexpr]* <<, >>
  1113. PARSESTATE
  1114. AParseShiftExpr( PARSESTATE State )
  1115. {
  1116. State = AParseAddExpr( State );
  1117. while (State.Error == SUCCESS) {
  1118. TCHAR Op = ANextChar( &State );
  1119. LONG Value = State.Value;
  1120. if (Op != TEXT( '<' ) && Op != TEXT( '>' )) {
  1121. break;
  1122. }
  1123. State.Token++;
  1124. if (Op != ANextChar( &State )) {
  1125. State.Error = MSG_SET_A_MISSING_OPERATOR;
  1126. return State;
  1127. }
  1128. State.Token++;
  1129. State = AParseAddExpr( State );
  1130. APerformArithmeticOperation( &State, Op, Value, State.Value );
  1131. }
  1132. return State;
  1133. }
  1134. // addexpr -> multexpr [ADDOP multexpr]* +, -
  1135. PARSESTATE
  1136. AParseAddExpr( PARSESTATE State )
  1137. {
  1138. State = AParseMultExpr( State );
  1139. while (State.Error == SUCCESS) {
  1140. TCHAR Op = ANextChar( &State );
  1141. LONG Value = State.Value;
  1142. if (Op != TEXT( '+' ) && Op != TEXT( '-' )) {
  1143. break;
  1144. }
  1145. State.Token++;
  1146. State = AParseMultExpr( State );
  1147. APerformArithmeticOperation( &State, Op, Value, State.Value );
  1148. }
  1149. return State;
  1150. }
  1151. // multexpr -> unaryexpr [MULOP unaryexpr]* *, /, %
  1152. PARSESTATE
  1153. AParseMultExpr( PARSESTATE State )
  1154. {
  1155. State = AParseUnaryExpr( State );
  1156. while (State.Error == SUCCESS) {
  1157. TCHAR Op = ANextChar( &State );
  1158. LONG Value = State.Value;
  1159. if (Op != TEXT( '*' ) && Op != TEXT( '/' ) && Op != TEXT( '%' )) {
  1160. break;
  1161. }
  1162. State.Token++;
  1163. State = AParseUnaryExpr( State );
  1164. APerformArithmeticOperation( &State, Op, Value, State.Value );
  1165. }
  1166. return State;
  1167. }
  1168. // unaryexpr -> UNARYOP unaryexpr +, -, !, ~
  1169. // ( expr ) | ()
  1170. // NUMBER
  1171. // LITERAL
  1172. PARSESTATE
  1173. AParseUnaryExpr( PARSESTATE State )
  1174. {
  1175. TCHAR c = ANextChar( &State );
  1176. PTCHAR FirstChar;
  1177. PTCHAR EndOfName;
  1178. if (c == NULLC) {
  1179. State.Error = MSG_SET_A_MISSING_OPERAND;
  1180. return State;
  1181. }
  1182. // ( expr )
  1183. if (c == TEXT( '(' )) {
  1184. State.Token++;
  1185. State = AParseExpr( State );
  1186. if (State.Error != SUCCESS) {
  1187. return State;
  1188. }
  1189. c = ANextChar( &State );
  1190. if (c != TEXT( ')' )) {
  1191. State.Error = MSG_SET_A_MISMATCHED_PARENS;
  1192. } else {
  1193. State.Token++;
  1194. }
  1195. return State;
  1196. }
  1197. // UNARYOP unaryexpr
  1198. if (_tcschr( szUnaryOps, c ) != NULL) {
  1199. State.Token++;
  1200. State = AParseUnaryExpr( State );
  1201. if (State.Error != SUCCESS) {
  1202. return State;
  1203. }
  1204. APerformUnaryOperation( &State, c, State.Value );
  1205. return State;
  1206. }
  1207. // NUMBER
  1208. if (_istdigit(c)) {
  1209. State.Value = _tcstoul( State.Token, &State.Token, 0 );
  1210. if (State.Value == ULONG_MAX && errno == ERANGE) {
  1211. State.Error = MSG_SET_NUMBER_TOO_LARGE;
  1212. } else if (_istdigit( *State.Token ) || _istalpha( *State.Token )) {
  1213. State.Error = MSG_SET_A_INVALID_NUMBER;
  1214. }
  1215. return State;
  1216. }
  1217. // Must be literal
  1218. if (!AParseVariable( &State, &FirstChar, &EndOfName )) {
  1219. State.Error = MSG_SET_A_MISSING_OPERAND;
  1220. return State;
  1221. }
  1222. State.Value = AGetValue( FirstChar, EndOfName );
  1223. return State;
  1224. }
  1225. /*** SetArithWork - set environment variable to value of arithmetic expression
  1226. *
  1227. * Purpose:
  1228. * Set environment variable to value of arithmetic expression
  1229. *
  1230. * int SetArithWork(TCHAR *tas)
  1231. *
  1232. * Args:
  1233. * tas - pointer to null terminated string of the form:
  1234. *
  1235. * VARNAME=expression
  1236. *
  1237. * Returns:
  1238. * If valid expression, return SUCCESS otherwise FAILURE.
  1239. *
  1240. */
  1241. int SetArithWork(TCHAR *tas)
  1242. {
  1243. PARSESTATE State;
  1244. //
  1245. // If no input, declare an error
  1246. //
  1247. if (!tas || !*tas) {
  1248. PutStdErr(MSG_BAD_SYNTAX, NOARGS);
  1249. return(FAILURE) ;
  1250. }
  1251. //
  1252. // Set up for parsing
  1253. //
  1254. State.Token = StripQuotes( tas );
  1255. State.Value = 0;
  1256. State.Error = SUCCESS;
  1257. State = AParseExpr( State );
  1258. if (State.Error == SUCCESS && ANextChar( &State ) != NULLC) {
  1259. State.Error = MSG_SET_A_MISSING_OPERATOR;
  1260. }
  1261. if (State.Error != SUCCESS) {
  1262. PutStdErr( State.Error, NOARGS );
  1263. //printf( "%ws\n", tas );
  1264. //printf( "%*s\n", State.Token - tas + 1, "^" );
  1265. } else if (!CurrentBatchFile) {
  1266. cmd_printf( TEXT("%d"), State.Value ) ;
  1267. }
  1268. return State.Error;
  1269. }