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.

1003 lines
25 KiB

  1. /*++
  2. Copyright (c) 1999 Intel Corporation
  3. Module Name:
  4. batch.c
  5. Abstract:
  6. Functions implementing batch scripting in the shell.
  7. Revision History
  8. --*/
  9. #include "shelle.h"
  10. /*
  11. * Constants
  12. */
  13. #define ASCII_LF ((CHAR8)0x0a)
  14. #define ASCII_CR ((CHAR8)0x0d)
  15. #define UNICODE_LF ((CHAR16)0x000a)
  16. #define UNICODE_CR ((CHAR16)0x000d)
  17. /* Can hold 64-bit hex error numbers + null char */
  18. #define LASTERROR_BUFSIZ (17)
  19. /*
  20. * Statics
  21. * (needed to maintain state across multiple calls or for callbacks)
  22. */
  23. STATIC UINTN NestLevel;
  24. STATIC UINTN LastError;
  25. STATIC CHAR16 LastErrorBuf[LASTERROR_BUFSIZ];
  26. STATIC BOOLEAN Condition;
  27. STATIC BOOLEAN GotoIsActive;
  28. STATIC UINT64 GotoFilePos;
  29. STATIC BOOLEAN BatchIsActive;
  30. STATIC BOOLEAN EchoIsOn;
  31. STATIC BOOLEAN BatchAbort;
  32. STATIC SIMPLE_INPUT_INTERFACE *OrigConIn;
  33. STATIC SIMPLE_TEXT_OUTPUT_INTERFACE *OrigConOut;
  34. STATIC EFI_FILE_HANDLE CurrentBatchFile;
  35. /*
  36. * Definitions for the argument list stack
  37. *
  38. * In order to support nested scripts (script calling script calling script...)
  39. * there is an argument list stack "BatchInfoStack". BatchInfoStack is a
  40. * list of argument lists. Each argument list contains Argv[0] - Argv[n]
  41. * for the corresponding script file. The head of BatchInfoStack corresponds
  42. * to the currently active script file.
  43. *
  44. * This allows positional argument substitution to be done when each line
  45. * is read and scanned, and calls to other script files can overwrite the
  46. * shell interface's argument list.
  47. */
  48. #define EFI_BATCH_INFO_SIGNATURE EFI_SIGNATURE_32('b','i','r','g')
  49. typedef struct {
  50. UINTN Signature;
  51. LIST_ENTRY Link;
  52. CHAR16 *ArgValue;
  53. } EFI_SHELL_BATCH_INFO;
  54. #define EFI_BATCH_INFOLIST_SIGNATURE EFI_SIGNATURE_32('b','l','s','t')
  55. typedef struct {
  56. UINTN Signature;
  57. LIST_ENTRY Link;
  58. LIST_ENTRY ArgListHead; /* Head of this argument list */
  59. UINT64 FilePosition; /* Current file position */
  60. } EFI_SHELL_BATCH_INFOLIST;
  61. STATIC LIST_ENTRY BatchInfoStack;
  62. /*
  63. * Prototypes
  64. */
  65. STATIC EFI_STATUS
  66. BatchIsAscii(
  67. IN EFI_FILE_HANDLE File,
  68. OUT BOOLEAN *IsAscii
  69. );
  70. STATIC EFI_STATUS
  71. BatchGetLine(
  72. IN EFI_FILE_HANDLE File,
  73. IN BOOLEAN Ascii,
  74. IN OUT UINT64 *FilePosition,
  75. IN OUT UINTN *BufSize,
  76. OUT CHAR16 *CommandLine
  77. );
  78. VOID
  79. SEnvInitBatch(
  80. VOID
  81. )
  82. /*++
  83. Function Name:
  84. SEnvInitBatch
  85. Description:
  86. Initializes global variables used for batch file processing.
  87. --*/
  88. {
  89. NestLevel = 0;
  90. LastError = EFI_SUCCESS;
  91. ZeroMem( LastErrorBuf, LASTERROR_BUFSIZ );
  92. Condition = TRUE;
  93. GotoIsActive = FALSE;
  94. GotoFilePos = (UINT64)0x00;
  95. BatchIsActive = FALSE;
  96. EchoIsOn = TRUE;
  97. BatchAbort = FALSE;
  98. OrigConIn = ST->ConIn;
  99. OrigConOut = ST->ConOut;
  100. InitializeListHead( &BatchInfoStack );
  101. SEnvInitForLoopInfo();
  102. }
  103. BOOLEAN
  104. SEnvBatchIsActive(
  105. VOID
  106. )
  107. /*++
  108. Function Name:
  109. SEnvBatchIsActive
  110. Description:
  111. Returns whether any batch files are currently being processed.
  112. --*/
  113. {
  114. /*
  115. * BUGBUG should be able to return IsListEmpty( &BatchInfoStack );
  116. * instead of using this variable
  117. */
  118. return BatchIsActive;
  119. }
  120. VOID
  121. SEnvSetBatchAbort(
  122. VOID
  123. )
  124. /*++
  125. Function Name:
  126. SEnvSetBatchAbort
  127. Description:
  128. Sets a flag to notify the main batch processing loop to exit.
  129. --*/
  130. {
  131. BatchAbort = TRUE;
  132. return;
  133. }
  134. VOID
  135. SEnvBatchGetConsole(
  136. OUT SIMPLE_INPUT_INTERFACE **ConIn,
  137. OUT SIMPLE_TEXT_OUTPUT_INTERFACE **ConOut
  138. )
  139. /*++
  140. Function Name:
  141. SEnvBatchGetConsole
  142. Description:
  143. Returns the Console I/O interface pointers.
  144. --*/
  145. {
  146. *ConIn = OrigConIn;
  147. *ConOut = OrigConOut;
  148. return;
  149. }
  150. EFI_STATUS
  151. SEnvBatchEchoCommand(
  152. IN ENV_SHELL_INTERFACE *Shell
  153. )
  154. /*++
  155. Function Name:
  156. SEnvBatchEchoCommand
  157. Description:
  158. Echoes the given command to stdout.
  159. --*/
  160. {
  161. UINTN i;
  162. CHAR16 *BatchFileName;
  163. EFI_STATUS Status;
  164. /*
  165. * Echo the parsed-and-expanded command to the console
  166. */
  167. if ( SEnvBatchIsActive() && EchoIsOn ) {
  168. BatchFileName = NULL;
  169. Status = SEnvBatchGetArg( 0, &BatchFileName );
  170. if ( EFI_ERROR(Status) ) {
  171. goto Done;
  172. }
  173. Print( L"%E" );
  174. for ( i=0; i<NestLevel; i++ ) {
  175. Print( L"+" );
  176. }
  177. Print( L"%s> ", BatchFileName );
  178. for ( i=0; i<Shell->ShellInt.Argc; i++ ) {
  179. Print( L"%s ", Shell->ShellInt.Argv[i] );
  180. }
  181. for ( i=0; i<Shell->ShellInt.RedirArgc; i++ ) {
  182. Print( L"%s ", Shell->ShellInt.RedirArgv[i] );
  183. }
  184. Print( L"\n" );
  185. }
  186. Done:
  187. /*
  188. * Switch output attribute to normal
  189. */
  190. Print (L"%N");
  191. return Status;
  192. }
  193. VOID
  194. SEnvBatchSetEcho(
  195. IN BOOLEAN Val
  196. )
  197. /*++
  198. Function Name:
  199. SEnvBatchSetEcho
  200. Description:
  201. Sets the echo flag to the specified value.
  202. --*/
  203. {
  204. EchoIsOn = Val;
  205. return;
  206. }
  207. BOOLEAN
  208. SEnvBatchGetEcho(
  209. VOID
  210. )
  211. /*++
  212. Function Name:
  213. SEnvBatchGetEcho
  214. Description:
  215. Returns the echo flag.
  216. --*/
  217. {
  218. return EchoIsOn;
  219. }
  220. EFI_STATUS
  221. SEnvBatchSetFilePos(
  222. IN UINT64 NewPos
  223. )
  224. /*++
  225. Function Name:
  226. SEnvBatchSetFilePos
  227. Description:
  228. Sets the current script file position to the specified value.
  229. --*/
  230. {
  231. EFI_STATUS Status = EFI_SUCCESS;
  232. EFI_SHELL_BATCH_INFOLIST *BatchInfo = NULL;
  233. Status = CurrentBatchFile->SetPosition( CurrentBatchFile, NewPos );
  234. if ( EFI_ERROR(Status) ) {
  235. goto Done;
  236. }
  237. if ( !IsListEmpty( &BatchInfoStack ) ) {
  238. BatchInfo = CR( BatchInfoStack.Flink,
  239. EFI_SHELL_BATCH_INFOLIST,
  240. Link,
  241. EFI_BATCH_INFOLIST_SIGNATURE );
  242. }
  243. if ( BatchInfo ) {
  244. BatchInfo->FilePosition = NewPos;
  245. } else {
  246. Status = EFI_NOT_FOUND;
  247. goto Done;
  248. }
  249. Done:
  250. return Status;
  251. }
  252. EFI_STATUS
  253. SEnvBatchGetFilePos(
  254. UINT64 *FilePos
  255. )
  256. /*++
  257. Function Name:
  258. SEnvBatchGetFilePos
  259. Description:
  260. Returns the current script file position.
  261. --*/
  262. {
  263. EFI_SHELL_BATCH_INFOLIST *BatchInfo = NULL;
  264. EFI_STATUS Status = EFI_SUCCESS;
  265. if ( !FilePos ) {
  266. Status = EFI_INVALID_PARAMETER;
  267. goto Done;
  268. }
  269. if ( !IsListEmpty( &BatchInfoStack ) ) {
  270. BatchInfo = CR( BatchInfoStack.Flink,
  271. EFI_SHELL_BATCH_INFOLIST,
  272. Link,
  273. EFI_BATCH_INFOLIST_SIGNATURE );
  274. }
  275. if ( BatchInfo ) {
  276. *FilePos = BatchInfo->FilePosition;
  277. } else {
  278. Status = EFI_NOT_FOUND;
  279. goto Done;
  280. }
  281. Done:
  282. return Status;
  283. }
  284. VOID
  285. SEnvBatchSetCondition(
  286. IN BOOLEAN Val
  287. )
  288. /*++
  289. Function Name:
  290. SEnvBatchSetCondition
  291. Description:
  292. Sets the condition flag to the specified value.
  293. --*/
  294. {
  295. Condition = Val;
  296. return;
  297. }
  298. VOID
  299. SEnvBatchSetGotoActive(
  300. VOID
  301. )
  302. /*++
  303. Function Name:
  304. SEnvBatchSetGotoActive
  305. Description:
  306. Sets the goto-is-active to TRUE and saves the current position
  307. of the active script file.
  308. --*/
  309. {
  310. GotoIsActive = TRUE;
  311. SEnvBatchGetFilePos( &GotoFilePos );
  312. return;
  313. }
  314. BOOLEAN
  315. SEnvBatchVarIsLastError(
  316. IN CHAR16 *Name
  317. )
  318. /*++
  319. Function Name:
  320. SEnvBatchVarIsLastError
  321. Description:
  322. Checks to see if variable's name is "lasterror".
  323. --*/
  324. {
  325. return (StriCmp( L"lasterror", Name ) == 0);
  326. }
  327. VOID
  328. SEnvBatchSetLastError(
  329. IN UINTN NewLastError
  330. )
  331. /*++
  332. Function Name:
  333. SEnvBatchSetLastError
  334. Description:
  335. Sets the lasterror variable's value to the given value.
  336. --*/
  337. {
  338. LastError = NewLastError;
  339. return;
  340. }
  341. CHAR16*
  342. SEnvBatchGetLastError( VOID
  343. )
  344. /*++
  345. Function Name:
  346. SEnvBatchGetLastError
  347. Description:
  348. Returns a pointer to a string representation of the error value
  349. returned by the last shell command.
  350. --*/
  351. {
  352. ValueToHex( LastErrorBuf, (UINT64)LastError );
  353. return LastErrorBuf;
  354. }
  355. STATIC EFI_STATUS
  356. BatchIsAscii(
  357. IN EFI_FILE_HANDLE File,
  358. OUT BOOLEAN *IsAscii
  359. )
  360. /*++
  361. Function Name:
  362. BatchIsAscii
  363. Description:
  364. Checks to see if the specified batch file is ASCII.
  365. --*/
  366. {
  367. EFI_STATUS Status=EFI_SUCCESS;
  368. CHAR8 Buffer8[2]; /* UNICODE byte-order-mark is two bytes */
  369. UINTN BufSize;
  370. /*
  371. * Read the first two bytes to check for byte order mark
  372. */
  373. BufSize = sizeof(Buffer8);
  374. Status = File->Read( File, &BufSize, Buffer8 );
  375. if ( EFI_ERROR(Status) ) {
  376. goto Done;
  377. }
  378. Status = File->SetPosition( File, (UINT64)0 );
  379. if ( EFI_ERROR(Status) ) {
  380. goto Done;
  381. }
  382. /*
  383. * If we find a UNICODE byte order mark assume it is UNICODE,
  384. * otherwise assume it is ASCII. UNICODE byte order mark on
  385. * IA little endian is first byte 0xff and second byte 0xfe
  386. */
  387. if ( (Buffer8[0] | (Buffer8[1] << 8)) == UNICODE_BYTE_ORDER_MARK ) {
  388. *IsAscii = FALSE;
  389. } else {
  390. *IsAscii = TRUE;
  391. }
  392. Done:
  393. return Status;
  394. }
  395. STATIC EFI_STATUS
  396. BatchGetLine(
  397. IN EFI_FILE_HANDLE File,
  398. IN BOOLEAN Ascii,
  399. IN OUT UINT64 *FilePosition,
  400. IN OUT UINTN *BufSize,
  401. OUT CHAR16 *CommandLine
  402. )
  403. /*++
  404. Function Name:
  405. BatchGetLine
  406. Description:
  407. Reads the next line from the batch file, converting it from
  408. ASCII to UNICODE if necessary. If end of file is encountered
  409. then it returns 0 in the BufSize parameter.
  410. --*/
  411. {
  412. EFI_STATUS Status;
  413. CHAR8 Buffer8[MAX_CMDLINE];
  414. CHAR16 Buffer16[MAX_CMDLINE];
  415. UINTN i = 0;
  416. UINTN CmdLenInChars = 0;
  417. UINTN CmdLenInBytes = 0;
  418. UINTN CharSize = 0;
  419. /*
  420. * Check params
  421. */
  422. if ( !CommandLine || !BufSize || !FilePosition ) {
  423. Status = EFI_INVALID_PARAMETER;
  424. goto Done;
  425. }
  426. /*
  427. * Initialize OUT param
  428. */
  429. ZeroMem( CommandLine, MAX_CMDLINE );
  430. /*
  431. * If beginning of UNICODE file, move past the Byte-Order-Mark (2 bytes)
  432. */
  433. if ( !Ascii && *FilePosition == (UINT64)0 ) {
  434. *FilePosition = (UINT64)2;
  435. Status = File->SetPosition( File, *FilePosition );
  436. if ( EFI_ERROR(Status) ) {
  437. goto Done;
  438. }
  439. }
  440. /*
  441. * (1) Read a buffer-full from the file
  442. * (2) Locate the end of the 1st line in the buffer
  443. * ASCII version and UNICODE version
  444. */
  445. if ( Ascii ) {
  446. CharSize = sizeof(CHAR8);
  447. Status = File->Read( File, BufSize, Buffer8 );
  448. if ( EFI_ERROR(Status) || *BufSize == 0 ) {
  449. goto Done;
  450. }
  451. for ( i=0; i<*BufSize; i++ ) {
  452. if ( Buffer8[i] == ASCII_LF ) {
  453. CmdLenInChars = i;
  454. CmdLenInBytes = CmdLenInChars;
  455. break;
  456. }
  457. }
  458. } else { /* UNICODE */
  459. CharSize = sizeof(CHAR16);
  460. Status = File->Read( File, BufSize, Buffer16 );
  461. if ( EFI_ERROR(Status) || *BufSize == 0 ) {
  462. goto Done;
  463. }
  464. for ( i=0; i < *BufSize/CharSize; i++ ) {
  465. if ( Buffer16[i] == UNICODE_LF ) {
  466. CmdLenInChars = i;
  467. CmdLenInBytes = CmdLenInChars * CharSize;
  468. break;
  469. }
  470. }
  471. }
  472. /*
  473. * Reset the file position to just after the command line
  474. */
  475. *FilePosition += (UINT64)(CmdLenInBytes + CharSize);
  476. Status = File->SetPosition( File, *FilePosition );
  477. /*
  478. * Copy, converting chars to UNICODE if necessary
  479. */
  480. if ( Ascii ) {
  481. for ( i=0; i<CmdLenInChars; i++ ) {
  482. CommandLine[i] = (CHAR16)Buffer8[i];
  483. }
  484. } else {
  485. CopyMem( CommandLine, Buffer16, CmdLenInBytes );
  486. }
  487. CmdLenInChars = i;
  488. Done:
  489. *BufSize = CmdLenInChars * CharSize;
  490. return Status;
  491. }
  492. EFI_STATUS
  493. SEnvBatchGetArg(
  494. IN UINTN Argno,
  495. OUT CHAR16 **Argval
  496. )
  497. /*++
  498. Function Name:
  499. BatchGetArg
  500. Description:
  501. Extract the specified element from the arglist at the top of the arglist
  502. stack. Return a pointer to the value field of the "Argno"th element of
  503. the list.
  504. --*/
  505. {
  506. EFI_SHELL_BATCH_INFOLIST *BatchInfo = NULL;
  507. LIST_ENTRY *Link = NULL;
  508. EFI_SHELL_BATCH_INFO *ArgEntry = NULL;
  509. UINTN i = 0;
  510. if ( !IsListEmpty( &BatchInfoStack ) ) {
  511. BatchInfo = CR( BatchInfoStack.Flink,
  512. EFI_SHELL_BATCH_INFOLIST,
  513. Link,
  514. EFI_BATCH_INFOLIST_SIGNATURE );
  515. }
  516. if ( !IsListEmpty( &BatchInfo->ArgListHead ) ) {
  517. for ( Link=BatchInfo->ArgListHead.Flink;
  518. Link!=&BatchInfo->ArgListHead;
  519. Link=Link->Flink) {
  520. ArgEntry = CR( Link,
  521. EFI_SHELL_BATCH_INFO,
  522. Link,
  523. EFI_BATCH_INFO_SIGNATURE);
  524. if ( i++ == Argno ) {
  525. *Argval = ArgEntry->ArgValue;
  526. return EFI_SUCCESS;
  527. }
  528. }
  529. }
  530. *Argval = NULL;
  531. return EFI_NOT_FOUND;
  532. }
  533. EFI_STATUS
  534. SEnvExecuteScript(
  535. IN ENV_SHELL_INTERFACE *Shell,
  536. IN EFI_FILE_HANDLE File
  537. )
  538. /*++
  539. Function Name:
  540. SEnvExecuteScript
  541. Description:
  542. Execute the commands in the script file specified by the
  543. file parameter.
  544. Arguments:
  545. Shell: shell interface of the caller
  546. File: file handle to open script file
  547. Returns:
  548. EFI_STATUS
  549. --*/
  550. {
  551. EFI_FILE_INFO *FileInfo;
  552. UINTN FileNameLen = 0;
  553. BOOLEAN EndOfFile = FALSE;
  554. EFI_STATUS Status = EFI_SUCCESS;
  555. UINTN BufSize = 0;
  556. UINTN FileInfoSize = 0;
  557. CHAR16 CommandLine[MAX_CMDLINE];
  558. EFI_SHELL_BATCH_INFOLIST *BatchInfo = NULL;
  559. EFI_SHELL_BATCH_INFO *ArgEntry = NULL;
  560. UINTN i = 0;
  561. BOOLEAN Output = TRUE;
  562. ENV_SHELL_INTERFACE NewShell;
  563. UINTN GotoTargetStatus;
  564. UINTN SkippedIfCount;
  565. /*
  566. * Initialize
  567. */
  568. BatchIsActive = TRUE;
  569. Status = EFI_SUCCESS;
  570. NestLevel++;
  571. SEnvInitTargetLabel();
  572. /*
  573. * Check params
  574. */
  575. if ( !File ) {
  576. Status = EFI_INVALID_PARAMETER;
  577. goto Done;
  578. }
  579. /*
  580. * Figure out if the file is ASCII or UNICODE.
  581. */
  582. Status = BatchIsAscii( File, &Shell->StdIn.Ascii );
  583. if ( EFI_ERROR( Status ) ) {
  584. goto Done;
  585. }
  586. /*
  587. * Get the filename from the file handle.
  588. */
  589. /*
  590. * Allocate buffer for file info (including file name)
  591. * BUGBUG 1024 arbitrary space for filename, as elsewhere in shell
  592. */
  593. FileInfoSize = SIZE_OF_EFI_FILE_INFO + 1024;
  594. FileInfo = AllocatePool(FileInfoSize);
  595. if (!FileInfo) {
  596. Status = EFI_OUT_OF_RESOURCES;
  597. goto Done;
  598. }
  599. /* Get file info */
  600. Status = File->GetInfo( File,
  601. &GenericFileInfo,
  602. &FileInfoSize,
  603. FileInfo );
  604. if ( EFI_ERROR(Status) ) {
  605. return Status;
  606. }
  607. /*
  608. * Save the handle
  609. */
  610. CurrentBatchFile = File;
  611. /*
  612. * Initialize argument list for this script
  613. * This list is needed since nested batch files would overwrite the
  614. * argument list in Shell->ShellInt.Argv[]. Here we maintain the args in
  615. * a local list on the stack.
  616. */
  617. BatchInfo = AllocateZeroPool( sizeof( EFI_SHELL_BATCH_INFOLIST ) );
  618. if ( !BatchInfo ) {
  619. Status = EFI_OUT_OF_RESOURCES;
  620. goto Done;
  621. }
  622. BatchInfo->Signature = EFI_BATCH_INFOLIST_SIGNATURE;
  623. BatchInfo->FilePosition = (UINT64)0x00;
  624. InitializeListHead( &BatchInfo->ArgListHead );
  625. for ( i=0; i<Shell->ShellInt.Argc; i++ ) {
  626. /* Allocate the new element of the argument list */
  627. ArgEntry = AllocateZeroPool( sizeof( EFI_SHELL_BATCH_INFO ) );
  628. if ( !ArgEntry ) {
  629. Status = EFI_OUT_OF_RESOURCES;
  630. goto Done;
  631. }
  632. /* Allocate space for the argument string in the arglist element */
  633. ArgEntry->ArgValue = AllocateZeroPool(StrSize(Shell->ShellInt.Argv[i]));
  634. if ( !ArgEntry->ArgValue ) {
  635. Status = EFI_OUT_OF_RESOURCES;
  636. goto Done;
  637. }
  638. /* Copy in the argument string */
  639. StrCpy( ArgEntry->ArgValue, Shell->ShellInt.Argv[i] );
  640. ArgEntry->Signature = EFI_BATCH_INFO_SIGNATURE;
  641. /* Add the arglist element to the end of the list */
  642. InsertTailList( &BatchInfo->ArgListHead, &ArgEntry->Link );
  643. }
  644. /* Push the arglist onto the arglist stack */
  645. InsertHeadList( &BatchInfoStack, &BatchInfo->Link );
  646. /*
  647. * Iterate through the file, reading a line at a time and executing each
  648. * line as a shell command. Nested shell scripts will come through
  649. * this code path recursively.
  650. */
  651. EndOfFile = FALSE;
  652. SkippedIfCount = 0;
  653. while (1) {
  654. /*
  655. * Read a command line from the file
  656. */
  657. BufSize = MAX_CMDLINE;
  658. Status = BatchGetLine( File,
  659. Shell->StdIn.Ascii,
  660. &BatchInfo->FilePosition,
  661. &BufSize,
  662. CommandLine );
  663. if ( EFI_ERROR( Status ) ) {
  664. goto Done;
  665. }
  666. /*
  667. * No error and no chars means EOF
  668. * If we are in the middle of a GOTO then rewind to search for the
  669. * label from the beginning of the file, otherwise we are done
  670. * with this script.
  671. */
  672. if ( BufSize == 0 ) {
  673. if ( GotoIsActive ) {
  674. BatchInfo->FilePosition = (UINT64)(0x00);
  675. Status = File->SetPosition( File, BatchInfo->FilePosition );
  676. if ( EFI_ERROR( Status ) ) {
  677. goto Done;
  678. } else {
  679. continue;
  680. }
  681. } else {
  682. goto Done;
  683. }
  684. }
  685. /*
  686. * Convert the command line to an arg list
  687. */
  688. ZeroMem( &NewShell, sizeof(NewShell ) );
  689. Status = SEnvStringToArg(
  690. CommandLine,
  691. TRUE,
  692. &NewShell.ShellInt.Argv,
  693. &NewShell.ShellInt.Argc
  694. );
  695. if (EFI_ERROR(Status)) {
  696. goto Done;
  697. }
  698. /*
  699. * Skip comments and blank lines
  700. */
  701. if ( NewShell.ShellInt.Argc == 0 ) {
  702. continue;
  703. }
  704. /*
  705. * If a GOTO command is active, skip everything until we find
  706. * the target label or until we determine it doesn't exist.
  707. */
  708. if ( GotoIsActive ) {
  709. /*
  710. * Check if we have the right label or if we've searched
  711. * the whole file
  712. */
  713. Status = SEnvCheckForGotoTarget( NewShell.ShellInt.Argv[0],
  714. GotoFilePos,
  715. BatchInfo->FilePosition,
  716. &GotoTargetStatus );
  717. if ( EFI_ERROR( Status ) ) {
  718. goto Done;
  719. }
  720. switch ( GotoTargetStatus ) {
  721. case GOTO_TARGET_FOUND:
  722. GotoIsActive = FALSE;
  723. SEnvFreeTargetLabel();
  724. continue;
  725. case GOTO_TARGET_NOT_FOUND:
  726. continue;
  727. case GOTO_TARGET_DOESNT_EXIST:
  728. GotoIsActive = FALSE;
  729. Status = EFI_INVALID_PARAMETER;
  730. LastError = Status;
  731. SEnvPrintLabelNotFound();
  732. SEnvFreeTargetLabel();
  733. continue;
  734. default:
  735. Status = EFI_INVALID_PARAMETER;
  736. SEnvFreeTargetLabel();
  737. Print( L"Internal error: invalid GotoTargetStatus\n" );
  738. break;
  739. }
  740. } else if ( NewShell.ShellInt.Argv[0][0] == L':' ) {
  741. /*
  742. * Skip labels when no GOTO is active
  743. */
  744. continue;
  745. }
  746. /*
  747. * Skip everything between an 'if' whose condition was false and its
  748. * matching 'endif'. Note that 'endif' doesn't do anything if
  749. * Condition is TRUE, so we only track endif matching when it is false.
  750. */
  751. if ( !Condition ) {
  752. if ( StriCmp( NewShell.ShellInt.Argv[0], L"if") == 0 ) {
  753. /*
  754. * Keep track of how many endifs we have to skip before we are
  755. * done with the FALSE Condition
  756. */
  757. SkippedIfCount += 1;
  758. continue;
  759. } else if ( StriCmp( NewShell.ShellInt.Argv[0], L"endif") == 0 ) {
  760. if ( SkippedIfCount > 0 ) {
  761. SkippedIfCount -= 1;
  762. continue;
  763. }
  764. /*
  765. * When SkippedIfCount goes to zero (as here), we have the
  766. * endif that matches the if with the FALSE condition that
  767. * we are dealing with, so we want to fall through and have
  768. * the endif command reset the Condition flag.
  769. */
  770. } else {
  771. /*
  772. * Condition FALSE, not an if or an endif, so skip
  773. */
  774. continue;
  775. }
  776. }
  777. /*
  778. * Execute the command
  779. */
  780. LastError = SEnvDoExecute(
  781. Shell->ShellInt.ImageHandle,
  782. CommandLine,
  783. &NewShell,
  784. TRUE
  785. );
  786. /*
  787. * Save the current file handle
  788. */
  789. CurrentBatchFile = File;
  790. if ( BatchAbort ) {
  791. goto Done;
  792. }
  793. }
  794. Done:
  795. /*
  796. * Clean up
  797. */
  798. /* Decrement the count of open script files */
  799. NestLevel--;
  800. /* Free any potential remaining GOTO target label */
  801. SEnvFreeTargetLabel();
  802. /* Reset the IF condition to TRUE, even if no ENDIF was found */
  803. SEnvBatchSetCondition( TRUE );
  804. /* Close the script file */
  805. if ( File ) {
  806. File->Close( File );
  807. }
  808. /* Free the file info structure used to get the file name from the handle */
  809. if ( FileInfo ) {
  810. FreePool( FileInfo );
  811. FileInfo = NULL;
  812. }
  813. /* Pop the argument list for this script off the stack */
  814. if ( !IsListEmpty( &BatchInfoStack ) ) {
  815. BatchInfo = CR( BatchInfoStack.Flink,
  816. EFI_SHELL_BATCH_INFOLIST,
  817. Link,
  818. EFI_BATCH_INFOLIST_SIGNATURE );
  819. RemoveEntryList( &BatchInfo->Link );
  820. }
  821. /* Free the argument list for this script file */
  822. while ( !IsListEmpty( &BatchInfo->ArgListHead ) ) {
  823. ArgEntry = CR( BatchInfo->ArgListHead.Flink,
  824. EFI_SHELL_BATCH_INFO,
  825. Link,
  826. EFI_BATCH_INFO_SIGNATURE );
  827. if ( ArgEntry ) {
  828. RemoveEntryList( &ArgEntry->Link );
  829. if ( ArgEntry->ArgValue ) {
  830. FreePool( ArgEntry->ArgValue );
  831. ArgEntry->ArgValue = NULL;
  832. }
  833. FreePool( ArgEntry );
  834. ArgEntry = NULL;
  835. }
  836. }
  837. FreePool( BatchInfo );
  838. BatchInfo = NULL;
  839. /*
  840. * If we are returning to the interactive shell, then reset
  841. * the batch-is-active flag
  842. */
  843. if ( IsListEmpty( &BatchInfoStack ) ) {
  844. BatchIsActive = FALSE;
  845. BatchAbort = FALSE;
  846. }
  847. return Status;
  848. }