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.

1243 lines
35 KiB

  1. /*++
  2. Copyright (c) 1998 Intel Corporation
  3. Module Name:
  4. exec.c
  5. Abstract:
  6. Revision History
  7. --*/
  8. #include "shelle.h"
  9. typedef struct {
  10. CHAR16 **Arg;
  11. UINTN ArgIndex;
  12. BOOLEAN Output;
  13. BOOLEAN Quote;
  14. UINTN AliasLevel;
  15. UINTN MacroParan;
  16. UINTN RecurseLevel;
  17. CHAR16 Buffer[MAX_ARG_LENGTH];
  18. } PARSE_STATE;
  19. typedef struct _SENV_OPEN_DIR {
  20. struct _SENV_OPEN_DIR *Next;
  21. EFI_FILE_HANDLE Handle;
  22. } SENV_OPEN_DIR;
  23. /*
  24. * Internal macros
  25. */
  26. #define ArgTooLong(i) (i > MAX_ARG_LENGTH-sizeof(CHAR16))
  27. /*
  28. * Internal prototypes
  29. */
  30. EFI_STATUS
  31. ShellParseStr (
  32. IN CHAR16 *Str,
  33. IN OUT PARSE_STATE *ParseState
  34. );
  35. EFI_STATUS
  36. SEnvDoExecute (
  37. IN EFI_HANDLE *ParentImageHandle,
  38. IN CHAR16 *CommandLine,
  39. IN ENV_SHELL_INTERFACE *Shell,
  40. IN BOOLEAN Output
  41. );
  42. VOID
  43. INTERNAL
  44. SEnvLoadImage (
  45. IN EFI_HANDLE ParentImage,
  46. IN CHAR16 *IName,
  47. OUT EFI_HANDLE *pImageHandle,
  48. OUT EFI_FILE_HANDLE *pScriptsHandle
  49. );
  50. /*
  51. * Parser driver function
  52. */
  53. EFI_STATUS
  54. SEnvStringToArg (
  55. IN CHAR16 *Str,
  56. IN BOOLEAN Output,
  57. OUT CHAR16 ***pArgv,
  58. OUT UINT32 *pArgc
  59. )
  60. {
  61. PARSE_STATE ParseState;
  62. EFI_STATUS Status;
  63. /*
  64. * Initialize a new state
  65. */
  66. ZeroMem (&ParseState, sizeof(ParseState));
  67. ParseState.Output = Output;
  68. ParseState.Arg = AllocateZeroPool (MAX_ARG_COUNT * sizeof(CHAR16 *));
  69. if (!ParseState.Arg) {
  70. return EFI_OUT_OF_RESOURCES;
  71. }
  72. /*
  73. * Parse the string
  74. */
  75. Status = ShellParseStr (Str, &ParseState);
  76. *pArgv = ParseState.Arg;
  77. *pArgc = (UINT32) ParseState.ArgIndex;
  78. /*
  79. * Done
  80. */
  81. return Status;
  82. }
  83. EFI_STATUS
  84. ShellParseStr (
  85. IN CHAR16 *Str,
  86. IN OUT PARSE_STATE *ParseState
  87. )
  88. {
  89. EFI_STATUS Status;
  90. CHAR16 *Alias;
  91. CHAR16 *NewArg;
  92. CHAR16 *SubstituteStr;
  93. UINTN Index;
  94. BOOLEAN Literal;
  95. BOOLEAN Comment;
  96. UINTN ArgNo;
  97. ParseState->RecurseLevel += 1;
  98. if (ParseState->RecurseLevel > 5) {
  99. DEBUG ((D_PARSE, "Recursive alias or macro\n"));
  100. if (ParseState->Output) {
  101. Print (L"Recursive alias or macro\n");
  102. }
  103. Status = EFI_INVALID_PARAMETER;
  104. goto Done;
  105. }
  106. NewArg = ParseState->Buffer;
  107. while (*Str) {
  108. /*
  109. * Skip leading white space
  110. */
  111. if (IsWhiteSpace(*Str)) {
  112. Str += 1;
  113. continue;
  114. }
  115. /*
  116. * Pull this arg out of the string
  117. */
  118. Index = 0;
  119. Literal = FALSE;
  120. Comment = FALSE;
  121. while (*Str) {
  122. /*
  123. * If we have white space (or the ',' arg separator) and we are
  124. * not in a quote or macro expansion, move to the next word
  125. */
  126. if ((IsWhiteSpace(*Str) || *Str == ',') &&
  127. !ParseState->Quote && !ParseState->MacroParan) {
  128. break;
  129. }
  130. /*
  131. * Check arg length
  132. */
  133. if ( ArgTooLong(Index) ) {
  134. DEBUG((D_PARSE, "Argument too long\n"));
  135. if (ParseState->Output) {
  136. Print (L"Argument too long\n");
  137. }
  138. Status = EFI_INVALID_PARAMETER;
  139. goto Done;
  140. }
  141. /*
  142. * Check char
  143. */
  144. switch (*Str) {
  145. case '#':
  146. /* Comment, discard the rest of the characters in the line */
  147. Comment = TRUE;
  148. while( *Str++ );
  149. break;
  150. case '%':
  151. if ( IsDigit(Str[1]) && IsWhiteSpace(Str[2]) ) {
  152. /* Found a script argument - substitute */
  153. ArgNo = Str[1] - '0';
  154. Status = SEnvBatchGetArg( ArgNo, &SubstituteStr );
  155. if ( EFI_ERROR(Status) ) {
  156. /* if not found, just ignore, as if no arg */
  157. DEBUG((D_PARSE, "Argument %d not found - ignored\n", ArgNo));
  158. Status = EFI_SUCCESS;
  159. goto Done;
  160. }
  161. if ( ArgTooLong(StrLen(SubstituteStr)) ) {
  162. DEBUG((D_PARSE, "Argument too long\n"));
  163. if (ParseState->Output) {
  164. Print (L"Argument too long\n");
  165. }
  166. Status = EFI_INVALID_PARAMETER;
  167. goto Done;
  168. }
  169. StrCpy( &NewArg[Index], SubstituteStr );
  170. Index += StrLen( SubstituteStr );
  171. Str += 1;
  172. } else if ( IsAlpha(Str[1]) && IsWhiteSpace(Str[2]) ) {
  173. /*
  174. * For loop index
  175. */
  176. Status = SEnvSubstituteForLoopIndex( Str, &SubstituteStr );
  177. if ( EFI_ERROR(Status) ) {
  178. goto Done;
  179. }
  180. if ( SubstituteStr ) {
  181. /* Found a match */
  182. if ( ArgTooLong(StrLen(SubstituteStr)) ) {
  183. DEBUG((D_PARSE, "Argument too long\n"));
  184. if (ParseState->Output) {
  185. Print (L"Argument too long\n");
  186. }
  187. Status = EFI_INVALID_PARAMETER;
  188. goto Done;
  189. }
  190. StrCpy( &NewArg[Index], SubstituteStr );
  191. Index += StrLen( SubstituteStr );
  192. /* only advance one char - standard processing will get the 2nd char */
  193. Str += 1;
  194. }
  195. /* if no match then just continue without substitution */
  196. } else {
  197. /*
  198. * Found a variable of some kind
  199. * If there is another '%' before any whitespace, look for
  200. * an environment variable to substitute.
  201. * If there is no environment variable, then the arg is the
  202. * literal string including the '%' signs; otherwise substitute
  203. */
  204. SubstituteStr = Str + 1;
  205. while ( !IsWhiteSpace(*SubstituteStr) ) {
  206. if ( *SubstituteStr == '%' ) {
  207. CHAR16 *VarName;
  208. UINTN VarNameLen;
  209. /*
  210. * Extract the (potential) variable name
  211. */
  212. VarNameLen = SubstituteStr - (Str + 1);
  213. VarName = AllocateZeroPool( (VarNameLen + 1)*sizeof(CHAR16) );
  214. if ( !VarName ) {
  215. Status = EFI_OUT_OF_RESOURCES;
  216. goto Done;
  217. }
  218. CopyMem( VarName, Str+1, (VarNameLen + 1)*sizeof(CHAR16) );
  219. VarName[VarNameLen] = (CHAR16)0x0000;
  220. /*
  221. * Check for special case "lasterror" variable
  222. * Otherwise just get the matching environment variable
  223. */
  224. if ( SEnvBatchVarIsLastError( VarName ) ) {
  225. SubstituteStr = SEnvBatchGetLastError();
  226. } else {
  227. SubstituteStr = SEnvGetEnv( VarName );
  228. }
  229. FreePool( VarName );
  230. if ( !SubstituteStr ) {
  231. /* Not found - this is OK, then just use the original
  232. * string %xxx% in the arg. Note that we know that
  233. * this loop will terminate, since we found the % b4 */
  234. NewArg[Index++] = *Str;
  235. Str += 1;
  236. while ( *Str != '%' ) {
  237. NewArg[Index++] = *Str;
  238. Str += 1;
  239. }
  240. NewArg[Index++] = *Str;
  241. Str += 1;
  242. } else {
  243. /* Insert the variable's value in the new arg -
  244. * the arg may include more than just the variable */
  245. if ( ArgTooLong( Index + StrLen(SubstituteStr) ) ) {
  246. DEBUG((D_PARSE, "Argument too long\n"));
  247. if (ParseState->Output) {
  248. Print (L"Argument too long\n");
  249. }
  250. Status = EFI_INVALID_PARAMETER;
  251. goto Done;
  252. }
  253. StrCpy( &NewArg[Index], SubstituteStr );
  254. Index += StrLen(SubstituteStr);
  255. Str += VarNameLen + 1;
  256. }
  257. break;
  258. }
  259. SubstituteStr += 1;
  260. } /* end while */
  261. }
  262. break;
  263. case '^':
  264. /* Literal, don't process aliases on this arg */
  265. if (Str[1]) {
  266. Str += 1;
  267. NewArg[Index++] = *Str;
  268. Literal = TRUE;
  269. }
  270. break;
  271. case '"':
  272. /* Quoted string entry and exit */
  273. ParseState->Quote = !ParseState->Quote;
  274. break;
  275. case '(':
  276. if (ParseState->MacroParan) {
  277. ParseState->MacroParan = ParseState->MacroParan + 1;
  278. }
  279. NewArg[Index++] = *Str;
  280. break;
  281. case ')':
  282. if (ParseState->MacroParan) {
  283. /* End of a macro - go evaluate it */
  284. ParseState->MacroParan -= 1;
  285. /* BUGBUG: code not complete */
  286. ASSERT (FALSE);
  287. } else {
  288. NewArg[Index++] = *Str;
  289. }
  290. break;
  291. case '$':
  292. /* If this is a start of a macro, pick it up */
  293. if (Str[1] == '(') {
  294. Str += 1;
  295. ParseState->MacroParan += 1;
  296. }
  297. NewArg[Index++] = *Str;
  298. break;
  299. default:
  300. if (!IsValidChar(*Str)) {
  301. DEBUG((D_PARSE, "Invalid char %x in string\n", *Str));
  302. if (ParseState->Output) {
  303. Print (L"Invalid char %x in string\n", *Str);
  304. }
  305. Status = EFI_INVALID_PARAMETER;
  306. goto Done;
  307. }
  308. NewArg[Index++] = *Str;
  309. break;
  310. }
  311. /*
  312. * Next char
  313. */
  314. Str += 1;
  315. }
  316. /*
  317. * Make sure the macro was terminated
  318. */
  319. if (ParseState->MacroParan) {
  320. DEBUG ((D_PARSE, "Too many '$(' parans\n"));
  321. if (ParseState->Output) {
  322. Print (L"Too many '$(' parans\n");
  323. }
  324. Status = EFI_INVALID_PARAMETER;
  325. goto Done;
  326. }
  327. /*
  328. * If the new argument string is empty and we have encountered a
  329. * comment, then skip it. Otherwise we have a new arg
  330. */
  331. if ( Comment && Index == 0 ) {
  332. break;
  333. } else {
  334. NewArg[Index] = 0;
  335. Alias = NULL;
  336. }
  337. /*
  338. * If it was composed with a literal, do not check to see if the arg has an alias
  339. */
  340. Alias = NULL;
  341. if (!Literal && !ParseState->AliasLevel && ParseState->ArgIndex == 0) {
  342. Alias = SEnvGetAlias(NewArg);
  343. }
  344. /*
  345. * If there's an alias, parse it
  346. */
  347. if (Alias) {
  348. ParseState->AliasLevel += 1;
  349. Status = ShellParseStr (Alias, ParseState);
  350. ParseState->AliasLevel -= 1;
  351. if (EFI_ERROR(Status)) {
  352. goto Done;
  353. }
  354. } else {
  355. /*
  356. * Otherwise, copy the word to the arg array
  357. */
  358. ParseState->Arg[ParseState->ArgIndex] = StrDuplicate(NewArg);
  359. if (!ParseState->Arg[ParseState->ArgIndex]) {
  360. Status = EFI_OUT_OF_RESOURCES;
  361. break;
  362. }
  363. ParseState->ArgIndex += 1;
  364. if (ParseState->ArgIndex >= MAX_ARG_COUNT-1) {
  365. DEBUG ((D_PARSE, "Too many arguments: %d\n", ParseState->ArgIndex));
  366. if (ParseState->Output) {
  367. Print(L"Too many arguments: %d\n", ParseState->ArgIndex);
  368. }
  369. Status = EFI_OUT_OF_RESOURCES;
  370. goto Done;
  371. }
  372. }
  373. /*
  374. * If last word ended with a comma, skip it to move to the next word
  375. */
  376. if (*Str == ',') {
  377. Str += 1;
  378. }
  379. }
  380. Status = EFI_SUCCESS;
  381. Done:
  382. ParseState->RecurseLevel -= 1;
  383. if (EFI_ERROR(Status)) {
  384. /* Free all the args allocated */
  385. for (Index=0; Index < ParseState->ArgIndex; Index++) {
  386. if (ParseState->Arg[Index]) {
  387. FreePool (ParseState->Arg[Index]);
  388. ParseState->Arg[Index] = NULL;
  389. }
  390. }
  391. ParseState->ArgIndex = 0;
  392. }
  393. return Status;
  394. }
  395. EFI_STATUS
  396. SEnvRedirOutput (
  397. IN OUT ENV_SHELL_INTERFACE *Shell,
  398. IN BOOLEAN Ascii,
  399. IN BOOLEAN Append,
  400. IN OUT UINTN *NewArgc,
  401. IN OUT UINTN *Index,
  402. OUT ENV_SHELL_REDIR_FILE *Redir
  403. )
  404. {
  405. CHAR16 *FileName;
  406. EFI_STATUS Status;
  407. EFI_FILE_INFO *Info;
  408. UINTN Size;
  409. CHAR16 UnicodeMarker = UNICODE_BYTE_ORDER_MARK;
  410. UINT64 FileMode;
  411. /*
  412. * Update args
  413. */
  414. if (!*NewArgc) {
  415. *NewArgc = *Index;
  416. }
  417. *Index += 1;
  418. if (*Index >= Shell->ShellInt.Argc) {
  419. return EFI_INVALID_PARAMETER;
  420. }
  421. if (Redir->Handle) {
  422. return EFI_INVALID_PARAMETER;
  423. }
  424. /*
  425. * Open the output file
  426. */
  427. Redir->Ascii = Ascii;
  428. Redir->WriteError = EFI_SUCCESS;
  429. FileName = Shell->ShellInt.Argv[*Index];
  430. Redir->FilePath = SEnvNameToPath(FileName);
  431. if (Redir->FilePath) {
  432. FileMode = EFI_FILE_MODE_WRITE | ((Append)? 0 : EFI_FILE_MODE_CREATE);
  433. Redir->File = ShellOpenFilePath(Redir->FilePath, FileMode);
  434. if (Append && !Redir->File) {
  435. /*
  436. * If file does not exist make a new one. And send us down the other path
  437. */
  438. FileMode |= EFI_FILE_MODE_CREATE;
  439. Redir->File = ShellOpenFilePath(Redir->FilePath, FileMode);
  440. Append = FALSE;
  441. }
  442. }
  443. if (!Redir->File) {
  444. Print(L"Could not open output file %hs\n", FileName);
  445. return EFI_INVALID_PARAMETER;
  446. }
  447. Info = LibFileInfo (Redir->File);
  448. ASSERT (Info);
  449. if (Append) {
  450. Size = sizeof(UnicodeMarker);
  451. Redir->File->Read (Redir->File, &Size, &UnicodeMarker);
  452. if ((UnicodeMarker == UNICODE_BYTE_ORDER_MARK) && Ascii) {
  453. Print(L"Could not Append Ascii to Unicode file %hs\n", FileName);
  454. return EFI_INVALID_PARAMETER;
  455. } else if ((UnicodeMarker != UNICODE_BYTE_ORDER_MARK) && !Ascii) {
  456. Print(L"Could not Append Unicode to Asci file %hs\n", FileName);
  457. return EFI_INVALID_PARAMETER;
  458. }
  459. /*
  460. * Seek to end of the file
  461. */
  462. Redir->File->SetPosition (Redir->File, (UINT64)-1);
  463. } else {
  464. /*
  465. * Truncate the file
  466. */
  467. Info->FileSize = 0;
  468. Size = SIZE_OF_EFI_FILE_INFO + StrSize(Info->FileName);
  469. if (Redir->File->SetInfo) {
  470. Redir->File->SetInfo (Redir->File, &GenericFileInfo, Size, Info);
  471. } else {
  472. DEBUG ((D_ERROR, "SEnvRedirOutput: SetInfo in filesystem driver not complete\n"));
  473. }
  474. FreePool (Info);
  475. if (!Ascii) {
  476. Size = sizeof(UnicodeMarker);
  477. Redir->File->Write(Redir->File, &Size, &UnicodeMarker);
  478. }
  479. }
  480. /*
  481. * Allocate a new handle
  482. */
  483. CopyMem(&Redir->Out, &SEnvConToIo, sizeof(SIMPLE_TEXT_OUTPUT_INTERFACE));
  484. Status = LibInstallProtocolInterfaces (
  485. &Redir->Handle,
  486. &TextOutProtocol, &Redir->Out,
  487. &DevicePathProtocol, Redir->FilePath,
  488. NULL
  489. );
  490. Redir->Signature = ENV_REDIR_SIGNATURE;
  491. ASSERT (!EFI_ERROR(Status));
  492. return EFI_SUCCESS;
  493. }
  494. EFI_STATUS
  495. SEnvExecRedir (
  496. IN OUT ENV_SHELL_INTERFACE *Shell
  497. )
  498. {
  499. UINTN NewArgc;
  500. UINTN Index;
  501. UINTN RedirIndex;
  502. EFI_STATUS Status;
  503. CHAR16 *p;
  504. CHAR16 LastChar;
  505. BOOLEAN Ascii;
  506. BOOLEAN Append;
  507. EFI_SYSTEM_TABLE *SysTable;
  508. UINTN StringLen;
  509. BOOLEAN RedirStdOut;
  510. Status = EFI_SUCCESS;
  511. NewArgc = 0;
  512. SysTable = Shell->SystemTable;
  513. for (Index=1; Index < Shell->ShellInt.Argc && !EFI_ERROR(Status); Index += 1) {
  514. p = Shell->ShellInt.Argv[Index];
  515. /*
  516. * Trailing a or A means do ASCII default is unicode */
  517. StringLen = StrLen(p);
  518. LastChar = p[StringLen - 1];
  519. Ascii = ((LastChar == 'a') || (LastChar == 'A'));
  520. RedirStdOut = FALSE;
  521. if (StrnCmp(p, L"2>", 2) == 0) {
  522. Status = SEnvRedirOutput (Shell, Ascii, FALSE, &NewArgc, &Index, &Shell->StdErr);
  523. SysTable->StdErr = &Shell->StdErr.Out;
  524. SysTable->StandardErrorHandle = Shell->StdErr.Handle;
  525. Shell->ShellInt.StdErr = Shell->StdErr.File;
  526. } else if (StrnCmp(p, L"1>", 2) == 0) {
  527. Append = (p[2] == '>');
  528. RedirStdOut = TRUE;
  529. } else if (*p == '>') {
  530. Append = (p[1] == '>');
  531. RedirStdOut = TRUE;
  532. }
  533. if (RedirStdOut) {
  534. Status = SEnvRedirOutput (Shell, Ascii, Append, &NewArgc, &Index, &Shell->StdOut);
  535. SysTable->ConOut = &Shell->StdOut.Out;
  536. SysTable->ConsoleOutHandle = Shell->StdOut.Handle;
  537. Shell->ShellInt.StdOut = Shell->StdOut.File;
  538. }
  539. }
  540. /*
  541. * Strip redirection args from arglist, saving in RedirArgv so they can be
  542. * echoed in batch scripts.
  543. */
  544. if (NewArgc) {
  545. Shell->ShellInt.RedirArgc = Shell->ShellInt.Argc - (UINT32) NewArgc;
  546. Shell->ShellInt.RedirArgv = AllocateZeroPool (Shell->ShellInt.RedirArgc * sizeof(CHAR16 *));
  547. if ( !Shell->ShellInt.RedirArgv ) {
  548. Status = EFI_OUT_OF_RESOURCES;
  549. goto Done;
  550. }
  551. RedirIndex = 0;
  552. for (Index = NewArgc; Index < Shell->ShellInt.Argc; Index += 1) {
  553. Shell->ShellInt.RedirArgv[RedirIndex++] = Shell->ShellInt.Argv[Index];
  554. Shell->ShellInt.Argv[Index] = NULL;
  555. }
  556. Shell->ShellInt.Argc = (UINT32) NewArgc;
  557. } else {
  558. Shell->ShellInt.RedirArgc = 0;
  559. Shell->ShellInt.RedirArgv = NULL;
  560. }
  561. Done:
  562. return Status;
  563. }
  564. VOID
  565. SEnvCloseRedir (
  566. IN OUT ENV_SHELL_REDIR_FILE *Redir
  567. )
  568. {
  569. if (Redir->File) {
  570. Redir->File->Close (Redir->File);
  571. }
  572. if (Redir->Handle) {
  573. BS->UninstallProtocolInterface (Redir->Handle, &TextOutProtocol, &Redir->Out);
  574. BS->UninstallProtocolInterface (Redir->Handle, &TextInProtocol, &Redir->In);
  575. BS->UninstallProtocolInterface (Redir->Handle, &DevicePathProtocol, Redir->FilePath);
  576. FreePool (Redir->FilePath);
  577. }
  578. }
  579. EFI_STATUS
  580. SEnvDoExecute (
  581. IN EFI_HANDLE *ParentImageHandle,
  582. IN CHAR16 *CommandLine,
  583. IN ENV_SHELL_INTERFACE *Shell,
  584. IN BOOLEAN Output
  585. )
  586. {
  587. EFI_SHELL_INTERFACE *ParentShell;
  588. EFI_SYSTEM_TABLE *ParentSystemTable;
  589. EFI_STATUS Status;
  590. UINTN Index;
  591. SHELLENV_INTERNAL_COMMAND InternalCommand;
  592. EFI_HANDLE NewImage;
  593. EFI_FILE_HANDLE Script;
  594. /*
  595. * Switch output attribute to normal
  596. */
  597. Print (L"%N");
  598. /*
  599. * Chck that there is something to do
  600. */
  601. if (Shell->ShellInt.Argc < 1) {
  602. goto Done;
  603. }
  604. /*
  605. * Handle special case of the internal "set default device command"
  606. * Is it one argument that ends with a ":"?
  607. */
  608. Index = StrLen(Shell->ShellInt.Argv[0]);
  609. if (Shell->ShellInt.Argc == 1 && Shell->ShellInt.Argv[0][Index-1] == ':') {
  610. Status = SEnvSetCurrentDevice (Shell->ShellInt.Argv[0]);
  611. goto Done;
  612. }
  613. /*
  614. * Assume some defaults
  615. */
  616. BS->HandleProtocol (ParentImageHandle, &LoadedImageProtocol, (VOID*)&Shell->ShellInt.Info);
  617. Shell->ShellInt.ImageHandle = ParentImageHandle;
  618. Shell->ShellInt.StdIn = &SEnvIOFromCon;
  619. Shell->ShellInt.StdOut = &SEnvIOFromCon;
  620. Shell->ShellInt.StdErr = &SEnvErrIOFromCon;
  621. /*
  622. * Get parent's image stdout & stdin
  623. */
  624. Status = BS->HandleProtocol (ParentImageHandle, &ShellInterfaceProtocol, (VOID*)&ParentShell);
  625. if (EFI_ERROR(Status)) {
  626. goto Done;
  627. }
  628. ParentSystemTable = ParentShell->Info->SystemTable;
  629. Shell->ShellInt.StdIn = ParentShell->StdIn;
  630. Shell->ShellInt.StdOut = ParentShell->StdOut;
  631. Shell->ShellInt.StdErr = ParentShell->StdErr;
  632. Shell->SystemTable = NULL;
  633. Status = BS->AllocatePool(EfiRuntimeServicesData,
  634. sizeof(EFI_SYSTEM_TABLE),
  635. (VOID **)&Shell->SystemTable);
  636. if (EFI_ERROR(Status)) {
  637. goto Done;
  638. }
  639. CopyMem (Shell->SystemTable, Shell->ShellInt.Info->SystemTable, sizeof(EFI_SYSTEM_TABLE));
  640. Status = SEnvExecRedir (Shell);
  641. SetCrc (&Shell->SystemTable->Hdr);
  642. if (EFI_ERROR(Status)) {
  643. goto Done;
  644. }
  645. /*
  646. * Attempt to dispatch it as an internal command
  647. */
  648. InternalCommand = SEnvGetCmdDispath(Shell->ShellInt.Argv[0]);
  649. if (InternalCommand) {
  650. /* Push & replace the current shell info on the parent image handle. (note we are using
  651. * the parent image's loaded image information structure) */
  652. BS->ReinstallProtocolInterface (ParentImageHandle, &ShellInterfaceProtocol, ParentShell, &Shell->ShellInt);
  653. ParentShell->Info->SystemTable = Shell->SystemTable;
  654. InitializeShellApplication (ParentImageHandle, Shell->SystemTable);
  655. SEnvBatchEchoCommand( Shell );
  656. /* Dispatch the command */
  657. Status = InternalCommand (ParentImageHandle, Shell->ShellInt.Info->SystemTable);
  658. /* Restore the parent's image handle shell info */
  659. BS->ReinstallProtocolInterface (ParentImageHandle, &ShellInterfaceProtocol, &Shell->ShellInt, ParentShell);
  660. ParentShell->Info->SystemTable = ParentSystemTable;
  661. InitializeShellApplication (ParentImageHandle, ParentSystemTable);
  662. goto Done;
  663. }
  664. /*
  665. * Load the app, or open the script
  666. */
  667. SEnvLoadImage(ParentImageHandle, Shell->ShellInt.Argv[0], &NewImage, &Script);
  668. if (!NewImage && !Script) {
  669. if ( Output ) {
  670. Print(L"'%es' not found\n", Shell->ShellInt.Argv[0]);
  671. }
  672. Status = EFI_INVALID_PARAMETER;
  673. goto Done;
  674. }
  675. if (NewImage) {
  676. CHAR16 *CurrentDir;
  677. CHAR16 *OptionsBuffer;
  678. UINT32 OptionsSize;
  679. /*
  680. * Put the shell info on the handle
  681. */
  682. BS->HandleProtocol (NewImage, &LoadedImageProtocol, (VOID*)&Shell->ShellInt.Info);
  683. LibInstallProtocolInterfaces (&NewImage, &ShellInterfaceProtocol, &Shell->ShellInt, NULL);
  684. /*
  685. * Create load options which may include command line and current
  686. * working directory
  687. */
  688. CurrentDir = SEnvGetCurDir(NULL);
  689. OptionsSize = (UINT32)StrSize(CommandLine); /* StrSize includes NULL */
  690. if (CurrentDir)
  691. OptionsSize += (UINT32)StrSize(CurrentDir); /* StrSize includes NULL */
  692. OptionsBuffer = AllocateZeroPool (OptionsSize);
  693. if (OptionsBuffer) {
  694. /*
  695. * Set the buffer before we manipulate it.
  696. */
  697. Shell->ShellInt.Info->LoadOptions = OptionsBuffer;
  698. Shell->ShellInt.Info->LoadOptionsSize = OptionsSize;
  699. /*
  700. * Copy the comamand line and current working directory
  701. */
  702. StrCpy ((CHAR16*)OptionsBuffer, CommandLine);
  703. if (CurrentDir)
  704. StrCpy (&OptionsBuffer[ StrLen (CommandLine) + 1 ], CurrentDir);
  705. } else {
  706. Shell->ShellInt.Info->LoadOptions = CommandLine;
  707. Shell->ShellInt.Info->LoadOptionsSize = (UINT32) StrSize(CommandLine);
  708. }
  709. /*
  710. * Pass a copy of the system table with new input & outputs
  711. */
  712. Shell->ShellInt.Info->SystemTable = Shell->SystemTable;
  713. /*
  714. * If the image is an app start it, else abort it
  715. */
  716. if (Shell->ShellInt.Info->ImageCodeType == EfiLoaderCode) {
  717. InitializeShellApplication (ParentImageHandle, Shell->SystemTable);
  718. SEnvBatchEchoCommand( Shell );
  719. Status = BS->StartImage (NewImage, 0, NULL);
  720. } else {
  721. Print (L"Image is not a application\n");
  722. BS->Exit(NewImage, EFI_INVALID_PARAMETER, 0, NULL);
  723. Status = EFI_INVALID_PARAMETER;
  724. }
  725. /*
  726. * App has exited, remove our data from the image handle
  727. */
  728. if (OptionsBuffer) {
  729. BS->FreePool (OptionsBuffer);
  730. }
  731. BS->UninstallProtocolInterface(NewImage, &ShellInterfaceProtocol, &Shell->ShellInt);
  732. InitializeShellApplication (ParentImageHandle, ParentSystemTable);
  733. } else if ( Script ) {
  734. SEnvBatchEchoCommand( Shell );
  735. /* Push & replace the current shell info on the parent image handle. (note we are using
  736. * the parent image's loaded image information structure) */
  737. BS->ReinstallProtocolInterface (ParentImageHandle, &ShellInterfaceProtocol, ParentShell, &Shell->ShellInt);
  738. ParentShell->Info->SystemTable = Shell->SystemTable;
  739. Status = SEnvExecuteScript( Shell, Script );
  740. /* Restore the parent's image handle shell info */
  741. BS->ReinstallProtocolInterface (ParentImageHandle, &ShellInterfaceProtocol, &Shell->ShellInt, ParentShell);
  742. ParentShell->Info->SystemTable = ParentSystemTable;
  743. InitializeShellApplication (ParentImageHandle, ParentSystemTable);
  744. }
  745. Done:
  746. SEnvBatchSetLastError( Status );
  747. if (EFI_ERROR(Status) && Output) {
  748. Print (L"Exit status code: %r\n", Status);
  749. }
  750. /*
  751. * Cleanup
  752. */
  753. if (Shell) {
  754. /*
  755. * Free copy of the system table
  756. */
  757. if (Shell->SystemTable) {
  758. BS->FreePool(Shell->SystemTable);
  759. }
  760. /*
  761. * If there's an arg list, free it
  762. */
  763. if (Shell->ShellInt.Argv) {
  764. for (Index=0; Index < Shell->ShellInt.Argc; Index += 1) {
  765. FreePool (Shell->ShellInt.Argv[Index]);
  766. }
  767. FreePool (Shell->ShellInt.Argv);
  768. }
  769. /*
  770. * If any redirection arguments were saved, free them
  771. */
  772. if (Shell->ShellInt.RedirArgv) {
  773. for (Index=0; Index < Shell->ShellInt.RedirArgc; Index++ ) {
  774. FreePool( Shell->ShellInt.RedirArgv[Index] );
  775. }
  776. FreePool( Shell->ShellInt.RedirArgv );
  777. }
  778. /*
  779. * Close any file redirection
  780. */
  781. SEnvCloseRedir(&Shell->StdOut);
  782. SEnvCloseRedir(&Shell->StdErr);
  783. SEnvCloseRedir(&Shell->StdIn);
  784. }
  785. /*
  786. * Switch output attribute to normal
  787. */
  788. Print (L"%N");
  789. return Status;
  790. }
  791. EFI_STATUS
  792. SEnvExecute (
  793. IN EFI_HANDLE *ParentImageHandle,
  794. IN CHAR16 *CommandLine,
  795. IN BOOLEAN Output
  796. )
  797. {
  798. ENV_SHELL_INTERFACE Shell;
  799. EFI_STATUS Status = EFI_SUCCESS;
  800. /*
  801. * Convert the command line to an arg list
  802. */
  803. ZeroMem( &Shell, sizeof(Shell ) );
  804. Status = SEnvStringToArg( CommandLine, Output, &Shell.ShellInt.Argv, &Shell.ShellInt.Argc );
  805. if (EFI_ERROR(Status)) {
  806. goto Done;
  807. }
  808. /*
  809. * Execute the command
  810. */
  811. Status = SEnvDoExecute( ParentImageHandle, CommandLine, &Shell, Output );
  812. if (EFI_ERROR(Status)) {
  813. goto Done;
  814. }
  815. Done:
  816. return Status;
  817. }
  818. VOID
  819. INTERNAL
  820. SEnvLoadImage (
  821. IN EFI_HANDLE ParentImage,
  822. IN CHAR16 *IName,
  823. OUT EFI_HANDLE *pImageHandle,
  824. OUT EFI_FILE_HANDLE *pScriptHandle
  825. )
  826. {
  827. CHAR16 *Path;
  828. CHAR16 *p1, *p2;
  829. CHAR16 *PathName;
  830. EFI_DEVICE_PATH *DevicePath;
  831. FILEPATH_DEVICE_PATH *FilePath;
  832. CHAR16 *FilePathStr;
  833. CHAR16 c;
  834. EFI_HANDLE ImageHandle;
  835. EFI_STATUS Status;
  836. SENV_OPEN_DIR *OpenDir, *OpenDirHead;
  837. EFI_FILE_HANDLE ScriptHandle;
  838. PathName = NULL;
  839. DevicePath = NULL;
  840. FilePathStr = NULL;
  841. ImageHandle = NULL;
  842. ScriptHandle = NULL;
  843. OpenDirHead = NULL;
  844. *pImageHandle = NULL;
  845. *pScriptHandle = NULL;
  846. /*
  847. * Get the path variable
  848. */
  849. Path = SEnvGetEnv (L"path");
  850. if (!Path) {
  851. DEBUG ((D_PARSE, "SEnvLoadImage: no path variable\n"));
  852. return ;
  853. }
  854. p1 = StrDuplicate(Path);
  855. Path = p1;
  856. /*
  857. * Search each path component
  858. * (using simple ';' as separator here - oh well)
  859. */
  860. c = *Path;
  861. for (p1=Path; *p1 && c; p1=p2+1) {
  862. for (p2=p1; *p2 && *p2 != ';'; p2++) ;
  863. if (p1 != p2) {
  864. c = *p2;
  865. *p2 = 0; /* null terminate the path */
  866. /*
  867. * Open the directory
  868. */
  869. DevicePath = SEnvNameToPath(p1);
  870. if (!DevicePath) {
  871. continue;
  872. }
  873. OpenDir = AllocateZeroPool (sizeof(SENV_OPEN_DIR));
  874. if (!OpenDir) {
  875. break;
  876. }
  877. OpenDir->Handle = ShellOpenFilePath(DevicePath, EFI_FILE_MODE_READ);
  878. OpenDir->Next = OpenDirHead;
  879. OpenDirHead = OpenDir;
  880. FreePool (DevicePath);
  881. DevicePath = NULL;
  882. if (!OpenDir->Handle) {
  883. continue;
  884. }
  885. /*
  886. * Attempt to open it as an execuatble
  887. */
  888. PathName = (p2[-1] == ':' || p2[-1] == '\\') ? L"%s%s.efi" : L"%s\\%s.efi";
  889. PathName = PoolPrint(PathName, p1, IName);
  890. if (!PathName) {
  891. break;
  892. }
  893. DevicePath = SEnvNameToPath(PathName);
  894. if (!DevicePath) {
  895. continue;
  896. }
  897. /*
  898. * Print the file path
  899. */
  900. FilePathStr = DevicePathToStr(DevicePath);
  901. /* DEBUG((D_PARSE, "SEnvLoadImage: load %hs\n", FilePathStr)); */
  902. /*
  903. * Attempt to load the image
  904. */
  905. Status = BS->LoadImage (FALSE, ParentImage, DevicePath, NULL, 0, &ImageHandle);
  906. if (!EFI_ERROR(Status)) {
  907. goto Done;
  908. }
  909. /*
  910. * Try as a ".nsh" file
  911. */
  912. FreePool(DevicePath);
  913. FreePool(PathName);
  914. DevicePath = NULL;
  915. PathName = NULL;
  916. if ( StriCmp( L".nsh", &(IName[StrLen(IName)-4]) ) == 0 ) {
  917. /* User entered entire filename with .nsh extension */
  918. PathName = PoolPrint (L"%s", IName);
  919. } else {
  920. /* User entered filename without .nsh extension */
  921. PathName = PoolPrint (L"%s.nsh", IName);
  922. }
  923. if (!PathName) {
  924. break;
  925. }
  926. DevicePath = SEnvFileNameToPath(PathName);
  927. if (DevicePath) {
  928. ASSERT (
  929. DevicePathType(DevicePath) == MEDIA_DEVICE_PATH &&
  930. DevicePathSubType(DevicePath) == MEDIA_FILEPATH_DP
  931. );
  932. FilePath = (FILEPATH_DEVICE_PATH *) DevicePath;
  933. Status = OpenDir->Handle->Open (
  934. OpenDir->Handle,
  935. &ScriptHandle,
  936. FilePath->PathName,
  937. EFI_FILE_MODE_READ,
  938. 0
  939. );
  940. FreePool(DevicePath);
  941. DevicePath = NULL;
  942. if (!EFI_ERROR(Status)) {
  943. goto Done;
  944. }
  945. }
  946. ScriptHandle = NULL; /* BUGBUG */
  947. }
  948. if (DevicePath) {
  949. FreePool (DevicePath);
  950. DevicePath = NULL;
  951. }
  952. if (PathName) {
  953. FreePool (PathName);
  954. PathName = NULL;
  955. }
  956. if (FilePathStr) {
  957. FreePool (FilePathStr);
  958. FilePathStr = NULL;
  959. }
  960. }
  961. Done:
  962. while (OpenDirHead) {
  963. if (OpenDirHead->Handle) {
  964. OpenDirHead->Handle->Close (OpenDirHead->Handle);
  965. }
  966. OpenDir = OpenDirHead->Next;
  967. FreePool (OpenDirHead);
  968. OpenDirHead = OpenDir;
  969. }
  970. FreePool (Path);
  971. if (DevicePath) {
  972. FreePool (DevicePath);
  973. DevicePath = NULL;
  974. }
  975. if (PathName) {
  976. FreePool (PathName);
  977. PathName = NULL;
  978. }
  979. if (FilePathStr) {
  980. FreePool (FilePathStr);
  981. FilePathStr = NULL;
  982. }
  983. if (ImageHandle) {
  984. ASSERT (!ScriptHandle);
  985. *pImageHandle = ImageHandle;
  986. }
  987. if (ScriptHandle) {
  988. ASSERT (!ImageHandle);
  989. *pScriptHandle = ScriptHandle;
  990. }
  991. }
  992. EFI_STATUS
  993. SEnvExit (
  994. IN EFI_HANDLE ImageHandle,
  995. IN EFI_SYSTEM_TABLE *SystemTable
  996. )
  997. {
  998. /* BUGBUG: for now just use a "magic" return code to indicate EOF */
  999. return -1;
  1000. }