Leaked source code of windows server 2003
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.

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