]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Application/Shell/Shell.c
udk2010.up2.shell initial release.
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
1 /** @file
2 This is THE shell (application)
3
4 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "Shell.h"
16
17 //
18 // Initialize the global structure
19 //
20 SHELL_INFO ShellInfoObject = {
21 NULL,
22 NULL,
23 FALSE,
24 FALSE,
25 {
26 0,
27 0,
28 0,
29 0,
30 0,
31 0,
32 0,
33 0,
34 0,
35 0,
36 NULL,
37 NULL
38 },
39 {0,0},
40 {
41 {0,0},
42 0,
43 0,
44 TRUE
45 },
46 NULL,
47 0,
48 NULL,
49 NULL,
50 NULL,
51 NULL,
52 NULL,
53 {0,0,NULL,NULL},
54 {0,0},
55 };
56
57 STATIC CONST CHAR16 mScriptExtension[] = L".NSH";
58 STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";
59
60 /**
61 The entry point for the application.
62
63 @param[in] ImageHandle The firmware allocated handle for the EFI image.
64 @param[in] SystemTable A pointer to the EFI System Table.
65
66 @retval EFI_SUCCESS The entry point is executed successfully.
67 @retval other Some error occurs when executing this entry point.
68
69 **/
70 EFI_STATUS
71 EFIAPI
72 UefiMain (
73 IN EFI_HANDLE ImageHandle,
74 IN EFI_SYSTEM_TABLE *SystemTable
75 )
76 {
77 EFI_STATUS Status;
78 CHAR16 *TempString;
79 UINTN Size;
80 // EFI_INPUT_KEY Key;
81
82 // gST->ConOut->OutputString(gST->ConOut, L"ReadKeyStroke Calling\r\n");
83 //
84 // Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
85 //
86 // gST->ConOut->OutputString(gST->ConOut, L"ReadKeyStroke Done\r\n");
87 // gBS->Stall (1000000);
88
89 if (PcdGet8(PcdShellSupportLevel) > 3) {
90 return (EFI_UNSUPPORTED);
91 }
92
93 //
94 // Clear the screen
95 //
96 Status = gST->ConOut->ClearScreen(gST->ConOut);
97 ASSERT_EFI_ERROR(Status);
98
99 //
100 // Populate the global structure from PCDs
101 //
102 ShellInfoObject.ImageDevPath = NULL;
103 ShellInfoObject.FileDevPath = NULL;
104 ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault);
105 ShellInfoObject.ViewingSettings.InsertMode = PcdGetBool(PcdShellInsertModeDefault);
106 ShellInfoObject.LogScreenCount = PcdGet8 (PcdShellScreenLogCount );
107
108 //
109 // verify we dont allow for spec violation
110 //
111 ASSERT(ShellInfoObject.LogScreenCount >= 3);
112
113 //
114 // Initialize the LIST ENTRY objects...
115 //
116 InitializeListHead(&ShellInfoObject.BufferToFreeList.Link);
117 InitializeListHead(&ShellInfoObject.ViewingSettings.CommandHistory.Link);
118 InitializeListHead(&ShellInfoObject.SplitList.Link);
119
120 //
121 // Check PCDs for optional features that are not implemented yet.
122 //
123 if ( PcdGetBool(PcdShellSupportOldProtocols)
124 || !FeaturePcdGet(PcdShellRequireHiiPlatform)
125 || FeaturePcdGet(PcdShellSupportFrameworkHii)
126 ) {
127 return (EFI_UNSUPPORTED);
128 }
129
130 //
131 // turn off the watchdog timer
132 //
133 gBS->SetWatchdogTimer (0, 0, 0, NULL);
134
135 //
136 // install our console logger. This will keep a log of the output for back-browsing
137 //
138 Status = ConsoleLoggerInstall(ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);
139 ASSERT_EFI_ERROR(Status);
140
141 //
142 // Enable the cursor to be visible
143 //
144 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
145
146 //
147 // If supporting EFI 1.1 we need to install HII protocol
148 // only do this if PcdShellRequireHiiPlatform == FALSE
149 //
150 // remove EFI_UNSUPPORTED check above when complete.
151 ///@todo add support for Framework HII
152
153 //
154 // install our (solitary) HII package
155 //
156 ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);
157 if (ShellInfoObject.HiiHandle == NULL) {
158 if (PcdGetBool(PcdShellSupportFrameworkHii)) {
159 ///@todo Add our package into Framework HII
160 }
161 if (ShellInfoObject.HiiHandle == NULL) {
162 return (EFI_NOT_STARTED);
163 }
164 }
165
166 //
167 // create and install the EfiShellParametersProtocol
168 //
169 Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);
170 ASSERT_EFI_ERROR(Status);
171 ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL);
172
173 //
174 // create and install the EfiShellProtocol
175 //
176 Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol);
177 ASSERT_EFI_ERROR(Status);
178 ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL);
179
180 //
181 // Now initialize the shell library (it requires Shell Parameters protocol)
182 //
183 Status = ShellInitialize();
184 ASSERT_EFI_ERROR(Status);
185
186 Status = CommandInit();
187 ASSERT_EFI_ERROR(Status);
188
189 //
190 // Check the command line
191 //
192 Status = ProcessCommandLine();
193
194 //
195 // If shell support level is >= 1 create the mappings and paths
196 //
197 if (PcdGet8(PcdShellSupportLevel) >= 1) {
198 Status = ShellCommandCreateInitialMappingsAndPaths();
199 }
200
201 //
202 // save the device path for the loaded image and the device path for the filepath (under loaded image)
203 // These are where to look for the startup.nsh file
204 //
205 Status = GetDevicePathsForImageAndFile(&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);
206 ASSERT_EFI_ERROR(Status);
207
208 //
209 // Display the version
210 //
211 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {
212 ShellPrintHiiEx (
213 0,
214 gST->ConOut->Mode->CursorRow,
215 NULL,
216 STRING_TOKEN (STR_VER_OUTPUT_MAIN),
217 ShellInfoObject.HiiHandle,
218 SupportLevel[PcdGet8(PcdShellSupportLevel)],
219 gEfiShellProtocol->MajorVersion,
220 gEfiShellProtocol->MinorVersion,
221 (gST->Hdr.Revision&0xffff0000)>>16,
222 (gST->Hdr.Revision&0x0000ffff),
223 gST->FirmwareVendor,
224 gST->FirmwareRevision
225 );
226 }
227
228 //
229 // Display the mapping
230 //
231 if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {
232 Status = RunCommand(L"map");
233 ASSERT_EFI_ERROR(Status);
234 }
235
236 //
237 // init all the built in alias'
238 //
239 Status = SetBuiltInAlias();
240 ASSERT_EFI_ERROR(Status);
241
242 //
243 // Initialize environment variables
244 //
245 if (ShellCommandGetProfileList() != NULL) {
246 Status = InternalEfiShellSetEnv(L"profiles", ShellCommandGetProfileList(), TRUE);
247 ASSERT_EFI_ERROR(Status);
248 }
249
250 Size = 100;
251 TempString = AllocateZeroPool(Size);
252
253 UnicodeSPrint(TempString, Size, L"%d", PcdGet8(PcdShellSupportLevel));
254 Status = InternalEfiShellSetEnv(L"uefishellsupport", TempString, TRUE);
255 ASSERT_EFI_ERROR(Status);
256
257 UnicodeSPrint(TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);
258 Status = InternalEfiShellSetEnv(L"uefishellversion", TempString, TRUE);
259 ASSERT_EFI_ERROR(Status);
260
261 UnicodeSPrint(TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);
262 Status = InternalEfiShellSetEnv(L"uefiversion", TempString, TRUE);
263 ASSERT_EFI_ERROR(Status);
264
265 FreePool(TempString);
266
267 if (!EFI_ERROR(Status)) {
268 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
269 //
270 // Set up the event for CTRL-C monitoring...
271 //
272
273 ///@todo add support for using SimpleInputEx here
274 // if SimpleInputEx is not available display a warning.
275 }
276
277 if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) {
278 //
279 // process the startup script or launch the called app.
280 //
281 Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
282 }
283
284 if ((PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
285 //
286 // begin the UI waiting loop
287 //
288 do {
289 //
290 // clean out all the memory allocated for CONST <something> * return values
291 // between each shell prompt presentation
292 //
293 if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
294 FreeBufferList(&ShellInfoObject.BufferToFreeList);
295 }
296
297 //
298 // Reset page break back to default.
299 //
300 ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault);
301 ShellInfoObject.ConsoleInfo->Enabled = TRUE;
302 ShellInfoObject.ConsoleInfo->RowCounter = 0;
303
304 //
305 // Display Prompt
306 //
307 Status = DoShellPrompt();
308 } while (!ShellCommandGetExit());
309 }
310 }
311 //
312 // uninstall protocols / free memory / etc...
313 //
314 if (ShellInfoObject.UserBreakTimer != NULL) {
315 gBS->CloseEvent(ShellInfoObject.UserBreakTimer);
316 DEBUG_CODE(ShellInfoObject.UserBreakTimer = NULL;);
317 }
318
319 if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){
320 ShellInfoObject.NewEfiShellProtocol->SetEnv(L"cwd", L"", TRUE);
321 }
322
323 if (ShellInfoObject.ImageDevPath != NULL) {
324 FreePool(ShellInfoObject.ImageDevPath);
325 DEBUG_CODE(ShellInfoObject.ImageDevPath = NULL;);
326 }
327 if (ShellInfoObject.FileDevPath != NULL) {
328 FreePool(ShellInfoObject.FileDevPath);
329 DEBUG_CODE(ShellInfoObject.FileDevPath = NULL;);
330 }
331 if (ShellInfoObject.NewShellParametersProtocol != NULL) {
332 CleanUpShellParametersProtocol(ShellInfoObject.NewShellParametersProtocol);
333 DEBUG_CODE(ShellInfoObject.NewShellParametersProtocol = NULL;);
334 }
335 if (ShellInfoObject.NewEfiShellProtocol != NULL){
336 CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol);
337 DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);
338 }
339
340 if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
341 FreeBufferList(&ShellInfoObject.BufferToFreeList);
342 }
343
344 if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){
345 ASSERT(FALSE);
346 }
347
348 if (ShellInfoObject.ShellInitSettings.FileName != NULL) {
349 FreePool(ShellInfoObject.ShellInitSettings.FileName);
350 DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileName = NULL;);
351 }
352
353 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
354 FreePool(ShellInfoObject.ShellInitSettings.FileOptions);
355 DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileOptions = NULL;);
356 }
357
358 if (ShellInfoObject.HiiHandle != NULL) {
359 HiiRemovePackages(ShellInfoObject.HiiHandle);
360 DEBUG_CODE(ShellInfoObject.HiiHandle = NULL;);
361 }
362
363 if (!IsListEmpty(&ShellInfoObject.ViewingSettings.CommandHistory.Link)){
364 FreeBufferList(&ShellInfoObject.ViewingSettings.CommandHistory);
365 }
366
367 ASSERT(ShellInfoObject.ConsoleInfo != NULL);
368 if (ShellInfoObject.ConsoleInfo != NULL) {
369 ConsoleLoggerUninstall(ShellInfoObject.ConsoleInfo);
370 FreePool(ShellInfoObject.ConsoleInfo);
371 DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);
372 }
373
374 return (Status);
375 }
376
377 /**
378 Sets all the alias' that were registered with the ShellCommandLib library.
379
380 @retval EFI_SUCCESS all init commands were run sucessfully.
381 **/
382 EFI_STATUS
383 EFIAPI
384 SetBuiltInAlias(
385 )
386 {
387 EFI_STATUS Status;
388 CONST ALIAS_LIST *List;
389 ALIAS_LIST *Node;
390
391 //
392 // Get all the commands we want to run
393 //
394 List = ShellCommandGetInitAliasList();
395
396 //
397 // for each command in the List
398 //
399 for ( Node = (ALIAS_LIST*)GetFirstNode(&List->Link)
400 ; !IsNull (&List->Link, &Node->Link)
401 ; Node = (ALIAS_LIST *)GetNextNode(&List->Link, &Node->Link)
402 ){
403 //
404 // install the alias'
405 //
406 Status = InternalSetAlias(Node->CommandString, Node->Alias, TRUE);
407 ASSERT_EFI_ERROR(Status);
408 }
409 return (EFI_SUCCESS);
410 }
411
412 /**
413 Internal function to determine if 2 command names are really the same.
414
415 @param[in] Command1 The pointer to the first command name.
416 @param[in] Command2 The pointer to the second command name.
417
418 @retval TRUE The 2 command names are the same.
419 @retval FALSE The 2 command names are not the same.
420 **/
421 BOOLEAN
422 EFIAPI
423 IsCommand(
424 IN CONST CHAR16 *Command1,
425 IN CONST CHAR16 *Command2
426 )
427 {
428 if (StringNoCaseCompare(&Command1, &Command2) == 0) {
429 return (TRUE);
430 }
431 return (FALSE);
432 }
433
434 /**
435 Internal function to determine if a command is a script only command.
436
437 @param[in] CommandName The pointer to the command name.
438
439 @retval TRUE The command is a script only command.
440 @retval FALSE The command is not a script only command.
441 **/
442 BOOLEAN
443 EFIAPI
444 IsScriptOnlyCommand(
445 IN CONST CHAR16 *CommandName
446 )
447 {
448 if (IsCommand(CommandName, L"for")
449 ||IsCommand(CommandName, L"endfor")
450 ||IsCommand(CommandName, L"if")
451 ||IsCommand(CommandName, L"else")
452 ||IsCommand(CommandName, L"endif")
453 ||IsCommand(CommandName, L"goto")) {
454 return (TRUE);
455 }
456 return (FALSE);
457 }
458
459
460
461 /**
462 This function will populate the 2 device path protocol parameters based on the
463 global gImageHandle. The DevPath will point to the device path for the handle that has
464 loaded image protocol installed on it. The FilePath will point to the device path
465 for the file that was loaded.
466
467 @param[in,out] DevPath On a sucessful return the device path to the loaded image.
468 @param[in,out] FilePath On a sucessful return the device path to the file.
469
470 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
471 @retval other A error from gBS->HandleProtocol.
472
473 @sa HandleProtocol
474 **/
475 EFI_STATUS
476 EFIAPI
477 GetDevicePathsForImageAndFile (
478 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,
479 IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
480 )
481 {
482 EFI_STATUS Status;
483 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
484 EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;
485
486 ASSERT(DevPath != NULL);
487 ASSERT(FilePath != NULL);
488
489 Status = gBS->OpenProtocol (
490 gImageHandle,
491 &gEfiLoadedImageProtocolGuid,
492 (VOID**)&LoadedImage,
493 gImageHandle,
494 NULL,
495 EFI_OPEN_PROTOCOL_GET_PROTOCOL
496 );
497 if (!EFI_ERROR (Status)) {
498 Status = gBS->OpenProtocol (
499 LoadedImage->DeviceHandle,
500 &gEfiDevicePathProtocolGuid,
501 (VOID**)&ImageDevicePath,
502 gImageHandle,
503 NULL,
504 EFI_OPEN_PROTOCOL_GET_PROTOCOL
505 );
506 if (!EFI_ERROR (Status)) {
507 *DevPath = DuplicateDevicePath (ImageDevicePath);
508 *FilePath = DuplicateDevicePath (LoadedImage->FilePath);
509 }
510 gBS->CloseProtocol(
511 gImageHandle,
512 &gEfiLoadedImageProtocolGuid,
513 gImageHandle,
514 NULL);
515 }
516 return (Status);
517 }
518
519 STATIC CONST SHELL_PARAM_ITEM mShellParamList[] = {
520 {L"-nostartup", TypeFlag},
521 {L"-startup", TypeFlag},
522 {L"-noconsoleout", TypeFlag},
523 {L"-noconsolein", TypeFlag},
524 {L"-nointerrupt", TypeFlag},
525 {L"-nomap", TypeFlag},
526 {L"-noversion", TypeFlag},
527 {L"-startup", TypeFlag},
528 {L"-delay", TypeValue},
529 {NULL, TypeMax}
530 };
531 /**
532 Process all Uefi Shell 2.0 command line options.
533
534 see Uefi Shell 2.0 section 3.2 for full details.
535
536 the command line must resemble the following:
537
538 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
539
540 ShellOpt-options Options which control the initialization behavior of the shell.
541 These options are read from the EFI global variable "ShellOpt"
542 and are processed before options or file-name.
543
544 options Options which control the initialization behavior of the shell.
545
546 file-name The name of a UEFI shell application or script to be executed
547 after initialization is complete. By default, if file-name is
548 specified, then -nostartup is implied. Scripts are not supported
549 by level 0.
550
551 file-name-options The command-line options that are passed to file-name when it
552 is invoked.
553
554 This will initialize the ShellInfoObject.ShellInitSettings global variable.
555
556 @retval EFI_SUCCESS The variable is initialized.
557 **/
558 EFI_STATUS
559 EFIAPI
560 ProcessCommandLine(
561 VOID
562 )
563 {
564 EFI_STATUS Status;
565 LIST_ENTRY *Package;
566 UINTN Size;
567 CONST CHAR16 *TempConst;
568 UINTN Count;
569 UINTN LoopVar;
570 CHAR16 *ProblemParam;
571
572 Package = NULL;
573 ProblemParam = NULL;
574
575 Status = ShellCommandLineParse (mShellParamList, &Package, NULL, FALSE);
576
577 Count = 1;
578 Size = 0;
579 TempConst = ShellCommandLineGetRawValue(Package, Count++);
580 if (TempConst != NULL && StrLen(TempConst)) {
581 ShellInfoObject.ShellInitSettings.FileName = AllocatePool(StrSize(TempConst));
582 StrCpy(ShellInfoObject.ShellInitSettings.FileName, TempConst);
583 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;
584 for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
585 if (StrCmp(gEfiShellParametersProtocol->Argv[LoopVar], ShellInfoObject.ShellInitSettings.FileName)==0) {
586 LoopVar++;
587 //
588 // We found the file... add the rest of the params...
589 //
590 for ( ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
591 ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));
592 StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
593 &Size,
594 L" ",
595 0);
596 StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
597 &Size,
598 gEfiShellParametersProtocol->Argv[LoopVar],
599 0);
600 }
601 }
602 }
603 } else {
604 ShellCommandLineFreeVarList(Package);
605 Package = NULL;
606 Status = ShellCommandLineParse (mShellParamList, &Package, &ProblemParam, FALSE);
607 if (EFI_ERROR(Status)) {
608 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), ShellInfoObject.HiiHandle, ProblemParam);
609 FreePool(ProblemParam);
610 ShellCommandLineFreeVarList(Package);
611 return (EFI_INVALID_PARAMETER);
612 }
613 }
614
615 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = ShellCommandLineGetFlag(Package, L"-startup");
616 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = ShellCommandLineGetFlag(Package, L"-nostartup");
617 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = ShellCommandLineGetFlag(Package, L"-noconsoleout");
618 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = ShellCommandLineGetFlag(Package, L"-noconsolein");
619 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = ShellCommandLineGetFlag(Package, L"-nointerrupt");
620 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = ShellCommandLineGetFlag(Package, L"-nomap");
621 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = ShellCommandLineGetFlag(Package, L"-noversion");
622 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = ShellCommandLineGetFlag(Package, L"-delay");
623
624 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay) {
625 TempConst = ShellCommandLineGetValue(Package, L"-delay");
626 if (TempConst != NULL) {
627 ShellInfoObject.ShellInitSettings.Delay = StrDecimalToUintn (TempConst);
628 } else {
629 ShellInfoObject.ShellInitSettings.Delay = 5;
630 }
631 } else {
632 ShellInfoObject.ShellInitSettings.Delay = 5;
633 }
634
635 ShellCommandLineFreeVarList(Package);
636
637 return (Status);
638 }
639
640 /**
641 Handles all interaction with the default startup script.
642
643 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
644
645 @param ImagePath the path to the image for shell. first place to look for the startup script
646 @param FilePath the path to the file for shell. second place to look for the startup script.
647
648 @retval EFI_SUCCESS the variable is initialized.
649 **/
650 EFI_STATUS
651 EFIAPI
652 DoStartupScript(
653 EFI_DEVICE_PATH_PROTOCOL *ImagePath,
654 EFI_DEVICE_PATH_PROTOCOL *FilePath
655 )
656 {
657 EFI_STATUS Status;
658 UINTN Delay;
659 EFI_INPUT_KEY Key;
660 SHELL_FILE_HANDLE FileHandle;
661 EFI_DEVICE_PATH_PROTOCOL *NewPath;
662 EFI_DEVICE_PATH_PROTOCOL *NamePath;
663 CHAR16 *FileStringPath;
664 UINTN NewSize;
665
666 Key.UnicodeChar = CHAR_NULL;
667 Key.ScanCode = 0;
668 FileHandle = NULL;
669
670 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) {
671 //
672 // launch something else instead
673 //
674 NewSize = StrSize(ShellInfoObject.ShellInitSettings.FileName);
675 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
676 NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16);
677 }
678 FileStringPath = AllocateZeroPool(NewSize);
679 StrCpy(FileStringPath, ShellInfoObject.ShellInitSettings.FileName);
680 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
681 StrCat (FileStringPath, L" ");
682 StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);
683 }
684 Status = RunCommand(FileStringPath);
685 FreePool(FileStringPath);
686 return (Status);
687
688 }
689
690 //
691 // for shell level 0 we do no scripts
692 // Without the Startup bit overriding we allow for nostartup to prevent scripts
693 //
694 if ( (PcdGet8(PcdShellSupportLevel) < 1)
695 || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup)
696 ){
697 return (EFI_SUCCESS);
698 }
699
700 //
701 // print out our warning and see if they press a key
702 //
703 for ( Status = EFI_UNSUPPORTED, Delay = (ShellInfoObject.ShellInitSettings.Delay * 10)
704 ; Delay > 0 && EFI_ERROR(Status)
705 ; Delay--
706 ){
707 ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay/10);
708 gBS->Stall (100000);
709 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
710 }
711 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);
712
713 //
714 // ESC was pressed
715 //
716 if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) {
717 return (EFI_SUCCESS);
718 }
719
720 NamePath = FileDevicePath (NULL, L"startup.nsh");
721 //
722 // Try the first location
723 //
724 NewPath = AppendDevicePathNode (ImagePath, NamePath);
725 Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);
726 if (EFI_ERROR(Status)) {
727 //
728 // Try the second location
729 //
730 FreePool(NewPath);
731 NewPath = AppendDevicePathNode (FilePath , NamePath);
732 Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);
733 }
734
735 //
736 // If we got a file, run it
737 //
738 if (!EFI_ERROR(Status)) {
739 Status = RunScriptFileHandle (FileHandle, L"startup.nsh");
740 ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
741 } else {
742 //
743 // we return success since we dont need to have a startup script
744 //
745 Status = EFI_SUCCESS;
746 ASSERT(FileHandle == NULL);
747 }
748
749 FreePool(NamePath);
750 FreePool(NewPath);
751
752 return (Status);
753 }
754
755 /**
756 Function to perform the shell prompt looping. It will do a single prompt,
757 dispatch the result, and then return. It is expected that the caller will
758 call this function in a loop many times.
759
760 @retval EFI_SUCCESS
761 @retval RETURN_ABORTED
762 **/
763 EFI_STATUS
764 EFIAPI
765 DoShellPrompt (
766 VOID
767 )
768 {
769 UINTN Column;
770 UINTN Row;
771 CHAR16 *CmdLine;
772 CONST CHAR16 *CurDir;
773 UINTN BufferSize;
774 EFI_STATUS Status;
775
776 CurDir = NULL;
777
778 //
779 // Get screen setting to decide size of the command line buffer
780 //
781 gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);
782 BufferSize = Column * Row * sizeof (CHAR16);
783 CmdLine = AllocateZeroPool (BufferSize);
784 if (CmdLine == NULL) {
785 return EFI_OUT_OF_RESOURCES;
786 }
787
788 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
789
790 //
791 // Prompt for input
792 //
793 gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);
794
795 if (CurDir != NULL && StrLen(CurDir) > 1) {
796 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
797 } else {
798 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
799 }
800
801 //
802 // Read a line from the console
803 //
804 Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);
805
806 //
807 // Null terminate the string and parse it
808 //
809 if (!EFI_ERROR (Status)) {
810 CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
811 Status = RunCommand(CmdLine);
812 }
813
814 //
815 // Done with this command
816 //
817 FreePool (CmdLine);
818 return Status;
819 }
820
821 /**
822 Add a buffer to the Buffer To Free List for safely returning buffers to other
823 places without risking letting them modify internal shell information.
824
825 @param Buffer Something to pass to FreePool when the shell is exiting.
826 **/
827 VOID*
828 EFIAPI
829 AddBufferToFreeList(
830 VOID *Buffer
831 )
832 {
833 BUFFER_LIST *BufferListEntry;
834
835 if (Buffer == NULL) {
836 return (NULL);
837 }
838
839 BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST));
840 ASSERT(BufferListEntry != NULL);
841 BufferListEntry->Buffer = Buffer;
842 InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);
843 return (Buffer);
844 }
845
846 /**
847 Add a buffer to the Line History List
848
849 @param Buffer The line buffer to add.
850 **/
851 VOID
852 EFIAPI
853 AddLineToCommandHistory(
854 IN CONST CHAR16 *Buffer
855 )
856 {
857 BUFFER_LIST *Node;
858
859 Node = AllocateZeroPool(sizeof(BUFFER_LIST));
860 ASSERT(Node != NULL);
861 Node->Buffer = AllocateZeroPool(StrSize(Buffer));
862 ASSERT(Node->Buffer != NULL);
863 StrCpy(Node->Buffer, Buffer);
864
865 InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
866 }
867
868 /**
869 Checks if a string is an alias for another command. If yes, then it replaces the alias name
870 with the correct command name.
871
872 @param[in,out] CommandString Upon entry the potential alias. Upon return the
873 command name if it was an alias. If it was not
874 an alias it will be unchanged. This function may
875 change the buffer to fit the command name.
876
877 @retval EFI_SUCCESS The name was changed.
878 @retval EFI_SUCCESS The name was not an alias.
879 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
880 **/
881 EFI_STATUS
882 EFIAPI
883 ShellConvertAlias(
884 IN OUT CHAR16 **CommandString
885 )
886 {
887 CONST CHAR16 *NewString;
888
889 NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);
890 if (NewString == NULL) {
891 return (EFI_SUCCESS);
892 }
893 FreePool(*CommandString);
894 *CommandString = AllocatePool(StrSize(NewString));
895 if (*CommandString == NULL) {
896 return (EFI_OUT_OF_RESOURCES);
897 }
898 StrCpy(*CommandString, NewString);
899 return (EFI_SUCCESS);
900 }
901
902 /**
903 Function allocates a new command line and replaces all instances of environment
904 variable names that are correctly preset to their values.
905
906 If the return value is not NULL the memory must be caller freed.
907
908 @param[in] OriginalCommandLine The original command line
909
910 @retval NULL An error ocurred.
911 @return The new command line with no environment variables present.
912 **/
913 CHAR16*
914 EFIAPI
915 ShellConvertVariables (
916 IN CONST CHAR16 *OriginalCommandLine
917 )
918 {
919 CONST CHAR16 *MasterEnvList;
920 UINTN NewSize;
921 CHAR16 *NewCommandLine1;
922 CHAR16 *NewCommandLine2;
923 CHAR16 *Temp;
924 UINTN ItemSize;
925 CHAR16 *ItemTemp;
926 SCRIPT_FILE *CurrentScriptFile;
927 ALIAS_LIST *AliasListNode;
928
929 ASSERT(OriginalCommandLine != NULL);
930
931 ItemSize = 0;
932 NewSize = StrSize(OriginalCommandLine);
933 CurrentScriptFile = ShellCommandGetCurrentScriptFile();
934 Temp = NULL;
935
936 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
937
938 //
939 // calculate the size required for the post-conversion string...
940 //
941 if (CurrentScriptFile != NULL) {
942 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
943 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
944 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
945 ){
946 for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)
947 ; Temp != NULL
948 ; Temp = StrStr(Temp+1, AliasListNode->Alias)
949 ){
950 //
951 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
952 //
953 if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {
954 NewSize += StrSize(AliasListNode->CommandString);
955 }
956 }
957 }
958 }
959
960 for (MasterEnvList = EfiShellGetEnv(NULL)
961 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
962 ; MasterEnvList += StrLen(MasterEnvList) + 1
963 ){
964 if (StrSize(MasterEnvList) > ItemSize) {
965 ItemSize = StrSize(MasterEnvList);
966 }
967 for (Temp = StrStr(OriginalCommandLine, MasterEnvList)
968 ; Temp != NULL
969 ; Temp = StrStr(Temp+1, MasterEnvList)
970 ){
971 //
972 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
973 //
974 if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&
975 ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {
976 NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));
977 }
978 }
979 }
980
981 //
982 // Quick out if none were found...
983 //
984 if (NewSize == StrSize(OriginalCommandLine)) {
985 ASSERT(Temp == NULL);
986 Temp = StrnCatGrow(&Temp, NULL, OriginalCommandLine, 0);
987 return (Temp);
988 }
989
990 //
991 // now do the replacements...
992 //
993 NewCommandLine1 = AllocateZeroPool(NewSize);
994 NewCommandLine2 = AllocateZeroPool(NewSize);
995 ItemTemp = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));
996 StrCpy(NewCommandLine1, OriginalCommandLine);
997 for (MasterEnvList = EfiShellGetEnv(NULL)
998 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
999 ; MasterEnvList += StrLen(MasterEnvList) + 1
1000 ){
1001 StrCpy(ItemTemp, L"%");
1002 StrCat(ItemTemp, MasterEnvList);
1003 StrCat(ItemTemp, L"%");
1004 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);
1005 StrCpy(NewCommandLine1, NewCommandLine2);
1006 }
1007 if (CurrentScriptFile != NULL) {
1008 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
1009 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1010 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1011 ){
1012 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);
1013 StrCpy(NewCommandLine1, NewCommandLine2);
1014 }
1015 }
1016
1017 FreePool(NewCommandLine2);
1018 FreePool(ItemTemp);
1019
1020 return (NewCommandLine1);
1021 }
1022
1023 /**
1024 Internal function to run a command line with pipe usage.
1025
1026 @param[in] CmdLine The pointer to the command line.
1027 @param[in] StdIn The pointer to the Standard input.
1028 @param[in] StdOut The pointer to the Standard output.
1029
1030 @retval EFI_SUCCESS The split command is executed successfully.
1031 @retval other Some error occurs when executing the split command.
1032 **/
1033 EFI_STATUS
1034 EFIAPI
1035 RunSplitCommand(
1036 IN CONST CHAR16 *CmdLine,
1037 IN SHELL_FILE_HANDLE *StdIn,
1038 IN SHELL_FILE_HANDLE *StdOut
1039 )
1040 {
1041 EFI_STATUS Status;
1042 CHAR16 *NextCommandLine;
1043 CHAR16 *OurCommandLine;
1044 UINTN Size1;
1045 UINTN Size2;
1046 SPLIT_LIST *Split;
1047 SHELL_FILE_HANDLE *TempFileHandle;
1048 BOOLEAN Unicode;
1049
1050 ASSERT(StdOut == NULL);
1051
1052 ASSERT(StrStr(CmdLine, L"|") != NULL);
1053
1054 Status = EFI_SUCCESS;
1055 NextCommandLine = NULL;
1056 OurCommandLine = NULL;
1057 Size1 = 0;
1058 Size2 = 0;
1059
1060 NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);
1061 OurCommandLine = StrnCatGrow(&OurCommandLine , &Size2, CmdLine , StrStr(CmdLine, L"|") - CmdLine);
1062 if (NextCommandLine[0] != CHAR_NULL &&
1063 NextCommandLine[0] == L'a' &&
1064 NextCommandLine[1] == L' '
1065 ){
1066 CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));
1067 Unicode = FALSE;
1068 } else {
1069 Unicode = TRUE;
1070 }
1071
1072
1073 //
1074 // make a SPLIT_LIST item and add to list
1075 //
1076 Split = AllocateZeroPool(sizeof(SPLIT_LIST));
1077 ASSERT(Split != NULL);
1078 Split->SplitStdIn = StdIn;
1079 Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);
1080 ASSERT(Split->SplitStdOut != NULL);
1081 InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);
1082
1083 ASSERT(StrStr(OurCommandLine, L"|") == NULL);
1084 Status = RunCommand(OurCommandLine);
1085
1086 //
1087 // move the output from the first to the in to the second.
1088 //
1089 TempFileHandle = Split->SplitStdOut;
1090 if (Split->SplitStdIn == StdIn) {
1091 Split->SplitStdOut = NULL;
1092 } else {
1093 Split->SplitStdOut = Split->SplitStdIn;
1094 }
1095 Split->SplitStdIn = TempFileHandle;
1096 ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);
1097
1098 if (!EFI_ERROR(Status)) {
1099 Status = RunCommand(NextCommandLine);
1100 }
1101
1102 //
1103 // remove the top level from the ScriptList
1104 //
1105 ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);
1106 RemoveEntryList(&Split->Link);
1107
1108 //
1109 // Note that the original StdIn is now the StdOut...
1110 //
1111 if (Split->SplitStdOut != NULL && Split->SplitStdOut != StdIn) {
1112 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));
1113 }
1114 if (Split->SplitStdIn != NULL) {
1115 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));
1116 }
1117
1118 FreePool(Split);
1119 FreePool(NextCommandLine);
1120 FreePool(OurCommandLine);
1121
1122 return (Status);
1123 }
1124
1125 /**
1126 Function will process and run a command line.
1127
1128 This will determine if the command line represents an internal shell
1129 command or dispatch an external application.
1130
1131 @param[in] CmdLine The command line to parse.
1132
1133 @retval EFI_SUCCESS The command was completed.
1134 @retval EFI_ABORTED The command's operation was aborted.
1135 **/
1136 EFI_STATUS
1137 EFIAPI
1138 RunCommand(
1139 IN CONST CHAR16 *CmdLine
1140 )
1141 {
1142 EFI_STATUS Status;
1143 CHAR16 *CommandName;
1144 SHELL_STATUS ShellStatus;
1145 UINTN Argc;
1146 CHAR16 **Argv;
1147 BOOLEAN LastError;
1148 CHAR16 LeString[11];
1149 CHAR16 *PostAliasCmdLine;
1150 UINTN PostAliasSize;
1151 CHAR16 *PostVariableCmdLine;
1152 CHAR16 *CommandWithPath;
1153 EFI_DEVICE_PATH_PROTOCOL *DevPath;
1154 CONST CHAR16 *TempLocation;
1155 CONST CHAR16 *TempLocation2;
1156 SHELL_FILE_HANDLE OriginalStdIn;
1157 SHELL_FILE_HANDLE OriginalStdOut;
1158 SHELL_FILE_HANDLE OriginalStdErr;
1159 CHAR16 *TempLocation3;
1160 UINTN Count;
1161 UINTN Count2;
1162 CHAR16 *CleanOriginal;
1163 SPLIT_LIST *Split;
1164
1165 ASSERT(CmdLine != NULL);
1166 if (StrLen(CmdLine) == 0) {
1167 return (EFI_SUCCESS);
1168 }
1169
1170 CommandName = NULL;
1171 PostVariableCmdLine = NULL;
1172 PostAliasCmdLine = NULL;
1173 CommandWithPath = NULL;
1174 DevPath = NULL;
1175 Status = EFI_SUCCESS;
1176 CleanOriginal = NULL;
1177 Split = NULL;
1178
1179 CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);
1180 while (CleanOriginal[StrLen(CleanOriginal)-1] == L' ') {
1181 CleanOriginal[StrLen(CleanOriginal)-1] = CHAR_NULL;
1182 }
1183 while (CleanOriginal[0] == L' ') {
1184 CopyMem(CleanOriginal, CleanOriginal+1, StrSize(CleanOriginal) - sizeof(CleanOriginal[0]));
1185 }
1186
1187 CommandName = NULL;
1188 if (StrStr(CleanOriginal, L" ") == NULL){
1189 StrnCatGrow(&CommandName, NULL, CleanOriginal, 0);
1190 } else {
1191 StrnCatGrow(&CommandName, NULL, CleanOriginal, StrStr(CleanOriginal, L" ") - CleanOriginal);
1192 }
1193
1194 ASSERT(PostAliasCmdLine == NULL);
1195 if (!ShellCommandIsCommandOnList(CommandName)) {
1196 //
1197 // Convert via alias
1198 //
1199 Status = ShellConvertAlias(&CommandName);
1200 PostAliasSize = 0;
1201 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, CommandName, 0);
1202 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, StrStr(CleanOriginal, L" "), 0);
1203 ASSERT_EFI_ERROR(Status);
1204 } else {
1205 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, NULL, CleanOriginal, 0);
1206 }
1207
1208 if (CleanOriginal != NULL) {
1209 FreePool(CleanOriginal);
1210 CleanOriginal = NULL;
1211 }
1212
1213 if (CommandName != NULL) {
1214 FreePool(CommandName);
1215 CommandName = NULL;
1216 }
1217
1218 PostVariableCmdLine = ShellConvertVariables(PostAliasCmdLine);
1219
1220 //
1221 // we can now free the modified by alias command line
1222 //
1223 if (PostAliasCmdLine != NULL) {
1224 FreePool(PostAliasCmdLine);
1225 PostAliasCmdLine = NULL;
1226 }
1227
1228 while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {
1229 PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;
1230 }
1231 while (PostVariableCmdLine[0] == L' ') {
1232 CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));
1233 }
1234
1235 //
1236 // We dont do normal processing with a split command line (output from one command input to another)
1237 //
1238 TempLocation3 = NULL;
1239 if (StrStr(PostVariableCmdLine, L"|") != NULL) {
1240 for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) {
1241 if (*TempLocation3 == L'^' && *(TempLocation3+1) == L'|') {
1242 TempLocation3++;
1243 } else if (*TempLocation3 == L'|') {
1244 break;
1245 }
1246 }
1247 }
1248 if (TempLocation3 != NULL && *TempLocation3 != CHAR_NULL) {
1249 //
1250 // are we in an existing split???
1251 //
1252 if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {
1253 Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);
1254 }
1255
1256 if (Split == NULL) {
1257 Status = RunSplitCommand(PostVariableCmdLine, NULL, NULL);
1258 } else {
1259 Status = RunSplitCommand(PostVariableCmdLine, Split->SplitStdIn, Split->SplitStdOut);
1260 }
1261 } else {
1262
1263 //
1264 // If this is a mapped drive change handle that...
1265 //
1266 if (PostVariableCmdLine[(StrLen(PostVariableCmdLine)-1)] == L':' && StrStr(PostVariableCmdLine, L" ") == NULL) {
1267 Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, PostVariableCmdLine);
1268 if (EFI_ERROR(Status)) {
1269 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, PostVariableCmdLine);
1270 }
1271 FreePool(PostVariableCmdLine);
1272 return (Status);
1273 }
1274
1275 ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...
1276 /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...
1277
1278
1279
1280 Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr);
1281 if (EFI_ERROR(Status)) {
1282 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);
1283 } else {
1284 //
1285 // remove the < and/or > from the command line now
1286 //
1287 for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) {
1288 if (*TempLocation3 == L'^') {
1289 if (*(TempLocation3+1) == L'<' || *(TempLocation3+1) == L'>') {
1290 CopyMem(TempLocation3, TempLocation3+1, StrSize(TempLocation3) - sizeof(TempLocation3[0]));
1291 }
1292 } else if (*TempLocation3 == L'>') {
1293 *TempLocation3 = CHAR_NULL;
1294 } else if ((*TempLocation3 == L'1' || *TempLocation3 == L'2')&&(*(TempLocation3+1) == L'>')) {
1295 *TempLocation3 = CHAR_NULL;
1296 }
1297 }
1298
1299 while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {
1300 PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;
1301 }
1302 while (PostVariableCmdLine[0] == L' ') {
1303 CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));
1304 }
1305
1306 //
1307 // get the argc and argv updated for internal commands
1308 //
1309 Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &Argv, &Argc);
1310 ASSERT_EFI_ERROR(Status);
1311
1312 for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) {
1313 if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[Count], L"-?") == ShellInfoObject.NewShellParametersProtocol->Argv[Count]
1314 || (ShellInfoObject.NewShellParametersProtocol->Argv[0][0] == L'?' && ShellInfoObject.NewShellParametersProtocol->Argv[0][1] == CHAR_NULL)
1315 ) {
1316 //
1317 // We need to redo the arguments since a parameter was -?
1318 // move them all down 1 to the end, then up one then replace the first with help
1319 //
1320 FreePool(ShellInfoObject.NewShellParametersProtocol->Argv[Count]);
1321 ShellInfoObject.NewShellParametersProtocol->Argv[Count] = NULL;
1322 for (Count2 = Count ; (Count2 + 1) < ShellInfoObject.NewShellParametersProtocol->Argc ; Count2++) {
1323 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2+1];
1324 }
1325 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = NULL;
1326 for (Count2 = ShellInfoObject.NewShellParametersProtocol->Argc -1 ; Count2 > 0 ; Count2--) {
1327 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2-1];
1328 }
1329 ShellInfoObject.NewShellParametersProtocol->Argv[0] = NULL;
1330 ShellInfoObject.NewShellParametersProtocol->Argv[0] = StrnCatGrow(&ShellInfoObject.NewShellParametersProtocol->Argv[0], NULL, L"help", 0);
1331 break;
1332 }
1333 }
1334
1335 //
1336 // command or file?
1337 //
1338 if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {
1339 //
1340 // Run the command (which was converted if it was an alias)
1341 //
1342 if (!EFI_ERROR(Status)) {
1343 Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError);
1344 ASSERT_EFI_ERROR(Status);
1345 UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%08x", ShellStatus);
1346 DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););
1347 if (LastError) {
1348 InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);
1349 }
1350 //
1351 // Pass thru the exitcode from the app.
1352 //
1353 if (ShellCommandGetExit()) {
1354 Status = ShellStatus;
1355 } else if (ShellStatus != 0 && IsScriptOnlyCommand(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {
1356 Status = EFI_ABORTED;
1357 }
1358 }
1359 } else {
1360 //
1361 // run an external file (or script)
1362 //
1363 if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[0], L":") != NULL) {
1364 ASSERT (CommandWithPath == NULL);
1365 if (ShellIsFile(ShellInfoObject.NewShellParametersProtocol->Argv[0]) == EFI_SUCCESS) {
1366 CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);
1367 }
1368 }
1369 if (CommandWithPath == NULL) {
1370 CommandWithPath = ShellFindFilePathEx(ShellInfoObject.NewShellParametersProtocol->Argv[0], mExecutableExtensions);
1371 }
1372 if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) {
1373 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]);
1374 } else {
1375 //
1376 // Check if it's a NSH (script) file.
1377 //
1378 TempLocation = CommandWithPath+StrLen(CommandWithPath)-4;
1379 TempLocation2 = mScriptExtension;
1380 if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) {
1381 Status = RunScriptFile (CommandWithPath);
1382 } else {
1383 DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);
1384 ASSERT(DevPath != NULL);
1385 Status = InternalShellExecuteDevicePath(
1386 &gImageHandle,
1387 DevPath,
1388 PostVariableCmdLine,
1389 NULL,
1390 NULL
1391 );
1392 }
1393 }
1394 }
1395 CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);
1396
1397 RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);
1398
1399 RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr);
1400 }
1401 if (CommandName != NULL) {
1402 if (ShellCommandGetCurrentScriptFile() != NULL && !IsScriptOnlyCommand(CommandName)) {
1403 //
1404 // if this is NOT a scipt only command return success so the script won't quit.
1405 // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)
1406 //
1407 Status = EFI_SUCCESS;
1408 }
1409 }
1410 }
1411
1412 SHELL_FREE_NON_NULL(CommandName);
1413 SHELL_FREE_NON_NULL(CommandWithPath);
1414 SHELL_FREE_NON_NULL(PostVariableCmdLine);
1415 SHELL_FREE_NON_NULL(DevPath);
1416
1417 return (Status);
1418 }
1419
1420 STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};
1421 /**
1422 Function determins if the CommandName COULD be a valid command. It does not determine whether
1423 this is a valid command. It only checks for invalid characters.
1424
1425 @param[in] CommandName The name to check
1426
1427 @retval TRUE CommandName could be a command name
1428 @retval FALSE CommandName could not be a valid command name
1429 **/
1430 BOOLEAN
1431 EFIAPI
1432 IsValidCommandName(
1433 IN CONST CHAR16 *CommandName
1434 )
1435 {
1436 UINTN Count;
1437 if (CommandName == NULL) {
1438 ASSERT(FALSE);
1439 return (FALSE);
1440 }
1441 for ( Count = 0
1442 ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])
1443 ; Count++
1444 ){
1445 if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {
1446 return (FALSE);
1447 }
1448 }
1449 return (TRUE);
1450 }
1451
1452 /**
1453 Function to process a NSH script file via SHELL_FILE_HANDLE.
1454
1455 @param[in] Handle The handle to the already opened file.
1456 @param[in] Name The name of the script file.
1457
1458 @retval EFI_SUCCESS the script completed sucessfully
1459 **/
1460 EFI_STATUS
1461 EFIAPI
1462 RunScriptFileHandle (
1463 IN SHELL_FILE_HANDLE Handle,
1464 IN CONST CHAR16 *Name
1465 )
1466 {
1467 EFI_STATUS Status;
1468 SCRIPT_FILE *NewScriptFile;
1469 UINTN LoopVar;
1470 CHAR16 *CommandLine;
1471 CHAR16 *CommandLine2;
1472 CHAR16 *CommandLine3;
1473 SCRIPT_COMMAND_LIST *LastCommand;
1474 BOOLEAN Ascii;
1475 BOOLEAN PreScriptEchoState;
1476 CONST CHAR16 *CurDir;
1477 UINTN LineCount;
1478
1479 ASSERT(!ShellCommandGetScriptExit());
1480
1481 PreScriptEchoState = ShellCommandGetEchoState();
1482
1483 NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));
1484 ASSERT(NewScriptFile != NULL);
1485
1486 //
1487 // Set up the name
1488 //
1489 ASSERT(NewScriptFile->ScriptName == NULL);
1490 NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);
1491
1492 //
1493 // Save the parameters (used to replace %0 to %9 later on)
1494 //
1495 NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;
1496 if (NewScriptFile->Argc != 0) {
1497 NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));
1498 ASSERT(NewScriptFile->Argv != NULL);
1499 for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {
1500 ASSERT(NewScriptFile->Argv[LoopVar] == NULL);
1501 NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);
1502 }
1503 } else {
1504 NewScriptFile->Argv = NULL;
1505 }
1506
1507 InitializeListHead(&NewScriptFile->CommandList);
1508 InitializeListHead(&NewScriptFile->SubstList);
1509
1510 //
1511 // Now build the list of all script commands.
1512 //
1513 LineCount = 0;
1514 while(!ShellFileHandleEof(Handle)) {
1515 CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);
1516 LineCount++;
1517 if (CommandLine == NULL || StrLen(CommandLine) == 0) {
1518 continue;
1519 }
1520 NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));
1521 ASSERT(NewScriptFile->CurrentCommand != NULL);
1522
1523 NewScriptFile->CurrentCommand->Cl = CommandLine;
1524 NewScriptFile->CurrentCommand->Data = NULL;
1525 NewScriptFile->CurrentCommand->Line = LineCount;
1526
1527 InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
1528 }
1529
1530 //
1531 // Add this as the topmost script file
1532 //
1533 ShellCommandSetNewScript (NewScriptFile);
1534
1535 //
1536 // Now enumerate through the commands and run each one.
1537 //
1538 CommandLine = AllocatePool(PcdGet16(PcdShellPrintBufferSize));
1539 CommandLine2 = AllocatePool(PcdGet16(PcdShellPrintBufferSize));
1540
1541 for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)
1542 ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)
1543 ; // conditional increment in the body of the loop
1544 ){
1545 StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl);
1546
1547 //
1548 // NULL out comments
1549 //
1550 for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {
1551 if (*CommandLine3 == L'^') {
1552 if (*(CommandLine3+1) == L'#' || *(CommandLine3+1) == L':') {
1553 CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));
1554 }
1555 } else if (*CommandLine3 == L'#') {
1556 *CommandLine3 = CHAR_NULL;
1557 }
1558 }
1559
1560 if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {
1561 //
1562 // Due to variability in starting the find and replace action we need to have both buffers the same.
1563 //
1564 StrCpy(CommandLine, CommandLine2);
1565
1566 //
1567 // Remove the %0 to %9 from the command line (if we have some arguments)
1568 //
1569 if (NewScriptFile->Argv != NULL) {
1570 switch (NewScriptFile->Argc) {
1571 default:
1572 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", NewScriptFile->Argv[9], FALSE, TRUE);
1573 ASSERT_EFI_ERROR(Status);
1574 case 9:
1575 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", NewScriptFile->Argv[8], FALSE, TRUE);
1576 ASSERT_EFI_ERROR(Status);
1577 case 8:
1578 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", NewScriptFile->Argv[7], FALSE, TRUE);
1579 ASSERT_EFI_ERROR(Status);
1580 case 7:
1581 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", NewScriptFile->Argv[6], FALSE, TRUE);
1582 ASSERT_EFI_ERROR(Status);
1583 case 6:
1584 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", NewScriptFile->Argv[5], FALSE, TRUE);
1585 ASSERT_EFI_ERROR(Status);
1586 case 5:
1587 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", NewScriptFile->Argv[4], FALSE, TRUE);
1588 ASSERT_EFI_ERROR(Status);
1589 case 4:
1590 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", NewScriptFile->Argv[3], FALSE, TRUE);
1591 ASSERT_EFI_ERROR(Status);
1592 case 3:
1593 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", NewScriptFile->Argv[2], FALSE, TRUE);
1594 ASSERT_EFI_ERROR(Status);
1595 case 2:
1596 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", NewScriptFile->Argv[1], FALSE, TRUE);
1597 ASSERT_EFI_ERROR(Status);
1598 case 1:
1599 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, TRUE);
1600 ASSERT_EFI_ERROR(Status);
1601 break;
1602 case 0:
1603 break;
1604 }
1605 }
1606 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", L"\"\"", FALSE, FALSE);
1607 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", L"\"\"", FALSE, FALSE);
1608 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", L"\"\"", FALSE, FALSE);
1609 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", L"\"\"", FALSE, FALSE);
1610 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", L"\"\"", FALSE, FALSE);
1611 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", L"\"\"", FALSE, FALSE);
1612 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", L"\"\"", FALSE, FALSE);
1613 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", L"\"\"", FALSE, FALSE);
1614 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", L"\"\"", FALSE, FALSE);
1615
1616 StrCpy(CommandLine2, CommandLine);
1617
1618 LastCommand = NewScriptFile->CurrentCommand;
1619
1620 for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);
1621
1622 if (CommandLine3[0] == L':' ) {
1623 //
1624 // This line is a goto target / label
1625 //
1626 } else {
1627 if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {
1628 if (ShellCommandGetEchoState()) {
1629 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
1630 if (CurDir != NULL && StrLen(CurDir) > 1) {
1631 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
1632 } else {
1633 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
1634 }
1635 ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);
1636 }
1637 Status = RunCommand(CommandLine3);
1638 }
1639
1640 if (ShellCommandGetScriptExit()) {
1641 ShellCommandRegisterExit(FALSE);
1642 Status = EFI_SUCCESS;
1643 break;
1644 }
1645 if (EFI_ERROR(Status)) {
1646 break;
1647 }
1648 if (ShellCommandGetExit()) {
1649 break;
1650 }
1651 }
1652 //
1653 // If that commend did not update the CurrentCommand then we need to advance it...
1654 //
1655 if (LastCommand == NewScriptFile->CurrentCommand) {
1656 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
1657 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
1658 NewScriptFile->CurrentCommand->Reset = TRUE;
1659 }
1660 }
1661 } else {
1662 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
1663 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
1664 NewScriptFile->CurrentCommand->Reset = TRUE;
1665 }
1666 }
1667 }
1668
1669 ShellCommandSetEchoState(PreScriptEchoState);
1670
1671 FreePool(CommandLine);
1672 FreePool(CommandLine2);
1673 ShellCommandSetNewScript (NULL);
1674 return (EFI_SUCCESS);
1675 }
1676
1677 /**
1678 Function to process a NSH script file.
1679
1680 @param[in] ScriptPath Pointer to the script file name (including file system path).
1681
1682 @retval EFI_SUCCESS the script completed sucessfully
1683 **/
1684 EFI_STATUS
1685 EFIAPI
1686 RunScriptFile (
1687 IN CONST CHAR16 *ScriptPath
1688 )
1689 {
1690 EFI_STATUS Status;
1691 SHELL_FILE_HANDLE FileHandle;
1692
1693 if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {
1694 return (EFI_INVALID_PARAMETER);
1695 }
1696
1697 Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);
1698 if (EFI_ERROR(Status)) {
1699 return (Status);
1700 }
1701
1702 Status = RunScriptFileHandle(FileHandle, ScriptPath);
1703
1704 ShellCloseFile(&FileHandle);
1705
1706 return (Status);
1707 }