2 This is THE shell (application)
4 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2013-2014, Hewlett-Packard Development Company, L.P.
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 // Initialize the global structure
21 SHELL_INFO ShellInfoObject
= {
56 {{NULL
, NULL
}, NULL
, NULL
},
57 {{NULL
, NULL
}, NULL
, NULL
},
69 STATIC CONST CHAR16 mScriptExtension
[] = L
".NSH";
70 STATIC CONST CHAR16 mExecutableExtensions
[] = L
".NSH;.EFI";
71 STATIC CONST CHAR16 mStartupScript
[] = L
"startup.nsh";
74 Cleans off leading and trailing spaces and tabs.
76 @param[in] String pointer to the string to trim them off.
84 ASSERT(String
!= NULL
);
85 ASSERT(*String
!= NULL
);
87 // Remove any spaces and tabs at the beginning of the (*String).
89 while (((*String
)[0] == L
' ') || ((*String
)[0] == L
'\t')) {
90 CopyMem((*String
), (*String
)+1, StrSize((*String
)) - sizeof((*String
)[0]));
94 // Remove any spaces and tabs at the end of the (*String).
96 while ((StrLen (*String
) > 0) && (((*String
)[StrLen((*String
))-1] == L
' ') || ((*String
)[StrLen((*String
))-1] == L
'\t'))) {
97 (*String
)[StrLen((*String
))-1] = CHAR_NULL
;
100 return (EFI_SUCCESS
);
104 Find a command line contains a split operation
106 @param[in] CmdLine The command line to parse.
108 @retval A pointer to the | character in CmdLine or NULL if not present.
113 IN CONST CHAR16
*CmdLine
116 CONST CHAR16
*TempSpot
;
118 if (StrStr(CmdLine
, L
"|") != NULL
) {
119 for (TempSpot
= CmdLine
; TempSpot
!= NULL
&& *TempSpot
!= CHAR_NULL
; TempSpot
++) {
120 if (*TempSpot
== L
'^' && *(TempSpot
+1) == L
'|') {
122 } else if (*TempSpot
== L
'|') {
131 Determine if a command line contains a split operation
133 @param[in] CmdLine The command line to parse.
135 @retval TRUE CmdLine has a valid split.
136 @retval FALSE CmdLine does not have a valid split.
141 IN CONST CHAR16
*CmdLine
144 CONST CHAR16
*TempSpot
;
145 TempSpot
= FindSplit(CmdLine
);
146 return (BOOLEAN
) ((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
));
150 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
151 feature's enabled state was not known when the shell initially launched.
153 @retval EFI_SUCCESS The feature is enabled.
154 @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.
158 InternalEfiShellStartCtrlSMonitor(
162 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
*SimpleEx
;
163 EFI_KEY_DATA KeyData
;
166 Status
= gBS
->OpenProtocol(
167 gST
->ConsoleInHandle
,
168 &gEfiSimpleTextInputExProtocolGuid
,
172 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
173 if (EFI_ERROR(Status
)) {
178 STRING_TOKEN (STR_SHELL_NO_IN_EX
),
179 ShellInfoObject
.HiiHandle
);
180 return (EFI_SUCCESS
);
183 KeyData
.KeyState
.KeyToggleState
= 0;
184 KeyData
.Key
.ScanCode
= 0;
185 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
186 KeyData
.Key
.UnicodeChar
= L
's';
188 Status
= SimpleEx
->RegisterKeyNotify(
191 NotificationFunction
,
192 &ShellInfoObject
.CtrlSNotifyHandle1
);
194 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
195 if (!EFI_ERROR(Status
)) {
196 Status
= SimpleEx
->RegisterKeyNotify(
199 NotificationFunction
,
200 &ShellInfoObject
.CtrlSNotifyHandle2
);
202 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
203 KeyData
.Key
.UnicodeChar
= 19;
205 if (!EFI_ERROR(Status
)) {
206 Status
= SimpleEx
->RegisterKeyNotify(
209 NotificationFunction
,
210 &ShellInfoObject
.CtrlSNotifyHandle3
);
212 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
213 if (!EFI_ERROR(Status
)) {
214 Status
= SimpleEx
->RegisterKeyNotify(
217 NotificationFunction
,
218 &ShellInfoObject
.CtrlSNotifyHandle4
);
226 The entry point for the application.
228 @param[in] ImageHandle The firmware allocated handle for the EFI image.
229 @param[in] SystemTable A pointer to the EFI System Table.
231 @retval EFI_SUCCESS The entry point is executed successfully.
232 @retval other Some error occurs when executing this entry point.
238 IN EFI_HANDLE ImageHandle
,
239 IN EFI_SYSTEM_TABLE
*SystemTable
245 EFI_HANDLE ConInHandle
;
246 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*OldConIn
;
249 SHELL_STATUS ExitStatus
;
251 if (PcdGet8(PcdShellSupportLevel
) > 3) {
252 return (EFI_UNSUPPORTED
);
258 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
259 if (EFI_ERROR(Status
)) {
264 // Populate the global structure from PCDs
266 ShellInfoObject
.ImageDevPath
= NULL
;
267 ShellInfoObject
.FileDevPath
= NULL
;
268 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
269 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
270 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
273 // verify we dont allow for spec violation
275 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
278 // Initialize the LIST ENTRY objects...
280 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
281 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
282 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
285 // Check PCDs for optional features that are not implemented yet.
287 if ( PcdGetBool(PcdShellSupportOldProtocols
)
288 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
289 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
291 return (EFI_UNSUPPORTED
);
295 // turn off the watchdog timer
297 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
300 // install our console logger. This will keep a log of the output for back-browsing
302 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
303 if(EFI_ERROR (Status
)) {
304 ExitStatus
= (SHELL_STATUS
) (Status
& (~MAX_BIT
));
306 ExitStatus
= SHELL_SUCCESS
;
309 if (!EFI_ERROR(Status
)) {
311 // Enable the cursor to be visible
313 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
316 // If supporting EFI 1.1 we need to install HII protocol
317 // only do this if PcdShellRequireHiiPlatform == FALSE
319 // remove EFI_UNSUPPORTED check above when complete.
320 ///@todo add support for Framework HII
323 // install our (solitary) HII package
325 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
326 if (ShellInfoObject
.HiiHandle
== NULL
) {
327 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
328 ///@todo Add our package into Framework HII
330 if (ShellInfoObject
.HiiHandle
== NULL
) {
331 Status
= EFI_NOT_STARTED
;
337 // create and install the EfiShellParametersProtocol
339 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
340 ASSERT_EFI_ERROR(Status
);
341 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
344 // create and install the EfiShellProtocol
346 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
347 ASSERT_EFI_ERROR(Status
);
348 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
351 // Now initialize the shell library (it requires Shell Parameters protocol)
353 Status
= ShellInitialize();
354 ASSERT_EFI_ERROR(Status
);
356 Status
= CommandInit();
357 ASSERT_EFI_ERROR(Status
);
360 // Check the command line
362 Status
= ProcessCommandLine ();
363 if (EFI_ERROR (Status
)) {
368 // If shell support level is >= 1 create the mappings and paths
370 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
371 Status
= ShellCommandCreateInitialMappingsAndPaths();
375 // save the device path for the loaded image and the device path for the filepath (under loaded image)
376 // These are where to look for the startup.nsh file
378 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
379 ASSERT_EFI_ERROR(Status
);
382 // Display the version
384 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
387 gST
->ConOut
->Mode
->CursorRow
,
389 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL
),
390 ShellInfoObject
.HiiHandle
,
391 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
392 gEfiShellProtocol
->MajorVersion
,
393 gEfiShellProtocol
->MinorVersion
400 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER
),
401 ShellInfoObject
.HiiHandle
,
402 (CHAR16
*) PcdGetPtr (PcdShellSupplier
)
409 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI
),
410 ShellInfoObject
.HiiHandle
,
411 (gST
->Hdr
.Revision
&0xffff0000)>>16,
412 (gST
->Hdr
.Revision
&0x0000ffff),
414 gST
->FirmwareRevision
419 // Display the mapping
421 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
422 Status
= RunCommand(L
"map", NULL
);
423 ASSERT_EFI_ERROR(Status
);
427 // init all the built in alias'
429 Status
= SetBuiltInAlias();
430 ASSERT_EFI_ERROR(Status
);
433 // Initialize environment variables
435 if (ShellCommandGetProfileList() != NULL
) {
436 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
437 ASSERT_EFI_ERROR(Status
);
441 TempString
= AllocateZeroPool(Size
);
443 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
444 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
445 ASSERT_EFI_ERROR(Status
);
447 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
448 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
449 ASSERT_EFI_ERROR(Status
);
451 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
452 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
453 ASSERT_EFI_ERROR(Status
);
455 FreePool(TempString
);
457 if (!EFI_ERROR(Status
)) {
458 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
460 // Set up the event for CTRL-C monitoring...
462 Status
= InernalEfiShellStartMonitor();
465 if (!EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
467 // Set up the event for CTRL-S monitoring...
469 Status
= InternalEfiShellStartCtrlSMonitor();
472 if (!EFI_ERROR(Status
) && ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
474 // close off the gST->ConIn
476 OldConIn
= gST
->ConIn
;
477 ConInHandle
= gST
->ConsoleInHandle
;
478 gST
->ConIn
= CreateSimpleTextInOnFile((SHELL_FILE_HANDLE
)&FileInterfaceNulFile
, &gST
->ConsoleInHandle
);
484 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
486 // process the startup script or launch the called app.
488 Status
= DoStartupScript(
489 ShellInfoObject
.ImageDevPath
,
490 ShellInfoObject
.FileDevPath
,
495 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
&& !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
497 // begin the UI waiting loop
501 // clean out all the memory allocated for CONST <something> * return values
502 // between each shell prompt presentation
504 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
505 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
509 // Reset page break back to default.
511 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
512 ASSERT (ShellInfoObject
.ConsoleInfo
!= NULL
);
513 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
514 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
517 // Reset the CTRL-C event (yes we ignore the return values)
519 Status
= gBS
->CheckEvent (ShellInfoObject
.NewEfiShellProtocol
->ExecutionBreak
);
524 Status
= DoShellPrompt();
525 } while (!ShellCommandGetExit());
526 ExitStatus
= (SHELL_STATUS
) ShellCommandGetExitCode();
528 if (OldConIn
!= NULL
&& ConInHandle
!= NULL
) {
529 CloseSimpleTextInOnFile (gST
->ConIn
);
530 gST
->ConIn
= OldConIn
;
531 gST
->ConsoleInHandle
= ConInHandle
;
538 // uninstall protocols / free memory / etc...
540 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
541 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
542 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
544 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
545 FreePool(ShellInfoObject
.ImageDevPath
);
546 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
548 if (ShellInfoObject
.FileDevPath
!= NULL
) {
549 FreePool(ShellInfoObject
.FileDevPath
);
550 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
552 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
553 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
554 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
556 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
557 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
558 InternalEfiShellSetEnv(L
"cwd", NULL
, TRUE
);
560 CleanUpShellProtocol(ShellInfoObject
.NewEfiShellProtocol
);
561 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
564 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
565 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
568 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
569 ASSERT(FALSE
); ///@todo finish this de-allocation.
572 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
573 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
574 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
577 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
578 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
579 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
582 if (ShellInfoObject
.HiiHandle
!= NULL
) {
583 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
584 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
587 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
588 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
591 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
592 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
593 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
594 FreePool(ShellInfoObject
.ConsoleInfo
);
595 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
598 if (!EFI_ERROR (Status
)) {
599 // If the command exited with an error, we pass this error out in the ExitData
600 // so that it can be retrieved by the EfiShellExecute function (which may
601 // start the shell with gBS->StartImage)
602 if (ExitStatus
!= SHELL_SUCCESS
) {
603 // Allocate a buffer for exit data to pass to gBS->Exit().
604 // This buffer will contain the empty string immediately followed by
605 // the shell's exit status. (The empty string is required by the UEFI spec)
606 ExitDataSize
= (sizeof (CHAR16
) + sizeof (SHELL_STATUS
));
607 ExitData
= AllocatePool (ExitDataSize
);
608 if (ExitData
== NULL
) {
609 return EFI_OUT_OF_RESOURCES
;
612 // Use CopyMem to avoid alignment faults
613 CopyMem ((ExitData
+ 1), &ExitStatus
, sizeof (ExitStatus
));
615 gBS
->Exit (ImageHandle
, EFI_ABORTED
, ExitDataSize
, ExitData
);
628 Sets all the alias' that were registered with the ShellCommandLib library.
630 @retval EFI_SUCCESS all init commands were run sucessfully.
638 CONST ALIAS_LIST
*List
;
642 // Get all the commands we want to run
644 List
= ShellCommandGetInitAliasList();
647 // for each command in the List
649 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
650 ; !IsNull (&List
->Link
, &Node
->Link
)
651 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
654 // install the alias'
656 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
657 ASSERT_EFI_ERROR(Status
);
659 return (EFI_SUCCESS
);
663 Internal function to determine if 2 command names are really the same.
665 @param[in] Command1 The pointer to the first command name.
666 @param[in] Command2 The pointer to the second command name.
668 @retval TRUE The 2 command names are the same.
669 @retval FALSE The 2 command names are not the same.
674 IN CONST CHAR16
*Command1
,
675 IN CONST CHAR16
*Command2
678 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
685 Internal function to determine if a command is a script only command.
687 @param[in] CommandName The pointer to the command name.
689 @retval TRUE The command is a script only command.
690 @retval FALSE The command is not a script only command.
695 IN CONST CHAR16
*CommandName
698 if (IsCommand(CommandName
, L
"for")
699 ||IsCommand(CommandName
, L
"endfor")
700 ||IsCommand(CommandName
, L
"if")
701 ||IsCommand(CommandName
, L
"else")
702 ||IsCommand(CommandName
, L
"endif")
703 ||IsCommand(CommandName
, L
"goto")) {
710 This function will populate the 2 device path protocol parameters based on the
711 global gImageHandle. The DevPath will point to the device path for the handle that has
712 loaded image protocol installed on it. The FilePath will point to the device path
713 for the file that was loaded.
715 @param[in, out] DevPath On a sucessful return the device path to the loaded image.
716 @param[in, out] FilePath On a sucessful return the device path to the file.
718 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
719 @retval other A error from gBS->HandleProtocol.
725 GetDevicePathsForImageAndFile (
726 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
727 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
731 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
732 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
734 ASSERT(DevPath
!= NULL
);
735 ASSERT(FilePath
!= NULL
);
737 Status
= gBS
->OpenProtocol (
739 &gEfiLoadedImageProtocolGuid
,
740 (VOID
**)&LoadedImage
,
743 EFI_OPEN_PROTOCOL_GET_PROTOCOL
745 if (!EFI_ERROR (Status
)) {
746 Status
= gBS
->OpenProtocol (
747 LoadedImage
->DeviceHandle
,
748 &gEfiDevicePathProtocolGuid
,
749 (VOID
**)&ImageDevicePath
,
752 EFI_OPEN_PROTOCOL_GET_PROTOCOL
754 if (!EFI_ERROR (Status
)) {
755 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
756 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
758 LoadedImage
->DeviceHandle
,
759 &gEfiDevicePathProtocolGuid
,
765 &gEfiLoadedImageProtocolGuid
,
773 Process all Uefi Shell 2.0 command line options.
775 see Uefi Shell 2.0 section 3.2 for full details.
777 the command line must resemble the following:
779 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
781 ShellOpt-options Options which control the initialization behavior of the shell.
782 These options are read from the EFI global variable "ShellOpt"
783 and are processed before options or file-name.
785 options Options which control the initialization behavior of the shell.
787 file-name The name of a UEFI shell application or script to be executed
788 after initialization is complete. By default, if file-name is
789 specified, then -nostartup is implied. Scripts are not supported
792 file-name-options The command-line options that are passed to file-name when it
795 This will initialize the ShellInfoObject.ShellInitSettings global variable.
797 @retval EFI_SUCCESS The variable is initialized.
808 CHAR16
*DelayValueStr
;
811 EFI_UNICODE_COLLATION_PROTOCOL
*UnicodeCollation
;
813 // `file-name-options` will contain arguments to `file-name` that we don't
814 // know about. This would cause ShellCommandLineParse to error, so we parse
815 // arguments manually, ignoring those after the first thing that doesn't look
816 // like a shell option (which is assumed to be `file-name`).
818 Status
= gBS
->LocateProtocol (
819 &gEfiUnicodeCollationProtocolGuid
,
821 (VOID
**) &UnicodeCollation
823 if (EFI_ERROR (Status
)) {
827 // Set default options
828 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= FALSE
;
829 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= FALSE
;
830 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= FALSE
;
831 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= FALSE
;
832 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= FALSE
;
833 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= FALSE
;
834 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= FALSE
;
835 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= FALSE
;
836 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= FALSE
;
837 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
840 // Start LoopVar at 0 to parse only optional arguments at Argv[0]
841 // and parse other parameters from Argv[1]. This is for use case that
842 // UEFI Shell boot option is created, and OptionalData is provided
843 // that starts with shell command-line options.
845 for (LoopVar
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
846 CurrentArg
= gEfiShellParametersProtocol
->Argv
[LoopVar
];
847 if (UnicodeCollation
->StriColl (
852 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= TRUE
;
854 else if (UnicodeCollation
->StriColl (
859 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= TRUE
;
861 else if (UnicodeCollation
->StriColl (
866 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= TRUE
;
868 else if (UnicodeCollation
->StriColl (
873 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= TRUE
;
875 else if (UnicodeCollation
->StriColl (
880 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= TRUE
;
882 else if (UnicodeCollation
->StriColl (
887 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= TRUE
;
889 else if (UnicodeCollation
->StriColl (
894 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= TRUE
;
896 else if (UnicodeCollation
->StriColl (
901 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= TRUE
;
902 // Check for optional delay value following "-delay"
903 DelayValueStr
= gEfiShellParametersProtocol
->Argv
[LoopVar
+ 1];
904 if (DelayValueStr
!= NULL
){
905 if (*DelayValueStr
== L
':') {
908 if (!EFI_ERROR(ShellConvertStringToUint64 (
914 ShellInfoObject
.ShellInitSettings
.Delay
= (UINTN
)DelayValue
;
918 } else if (UnicodeCollation
->StriColl (
923 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= TRUE
;
924 } else if (StrnCmp (L
"-", CurrentArg
, 1) == 0) {
925 // Unrecognised option
926 ShellPrintHiiEx(-1, -1, NULL
,
927 STRING_TOKEN (STR_GEN_PROBLEM
),
928 ShellInfoObject
.HiiHandle
,
931 return EFI_INVALID_PARAMETER
;
934 // First argument should be Shell.efi image name
940 ShellInfoObject
.ShellInitSettings
.FileName
= AllocateCopyPool(StrSize(CurrentArg
), CurrentArg
);
941 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
942 return (EFI_OUT_OF_RESOURCES
);
945 // We found `file-name`.
947 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
950 // Add `file-name-options`
951 for (Size
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
952 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
953 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
957 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
958 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
959 return (EFI_OUT_OF_RESOURCES
);
961 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
963 gEfiShellParametersProtocol
->Argv
[LoopVar
],
965 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
966 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
967 return (EFI_OUT_OF_RESOURCES
);
973 // "-nointerrupt" overrides "-delay"
974 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
975 ShellInfoObject
.ShellInitSettings
.Delay
= 0;
982 Handles all interaction with the default startup script.
984 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
986 @param ImagePath the path to the image for shell. first place to look for the startup script
987 @param FilePath the path to the file for shell. second place to look for the startup script.
989 @param[out] ExitStatus The exit code of the script. Ignored if NULL.
991 @retval EFI_SUCCESS the variable is initialized.
996 IN EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
997 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
,
998 OUT SHELL_STATUS
*ExitStatus
1004 SHELL_FILE_HANDLE FileHandle
;
1005 EFI_DEVICE_PATH_PROTOCOL
*NewPath
;
1006 EFI_DEVICE_PATH_PROTOCOL
*NamePath
;
1007 CHAR16
*FileStringPath
;
1010 CONST CHAR16
*MapName
;
1012 Key
.UnicodeChar
= CHAR_NULL
;
1016 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
1018 // launch something else instead
1020 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
1021 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1022 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
1024 FileStringPath
= AllocateZeroPool(NewSize
);
1025 if (FileStringPath
== NULL
) {
1026 return (EFI_OUT_OF_RESOURCES
);
1028 StrnCpy(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileName
, NewSize
/sizeof(CHAR16
) -1);
1029 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1030 StrnCat(FileStringPath
, L
" ", NewSize
/sizeof(CHAR16
) - StrLen(FileStringPath
) -1);
1031 StrnCat(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileOptions
, NewSize
/sizeof(CHAR16
) - StrLen(FileStringPath
) -1);
1033 Status
= RunCommand(FileStringPath
, ExitStatus
);
1034 FreePool(FileStringPath
);
1040 // for shell level 0 we do no scripts
1041 // Without the Startup bit overriding we allow for nostartup to prevent scripts
1043 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
1044 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
1046 return (EFI_SUCCESS
);
1049 gST
->ConOut
->EnableCursor(gST
->ConOut
, FALSE
);
1051 // print out our warning and see if they press a key
1053 for ( Status
= EFI_UNSUPPORTED
, Delay
= ShellInfoObject
.ShellInitSettings
.Delay
1054 ; Delay
!= 0 && EFI_ERROR(Status
)
1057 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
);
1058 gBS
->Stall (1000000);
1059 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
1060 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
1063 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
1064 gST
->ConOut
->EnableCursor(gST
->ConOut
, TRUE
);
1069 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
1070 return (EFI_SUCCESS
);
1074 // Try the first location (must be file system)
1076 MapName
= ShellInfoObject
.NewEfiShellProtocol
->GetMapFromDevicePath(&ImagePath
);
1077 if (MapName
!= NULL
) {
1078 FileStringPath
= NULL
;
1080 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, MapName
, 0);
1081 if (FileStringPath
== NULL
) {
1082 Status
= EFI_OUT_OF_RESOURCES
;
1084 TempSpot
= StrStr(FileStringPath
, L
";");
1085 if (TempSpot
!= NULL
) {
1086 *TempSpot
= CHAR_NULL
;
1088 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, ((FILEPATH_DEVICE_PATH
*)FilePath
)->PathName
, 0);
1089 PathRemoveLastItem(FileStringPath
);
1090 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, mStartupScript
, 0);
1091 Status
= ShellInfoObject
.NewEfiShellProtocol
->OpenFileByName(FileStringPath
, &FileHandle
, EFI_FILE_MODE_READ
);
1092 FreePool(FileStringPath
);
1095 if (EFI_ERROR(Status
)) {
1096 NamePath
= FileDevicePath (NULL
, mStartupScript
);
1097 NewPath
= AppendDevicePathNode (ImagePath
, NamePath
);
1103 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
1107 // If we got a file, run it
1109 if (!EFI_ERROR(Status
) && FileHandle
!= NULL
) {
1110 Status
= RunScriptFile (
1114 ShellInfoObject
.NewShellParametersProtocol
,
1117 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
1119 FileStringPath
= ShellFindFilePath(mStartupScript
);
1120 if (FileStringPath
== NULL
) {
1122 // we return success since we dont need to have a startup script
1124 Status
= EFI_SUCCESS
;
1125 ASSERT(FileHandle
== NULL
);
1127 Status
= RunScriptFile(
1131 ShellInfoObject
.NewShellParametersProtocol
,
1134 FreePool(FileStringPath
);
1143 Function to perform the shell prompt looping. It will do a single prompt,
1144 dispatch the result, and then return. It is expected that the caller will
1145 call this function in a loop many times.
1148 @retval RETURN_ABORTED
1159 CONST CHAR16
*CurDir
;
1166 // Get screen setting to decide size of the command line buffer
1168 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
1169 BufferSize
= Column
* Row
* sizeof (CHAR16
);
1170 CmdLine
= AllocateZeroPool (BufferSize
);
1171 if (CmdLine
== NULL
) {
1172 return EFI_OUT_OF_RESOURCES
;
1175 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1180 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
1182 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1183 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1185 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1189 // Read a line from the console
1191 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
1194 // Null terminate the string and parse it
1196 if (!EFI_ERROR (Status
)) {
1197 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
1198 Status
= RunCommand(CmdLine
, NULL
);
1202 // Done with this command
1209 Add a buffer to the Buffer To Free List for safely returning buffers to other
1210 places without risking letting them modify internal shell information.
1212 @param Buffer Something to pass to FreePool when the shell is exiting.
1216 AddBufferToFreeList(
1220 BUFFER_LIST
*BufferListEntry
;
1222 if (Buffer
== NULL
) {
1226 BufferListEntry
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1227 ASSERT(BufferListEntry
!= NULL
);
1228 BufferListEntry
->Buffer
= Buffer
;
1229 InsertTailList(&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
1234 Add a buffer to the Line History List
1236 @param Buffer The line buffer to add.
1240 AddLineToCommandHistory(
1241 IN CONST CHAR16
*Buffer
1246 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1247 ASSERT(Node
!= NULL
);
1248 Node
->Buffer
= AllocateCopyPool(StrSize(Buffer
), Buffer
);
1249 ASSERT(Node
->Buffer
!= NULL
);
1251 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1255 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1256 with the correct command name.
1258 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1259 command name if it was an alias. If it was not
1260 an alias it will be unchanged. This function may
1261 change the buffer to fit the command name.
1263 @retval EFI_SUCCESS The name was changed.
1264 @retval EFI_SUCCESS The name was not an alias.
1265 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1270 IN OUT CHAR16
**CommandString
1273 CONST CHAR16
*NewString
;
1275 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
1276 if (NewString
== NULL
) {
1277 return (EFI_SUCCESS
);
1279 FreePool(*CommandString
);
1280 *CommandString
= AllocateCopyPool(StrSize(NewString
), NewString
);
1281 if (*CommandString
== NULL
) {
1282 return (EFI_OUT_OF_RESOURCES
);
1284 return (EFI_SUCCESS
);
1288 Parse for the next instance of one string within another string. Can optionally make sure that
1289 the string was not escaped (^ character) per the shell specification.
1291 @param[in] SourceString The string to search within
1292 @param[in] FindString The string to look for
1293 @param[in] CheckForEscapeCharacter TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances
1298 IN CONST CHAR16
*SourceString
,
1299 IN CONST CHAR16
*FindString
,
1300 IN CONST BOOLEAN CheckForEscapeCharacter
1304 if (SourceString
== NULL
) {
1307 Temp
= StrStr(SourceString
, FindString
);
1310 // If nothing found, or we dont care about escape characters
1312 if (Temp
== NULL
|| !CheckForEscapeCharacter
) {
1317 // If we found an escaped character, try again on the remainder of the string
1319 if ((Temp
> (SourceString
)) && *(Temp
-1) == L
'^') {
1320 return FindNextInstance(Temp
+1, FindString
, CheckForEscapeCharacter
);
1324 // we found the right character
1330 This function will eliminate unreplaced (and therefore non-found) environment variables.
1332 @param[in,out] CmdLine The command line to update.
1336 StripUnreplacedEnvironmentVariables(
1337 IN OUT CHAR16
*CmdLine
1340 CHAR16
*FirstPercent
;
1342 CHAR16
*SecondPercent
;
1343 CHAR16
*SecondQuote
;
1344 CHAR16
*CurrentLocator
;
1346 for (CurrentLocator
= CmdLine
; CurrentLocator
!= NULL
; ) {
1347 FirstQuote
= FindNextInstance(CurrentLocator
, L
"\"", TRUE
);
1348 FirstPercent
= FindNextInstance(CurrentLocator
, L
"%", TRUE
);
1349 SecondPercent
= FirstPercent
!=NULL
?FindNextInstance(FirstPercent
+1, L
"%", TRUE
):NULL
;
1350 if (FirstPercent
== NULL
|| SecondPercent
== NULL
) {
1352 // If we ever dont have 2 % we are done.
1357 if (FirstQuote
< FirstPercent
) {
1358 SecondQuote
= FirstQuote
!= NULL
?FindNextInstance(FirstQuote
+1, L
"\"", TRUE
):NULL
;
1360 // Quote is first found
1363 if (SecondQuote
< FirstPercent
) {
1365 // restart after the pair of "
1367 CurrentLocator
= SecondQuote
+ 1;
1368 } else /* FirstPercent < SecondQuote */{
1370 // Restart on the first percent
1372 CurrentLocator
= FirstPercent
;
1376 ASSERT(FirstPercent
< FirstQuote
);
1377 if (SecondPercent
< FirstQuote
) {
1378 FirstPercent
[0] = L
'\"';
1379 SecondPercent
[0] = L
'\"';
1382 // We need to remove from FirstPercent to SecondPercent
1384 CopyMem(FirstPercent
+ 1, SecondPercent
, StrSize(SecondPercent
));
1385 CurrentLocator
= FirstPercent
+ 2;
1388 ASSERT(FirstQuote
< SecondPercent
);
1389 CurrentLocator
= FirstQuote
;
1391 return (EFI_SUCCESS
);
1395 Function allocates a new command line and replaces all instances of environment
1396 variable names that are correctly preset to their values.
1398 If the return value is not NULL the memory must be caller freed.
1400 @param[in] OriginalCommandLine The original command line
1402 @retval NULL An error ocurred.
1403 @return The new command line with no environment variables present.
1407 ShellConvertVariables (
1408 IN CONST CHAR16
*OriginalCommandLine
1411 CONST CHAR16
*MasterEnvList
;
1413 CHAR16
*NewCommandLine1
;
1414 CHAR16
*NewCommandLine2
;
1418 SCRIPT_FILE
*CurrentScriptFile
;
1419 ALIAS_LIST
*AliasListNode
;
1421 ASSERT(OriginalCommandLine
!= NULL
);
1424 NewSize
= StrSize(OriginalCommandLine
);
1425 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
1428 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1431 // calculate the size required for the post-conversion string...
1433 if (CurrentScriptFile
!= NULL
) {
1434 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1435 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1436 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1438 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
1440 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
1443 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
1445 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
1446 NewSize
+= StrSize(AliasListNode
->CommandString
);
1452 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1453 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1454 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1456 if (StrSize(MasterEnvList
) > ItemSize
) {
1457 ItemSize
= StrSize(MasterEnvList
);
1459 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
1461 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
1464 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
1466 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
1467 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
1468 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
1474 // now do the replacements...
1476 NewCommandLine1
= AllocateCopyPool(NewSize
, OriginalCommandLine
);
1477 NewCommandLine2
= AllocateZeroPool(NewSize
);
1478 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
1479 if (NewCommandLine1
== NULL
|| NewCommandLine2
== NULL
|| ItemTemp
== NULL
) {
1480 SHELL_FREE_NON_NULL(NewCommandLine1
);
1481 SHELL_FREE_NON_NULL(NewCommandLine2
);
1482 SHELL_FREE_NON_NULL(ItemTemp
);
1485 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1486 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
1487 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1490 StrnCat(ItemTemp
, MasterEnvList
, ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
))-1 - StrLen(ItemTemp
));
1491 StrnCat(ItemTemp
, L
"%", ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
))-1 - StrLen(ItemTemp
));
1492 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1493 StrnCpy(NewCommandLine1
, NewCommandLine2
, NewSize
/sizeof(CHAR16
)-1);
1495 if (CurrentScriptFile
!= NULL
) {
1496 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1497 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1498 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1500 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1501 StrnCpy(NewCommandLine1
, NewCommandLine2
, NewSize
/sizeof(CHAR16
)-1);
1505 // Remove non-existant environment variables in scripts only
1507 StripUnreplacedEnvironmentVariables(NewCommandLine1
);
1511 // Now cleanup any straggler intentionally ignored "%" characters
1513 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, L
"^%", L
"%", TRUE
, FALSE
);
1514 StrnCpy(NewCommandLine1
, NewCommandLine2
, NewSize
/sizeof(CHAR16
)-1);
1516 FreePool(NewCommandLine2
);
1519 return (NewCommandLine1
);
1523 Internal function to run a command line with pipe usage.
1525 @param[in] CmdLine The pointer to the command line.
1526 @param[in] StdIn The pointer to the Standard input.
1527 @param[in] StdOut The pointer to the Standard output.
1529 @param[out] ExitStatus The exit code of the last command in the pipeline.
1532 @retval EFI_SUCCESS The split command is executed successfully.
1533 @retval other Some error occurs when executing the split command.
1538 IN CONST CHAR16
*CmdLine
,
1539 IN SHELL_FILE_HANDLE
*StdIn
,
1540 IN SHELL_FILE_HANDLE
*StdOut
,
1541 OUT SHELL_STATUS
*ExitStatus
1545 CHAR16
*NextCommandLine
;
1546 CHAR16
*OurCommandLine
;
1550 SHELL_FILE_HANDLE
*TempFileHandle
;
1553 ASSERT(StdOut
== NULL
);
1555 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1557 Status
= EFI_SUCCESS
;
1558 NextCommandLine
= NULL
;
1559 OurCommandLine
= NULL
;
1563 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1564 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1566 if (NextCommandLine
== NULL
|| OurCommandLine
== NULL
) {
1567 SHELL_FREE_NON_NULL(OurCommandLine
);
1568 SHELL_FREE_NON_NULL(NextCommandLine
);
1569 return (EFI_OUT_OF_RESOURCES
);
1570 } else if (StrStr(OurCommandLine
, L
"|") != NULL
|| Size1
== 0 || Size2
== 0) {
1571 SHELL_FREE_NON_NULL(OurCommandLine
);
1572 SHELL_FREE_NON_NULL(NextCommandLine
);
1573 return (EFI_INVALID_PARAMETER
);
1574 } else if (NextCommandLine
[0] != CHAR_NULL
&&
1575 NextCommandLine
[0] == L
'a' &&
1576 NextCommandLine
[1] == L
' '
1578 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1586 // make a SPLIT_LIST item and add to list
1588 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1589 ASSERT(Split
!= NULL
);
1590 Split
->SplitStdIn
= StdIn
;
1591 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1592 ASSERT(Split
->SplitStdOut
!= NULL
);
1593 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1595 Status
= RunCommand(OurCommandLine
, NULL
);
1598 // move the output from the first to the in to the second.
1600 TempFileHandle
= Split
->SplitStdOut
;
1601 if (Split
->SplitStdIn
== StdIn
) {
1602 Split
->SplitStdOut
= NULL
;
1604 Split
->SplitStdOut
= Split
->SplitStdIn
;
1606 Split
->SplitStdIn
= TempFileHandle
;
1607 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
), 0);
1609 if (!EFI_ERROR(Status
)) {
1610 Status
= RunCommand(NextCommandLine
, ExitStatus
);
1614 // remove the top level from the ScriptList
1616 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1617 RemoveEntryList(&Split
->Link
);
1620 // Note that the original StdIn is now the StdOut...
1622 if (Split
->SplitStdOut
!= NULL
&& Split
->SplitStdOut
!= StdIn
) {
1623 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdOut
));
1625 if (Split
->SplitStdIn
!= NULL
) {
1626 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
));
1630 FreePool(NextCommandLine
);
1631 FreePool(OurCommandLine
);
1637 Take the original command line, substitute any variables, free
1638 the original string, return the modified copy.
1640 @param[in] CmdLine pointer to the command line to update.
1642 @retval EFI_SUCCESS the function was successful.
1643 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1647 ShellSubstituteVariables(
1652 NewCmdLine
= ShellConvertVariables(*CmdLine
);
1653 SHELL_FREE_NON_NULL(*CmdLine
);
1654 if (NewCmdLine
== NULL
) {
1655 return (EFI_OUT_OF_RESOURCES
);
1657 *CmdLine
= NewCmdLine
;
1658 return (EFI_SUCCESS
);
1662 Take the original command line, substitute any alias in the first group of space delimited characters, free
1663 the original string, return the modified copy.
1665 @param[in] CmdLine pointer to the command line to update.
1667 @retval EFI_SUCCESS the function was successful.
1668 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1672 ShellSubstituteAliases(
1677 CHAR16
*CommandName
;
1679 UINTN PostAliasSize
;
1680 ASSERT(CmdLine
!= NULL
);
1681 ASSERT(*CmdLine
!= NULL
);
1685 if (StrStr((*CmdLine
), L
" ") == NULL
){
1686 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), 0);
1688 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), StrStr((*CmdLine
), L
" ") - (*CmdLine
));
1692 // This cannot happen 'inline' since the CmdLine can need extra space.
1695 if (!ShellCommandIsCommandOnList(CommandName
)) {
1697 // Convert via alias
1699 Status
= ShellConvertAlias(&CommandName
);
1700 if (EFI_ERROR(Status
)){
1704 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, CommandName
, 0);
1705 if (NewCmdLine
== NULL
) {
1706 SHELL_FREE_NON_NULL(CommandName
);
1707 SHELL_FREE_NON_NULL(*CmdLine
);
1708 return (EFI_OUT_OF_RESOURCES
);
1710 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, StrStr((*CmdLine
), L
" "), 0);
1711 if (NewCmdLine
== NULL
) {
1712 SHELL_FREE_NON_NULL(CommandName
);
1713 SHELL_FREE_NON_NULL(*CmdLine
);
1714 return (EFI_OUT_OF_RESOURCES
);
1717 NewCmdLine
= StrnCatGrow(&NewCmdLine
, NULL
, (*CmdLine
), 0);
1720 SHELL_FREE_NON_NULL(*CmdLine
);
1721 SHELL_FREE_NON_NULL(CommandName
);
1724 // re-assign the passed in double pointer to point to our newly allocated buffer
1726 *CmdLine
= NewCmdLine
;
1728 return (EFI_SUCCESS
);
1732 Takes the Argv[0] part of the command line and determine the meaning of it.
1734 @param[in] CmdName pointer to the command line to update.
1736 @retval Internal_Command The name is an internal command.
1737 @retval File_Sys_Change the name is a file system change.
1738 @retval Script_File_Name the name is a NSH script file.
1739 @retval Unknown_Invalid the name is unknown.
1740 @retval Efi_Application the name is an application (.EFI).
1742 SHELL_OPERATION_TYPES
1745 IN CONST CHAR16
*CmdName
1748 CHAR16
* FileWithPath
;
1749 CONST CHAR16
* TempLocation
;
1750 CONST CHAR16
* TempLocation2
;
1752 FileWithPath
= NULL
;
1754 // test for an internal command.
1756 if (ShellCommandIsCommandOnList(CmdName
)) {
1757 return (Internal_Command
);
1761 // Test for file system change request. anything ending with first : and cant have spaces.
1763 if (CmdName
[(StrLen(CmdName
)-1)] == L
':') {
1764 if ( StrStr(CmdName
, L
" ") != NULL
1765 || StrLen(StrStr(CmdName
, L
":")) > 1
1767 return (Unknown_Invalid
);
1769 return (File_Sys_Change
);
1775 if ((FileWithPath
= ShellFindFilePathEx(CmdName
, mExecutableExtensions
)) != NULL
) {
1777 // See if that file has a script file extension
1779 if (StrLen(FileWithPath
) > 4) {
1780 TempLocation
= FileWithPath
+StrLen(FileWithPath
)-4;
1781 TempLocation2
= mScriptExtension
;
1782 if (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0) {
1783 SHELL_FREE_NON_NULL(FileWithPath
);
1784 return (Script_File_Name
);
1789 // Was a file, but not a script. we treat this as an application.
1791 SHELL_FREE_NON_NULL(FileWithPath
);
1792 return (Efi_Application
);
1795 SHELL_FREE_NON_NULL(FileWithPath
);
1797 // No clue what this is... return invalid flag...
1799 return (Unknown_Invalid
);
1803 Determine if the first item in a command line is valid.
1805 @param[in] CmdLine The command line to parse.
1807 @retval EFI_SUCCESS The item is valid.
1808 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1809 @retval EFI_NOT_FOUND The operation type is unknown or invalid.
1814 IN CONST CHAR16
*CmdLine
1818 CHAR16
*FirstParameter
;
1824 Temp
= StrnCatGrow(&Temp
, NULL
, CmdLine
, 0);
1826 return (EFI_OUT_OF_RESOURCES
);
1829 FirstParameter
= StrStr(Temp
, L
"|");
1830 if (FirstParameter
!= NULL
) {
1831 *FirstParameter
= CHAR_NULL
;
1834 FirstParameter
= NULL
;
1837 // Process the command line
1839 Status
= ProcessCommandLineToFinal(&Temp
);
1841 if (!EFI_ERROR(Status
)) {
1842 FirstParameter
= AllocateZeroPool(StrSize(CmdLine
));
1843 if (FirstParameter
== NULL
) {
1844 SHELL_FREE_NON_NULL(Temp
);
1845 return (EFI_OUT_OF_RESOURCES
);
1847 TempWalker
= (CHAR16
*)Temp
;
1848 GetNextParameter(&TempWalker
, &FirstParameter
, StrSize(CmdLine
));
1850 if (GetOperationType(FirstParameter
) == Unknown_Invalid
) {
1851 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
1852 SetLastError(SHELL_NOT_FOUND
);
1853 Status
= EFI_NOT_FOUND
;
1857 SHELL_FREE_NON_NULL(Temp
);
1858 SHELL_FREE_NON_NULL(FirstParameter
);
1863 Determine if a command line contains with a split contains only valid commands.
1865 @param[in] CmdLine The command line to parse.
1867 @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.
1868 @retval EFI_ABORTED CmdLine has at least one invalid command or application.
1873 IN CONST CHAR16
*CmdLine
1876 CONST CHAR16
*TempSpot
;
1880 // Verify up to the pipe or end character
1882 Status
= IsValidSplit(CmdLine
);
1883 if (EFI_ERROR(Status
)) {
1888 // If this was the only item, then get out
1890 if (!ContainsSplit(CmdLine
)) {
1891 return (EFI_SUCCESS
);
1895 // recurse to verify the next item
1897 TempSpot
= FindSplit(CmdLine
)+1;
1898 return (VerifySplit(TempSpot
));
1902 Process a split based operation.
1904 @param[in] CmdLine Pointer to the command line to process
1905 @param[out] ExitStatus The exit status of the command. Ignored if NULL.
1906 Invalid if this function returns an error.
1908 @retval EFI_SUCCESS The operation was successful
1909 @return an error occured.
1913 ProcessNewSplitCommandLine(
1914 IN CONST CHAR16
*CmdLine
,
1915 OUT SHELL_STATUS
*ExitStatus
1921 Status
= VerifySplit(CmdLine
);
1922 if (EFI_ERROR(Status
)) {
1929 // are we in an existing split???
1931 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
1932 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
1935 if (Split
== NULL
) {
1936 Status
= RunSplitCommand(CmdLine
, NULL
, NULL
, ExitStatus
);
1938 Status
= RunSplitCommand(
1945 if (EFI_ERROR(Status
)) {
1946 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_SPLIT
), ShellInfoObject
.HiiHandle
, CmdLine
);
1952 Handle a request to change the current file system.
1954 @param[in] CmdLine The passed in command line.
1956 @retval EFI_SUCCESS The operation was successful.
1961 IN CONST CHAR16
*CmdLine
1965 Status
= EFI_SUCCESS
;
1968 // make sure we are the right operation
1970 ASSERT(CmdLine
[(StrLen(CmdLine
)-1)] == L
':' && StrStr(CmdLine
, L
" ") == NULL
);
1973 // Call the protocol API to do the work
1975 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, CmdLine
);
1978 // Report any errors
1980 if (EFI_ERROR(Status
)) {
1981 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, CmdLine
);
1988 Reprocess the command line to direct all -? to the help command.
1990 if found, will add "help" as argv[0], and move the rest later.
1992 @param[in,out] CmdLine pointer to the command line to update
1997 IN OUT CHAR16
**CmdLine
2000 CHAR16
*CurrentParameter
;
2003 CHAR16
*NewCommandLine
;
2006 Status
= EFI_SUCCESS
;
2008 CurrentParameter
= AllocateZeroPool(StrSize(*CmdLine
));
2009 if (CurrentParameter
== NULL
) {
2010 return (EFI_OUT_OF_RESOURCES
);
2014 while(Walker
!= NULL
&& *Walker
!= CHAR_NULL
) {
2015 LastWalker
= Walker
;
2016 GetNextParameter(&Walker
, &CurrentParameter
, StrSize(*CmdLine
));
2017 if (StrStr(CurrentParameter
, L
"-?") == CurrentParameter
) {
2018 LastWalker
[0] = L
' ';
2019 LastWalker
[1] = L
' ';
2020 NewCommandLine
= AllocateZeroPool(StrSize(L
"help ") + StrSize(*CmdLine
));
2021 if (NewCommandLine
== NULL
) {
2022 Status
= EFI_OUT_OF_RESOURCES
;
2027 // We know the space is sufficient since we just calculated it.
2029 StrnCpy(NewCommandLine
, L
"help ", 5);
2030 StrnCat(NewCommandLine
, *CmdLine
, StrLen(*CmdLine
));
2031 SHELL_FREE_NON_NULL(*CmdLine
);
2032 *CmdLine
= NewCommandLine
;
2037 SHELL_FREE_NON_NULL(CurrentParameter
);
2043 Function to update the shell variable "lasterror".
2045 @param[in] ErrorCode the error code to put into lasterror.
2050 IN CONST SHELL_STATUS ErrorCode
2053 CHAR16 LeString
[19];
2054 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
2055 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ErrorCode
);
2057 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", ErrorCode
);
2059 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
2060 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
2062 return (EFI_SUCCESS
);
2066 Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.
2068 @param[in,out] CmdLine pointer to the command line to update
2070 @retval EFI_SUCCESS The operation was successful
2071 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
2072 @return some other error occured
2076 ProcessCommandLineToFinal(
2077 IN OUT CHAR16
**CmdLine
2081 TrimSpaces(CmdLine
);
2083 Status
= ShellSubstituteAliases(CmdLine
);
2084 if (EFI_ERROR(Status
)) {
2088 TrimSpaces(CmdLine
);
2090 Status
= ShellSubstituteVariables(CmdLine
);
2091 if (EFI_ERROR(Status
)) {
2094 ASSERT (*CmdLine
!= NULL
);
2096 TrimSpaces(CmdLine
);
2099 // update for help parsing
2101 if (StrStr(*CmdLine
, L
"?") != NULL
) {
2103 // This may do nothing if the ? does not indicate help.
2104 // Save all the details for in the API below.
2106 Status
= DoHelpUpdate(CmdLine
);
2109 TrimSpaces(CmdLine
);
2111 return (EFI_SUCCESS
);
2115 Run an internal shell command.
2117 This API will upadate the shell's environment since these commands are libraries.
2119 @param[in] CmdLine the command line to run.
2120 @param[in] FirstParameter the first parameter on the command line
2121 @param[in] ParamProtocol the shell parameters protocol pointer
2123 @param[out] ExitStatus The exit code of the command. Ignored if NULL.
2125 @retval EFI_SUCCESS The command was completed.
2126 @retval EFI_ABORTED The command's operation was aborted.
2131 IN CONST CHAR16
*CmdLine
,
2132 IN CHAR16
*FirstParameter
,
2133 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2134 OUT SHELL_STATUS
*ExitStatus OPTIONAL
2140 SHELL_STATUS CommandReturnedStatus
;
2144 // get the argc and argv updated for internal commands
2146 Status
= UpdateArgcArgv(ParamProtocol
, CmdLine
, &Argv
, &Argc
);
2147 if (!EFI_ERROR(Status
)) {
2149 // Run the internal command.
2151 Status
= ShellCommandRunCommandHandler(FirstParameter
, &CommandReturnedStatus
, &LastError
);
2153 if (!EFI_ERROR(Status
)) {
2155 // Update last error status.
2156 // some commands do not update last error.
2159 SetLastError(CommandReturnedStatus
);
2161 if (ExitStatus
!= NULL
) {
2162 *ExitStatus
= CommandReturnedStatus
;
2166 // Pass thru the exitcode from the app.
2168 if (ShellCommandGetExit()) {
2170 // An Exit was requested ("exit" command), pass its value up.
2172 Status
= CommandReturnedStatus
;
2173 } else if (CommandReturnedStatus
!= SHELL_SUCCESS
&& IsScriptOnlyCommand(FirstParameter
)) {
2175 // Always abort when a script only command fails for any reason
2177 Status
= EFI_ABORTED
;
2178 } else if (ShellCommandGetCurrentScriptFile() != NULL
&& CommandReturnedStatus
== SHELL_ABORTED
) {
2180 // Abort when in a script and a command aborted
2182 Status
= EFI_ABORTED
;
2188 // This is guarenteed to be called after UpdateArgcArgv no matter what else happened.
2189 // This is safe even if the update API failed. In this case, it may be a no-op.
2191 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);
2194 // If a script is running and the command is not a scipt only command, then
2195 // change return value to success so the script won't halt (unless aborted).
2197 // Script only commands have to be able halt the script since the script will
2198 // not operate if they are failing.
2200 if ( ShellCommandGetCurrentScriptFile() != NULL
2201 && !IsScriptOnlyCommand(FirstParameter
)
2202 && Status
!= EFI_ABORTED
2204 Status
= EFI_SUCCESS
;
2211 Function to run the command or file.
2213 @param[in] Type the type of operation being run.
2214 @param[in] CmdLine the command line to run.
2215 @param[in] FirstParameter the first parameter on the command line
2216 @param[in] ParamProtocol the shell parameters protocol pointer
2218 @param[out] ExitStatus The exit code of the command or file.
2221 @retval EFI_SUCCESS The command was completed.
2222 @retval EFI_ABORTED The command's operation was aborted.
2227 IN SHELL_OPERATION_TYPES Type
,
2228 IN CONST CHAR16
*CmdLine
,
2229 IN CHAR16
*FirstParameter
,
2230 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2231 OUT SHELL_STATUS
*ExitStatus
2235 EFI_STATUS StartStatus
;
2236 CHAR16
*CommandWithPath
;
2237 EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
2238 SHELL_STATUS CalleeExitStatus
;
2240 Status
= EFI_SUCCESS
;
2241 CommandWithPath
= NULL
;
2243 CalleeExitStatus
= SHELL_INVALID_PARAMETER
;
2246 case Internal_Command
:
2247 Status
= RunInternalCommand(
2254 case Script_File_Name
:
2255 case Efi_Application
:
2257 // Process a fully qualified path
2259 if (StrStr(FirstParameter
, L
":") != NULL
) {
2260 ASSERT (CommandWithPath
== NULL
);
2261 if (ShellIsFile(FirstParameter
) == EFI_SUCCESS
) {
2262 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, FirstParameter
, 0);
2267 // Process a relative path and also check in the path environment variable
2269 if (CommandWithPath
== NULL
) {
2270 CommandWithPath
= ShellFindFilePathEx(FirstParameter
, mExecutableExtensions
);
2274 // This should be impossible now.
2276 ASSERT(CommandWithPath
!= NULL
);
2279 // Make sure that path is not just a directory (or not found)
2281 if (!EFI_ERROR(ShellIsDirectory(CommandWithPath
))) {
2282 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2283 SetLastError(SHELL_NOT_FOUND
);
2286 case Script_File_Name
:
2287 Status
= RunScriptFile (
2295 case Efi_Application
:
2297 // Get the device path of the application image
2299 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
2300 if (DevPath
== NULL
){
2301 Status
= EFI_OUT_OF_RESOURCES
;
2306 // Execute the device path
2308 Status
= InternalShellExecuteDevicePath(
2318 SHELL_FREE_NON_NULL(DevPath
);
2320 if(EFI_ERROR (Status
)) {
2321 CalleeExitStatus
= (SHELL_STATUS
) (Status
& (~MAX_BIT
));
2323 CalleeExitStatus
= (SHELL_STATUS
) StartStatus
;
2327 // Update last error status.
2329 // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
2330 SetLastError(CalleeExitStatus
);
2346 SHELL_FREE_NON_NULL(CommandWithPath
);
2348 if (ExitStatus
!= NULL
) {
2349 *ExitStatus
= CalleeExitStatus
;
2356 Function to setup StdIn, StdErr, StdOut, and then run the command or file.
2358 @param[in] Type the type of operation being run.
2359 @param[in] CmdLine the command line to run.
2360 @param[in] FirstParameter the first parameter on the command line.
2361 @param[in] ParamProtocol the shell parameters protocol pointer
2363 @param[out] ExitStatus The exit code of the command or file.
2366 @retval EFI_SUCCESS The command was completed.
2367 @retval EFI_ABORTED The command's operation was aborted.
2371 SetupAndRunCommandOrFile(
2372 IN SHELL_OPERATION_TYPES Type
,
2374 IN CHAR16
*FirstParameter
,
2375 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2376 OUT SHELL_STATUS
*ExitStatus
2380 SHELL_FILE_HANDLE OriginalStdIn
;
2381 SHELL_FILE_HANDLE OriginalStdOut
;
2382 SHELL_FILE_HANDLE OriginalStdErr
;
2383 SYSTEM_TABLE_INFO OriginalSystemTableInfo
;
2386 // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
2388 Status
= UpdateStdInStdOutStdErr(ParamProtocol
, CmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2391 // The StdIn, StdOut, and StdErr are set up.
2392 // Now run the command, script, or application
2394 if (!EFI_ERROR(Status
)) {
2395 Status
= RunCommandOrFile(
2407 if (EFI_ERROR(Status
)) {
2408 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
));
2412 // put back the original StdIn, StdOut, and StdErr
2414 RestoreStdInStdOutStdErr(ParamProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2420 Function will process and run a command line.
2422 This will determine if the command line represents an internal shell
2423 command or dispatch an external application.
2425 @param[in] CmdLine The command line to parse.
2426 @param[out] ExitStatus The exit code of the command. Ignored if NULL.
2428 @retval EFI_SUCCESS The command was completed.
2429 @retval EFI_ABORTED The command's operation was aborted.
2434 IN CONST CHAR16
*CmdLine
,
2435 OUT SHELL_STATUS
*ExitStatus
2439 CHAR16
*CleanOriginal
;
2440 CHAR16
*FirstParameter
;
2442 SHELL_OPERATION_TYPES Type
;
2444 ASSERT(CmdLine
!= NULL
);
2445 if (StrLen(CmdLine
) == 0) {
2446 return (EFI_SUCCESS
);
2449 Status
= EFI_SUCCESS
;
2450 CleanOriginal
= NULL
;
2452 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
2453 if (CleanOriginal
== NULL
) {
2454 return (EFI_OUT_OF_RESOURCES
);
2457 TrimSpaces(&CleanOriginal
);
2460 // NULL out comments (leveraged from RunScriptFileHandle() ).
2461 // The # character on a line is used to denote that all characters on the same line
2462 // and to the right of the # are to be ignored by the shell.
2463 // Afterward, again remove spaces, in case any were between the last command-parameter and '#'.
2465 for (TempWalker
= CleanOriginal
; TempWalker
!= NULL
&& *TempWalker
!= CHAR_NULL
; TempWalker
++) {
2466 if (*TempWalker
== L
'^') {
2467 if (*(TempWalker
+ 1) == L
'#') {
2468 CopyMem (TempWalker
, TempWalker
+ 1, StrSize (TempWalker
) - sizeof (TempWalker
[0]));
2470 } else if (*TempWalker
== L
'#') {
2471 *TempWalker
= CHAR_NULL
;
2475 TrimSpaces(&CleanOriginal
);
2478 // Handle case that passed in command line is just 1 or more " " characters.
2480 if (StrLen (CleanOriginal
) == 0) {
2481 SHELL_FREE_NON_NULL(CleanOriginal
);
2482 return (EFI_SUCCESS
);
2485 Status
= ProcessCommandLineToFinal(&CleanOriginal
);
2486 if (EFI_ERROR(Status
)) {
2487 SHELL_FREE_NON_NULL(CleanOriginal
);
2492 // We dont do normal processing with a split command line (output from one command input to another)
2494 if (ContainsSplit(CleanOriginal
)) {
2495 Status
= ProcessNewSplitCommandLine(CleanOriginal
, ExitStatus
);
2496 SHELL_FREE_NON_NULL(CleanOriginal
);
2501 // We need the first parameter information so we can determine the operation type
2503 FirstParameter
= AllocateZeroPool(StrSize(CleanOriginal
));
2504 if (FirstParameter
== NULL
) {
2505 SHELL_FREE_NON_NULL(CleanOriginal
);
2506 return (EFI_OUT_OF_RESOURCES
);
2508 TempWalker
= CleanOriginal
;
2509 GetNextParameter(&TempWalker
, &FirstParameter
, StrSize(CleanOriginal
));
2512 // Depending on the first parameter we change the behavior
2514 switch (Type
= GetOperationType(FirstParameter
)) {
2515 case File_Sys_Change
:
2516 Status
= ChangeMappedDrive (FirstParameter
);
2518 case Internal_Command
:
2519 case Script_File_Name
:
2520 case Efi_Application
:
2521 Status
= SetupAndRunCommandOrFile(
2525 ShellInfoObject
.NewShellParametersProtocol
,
2531 // Whatever was typed, it was invalid.
2533 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2534 SetLastError(SHELL_NOT_FOUND
);
2538 SHELL_FREE_NON_NULL(CleanOriginal
);
2539 SHELL_FREE_NON_NULL(FirstParameter
);
2544 STATIC CONST UINT16 InvalidChars
[] = {L
'*', L
'?', L
'<', L
'>', L
'\\', L
'/', L
'\"', 0x0001, 0x0002};
2546 Function determins if the CommandName COULD be a valid command. It does not determine whether
2547 this is a valid command. It only checks for invalid characters.
2549 @param[in] CommandName The name to check
2551 @retval TRUE CommandName could be a command name
2552 @retval FALSE CommandName could not be a valid command name
2557 IN CONST CHAR16
*CommandName
2561 if (CommandName
== NULL
) {
2566 ; Count
< sizeof(InvalidChars
) / sizeof(InvalidChars
[0])
2569 if (ScanMem16(CommandName
, StrSize(CommandName
), InvalidChars
[Count
]) != NULL
) {
2577 Function to process a NSH script file via SHELL_FILE_HANDLE.
2579 @param[in] Handle The handle to the already opened file.
2580 @param[in] Name The name of the script file.
2582 @param[out] ExitStatus The exit code of the script. Ignored if NULL.
2584 @retval EFI_SUCCESS the script completed sucessfully
2588 RunScriptFileHandle (
2589 IN SHELL_FILE_HANDLE Handle
,
2590 IN CONST CHAR16
*Name
,
2591 OUT SHELL_STATUS
*ExitStatus
2595 SCRIPT_FILE
*NewScriptFile
;
2597 CHAR16
*CommandLine
;
2598 CHAR16
*CommandLine2
;
2599 CHAR16
*CommandLine3
;
2600 SCRIPT_COMMAND_LIST
*LastCommand
;
2602 BOOLEAN PreScriptEchoState
;
2603 BOOLEAN PreCommandEchoState
;
2604 CONST CHAR16
*CurDir
;
2606 CHAR16 LeString
[50];
2607 SHELL_STATUS CalleeExitStatus
;
2609 ASSERT(!ShellCommandGetScriptExit());
2611 CalleeExitStatus
= SHELL_SUCCESS
;
2613 PreScriptEchoState
= ShellCommandGetEchoState();
2615 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
2616 if (NewScriptFile
== NULL
) {
2617 return (EFI_OUT_OF_RESOURCES
);
2623 ASSERT(NewScriptFile
->ScriptName
== NULL
);
2624 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
2625 if (NewScriptFile
->ScriptName
== NULL
) {
2626 DeleteScriptFileStruct(NewScriptFile
);
2627 return (EFI_OUT_OF_RESOURCES
);
2631 // Save the parameters (used to replace %0 to %9 later on)
2633 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
2634 if (NewScriptFile
->Argc
!= 0) {
2635 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
2636 if (NewScriptFile
->Argv
== NULL
) {
2637 DeleteScriptFileStruct(NewScriptFile
);
2638 return (EFI_OUT_OF_RESOURCES
);
2640 for (LoopVar
= 0 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
2641 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
2642 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
2643 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
2644 DeleteScriptFileStruct(NewScriptFile
);
2645 return (EFI_OUT_OF_RESOURCES
);
2649 NewScriptFile
->Argv
= NULL
;
2652 InitializeListHead(&NewScriptFile
->CommandList
);
2653 InitializeListHead(&NewScriptFile
->SubstList
);
2656 // Now build the list of all script commands.
2659 while(!ShellFileHandleEof(Handle
)) {
2660 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
2662 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0 || CommandLine
[0] == '#') {
2663 SHELL_FREE_NON_NULL(CommandLine
);
2666 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
2667 if (NewScriptFile
->CurrentCommand
== NULL
) {
2668 SHELL_FREE_NON_NULL(CommandLine
);
2669 DeleteScriptFileStruct(NewScriptFile
);
2670 return (EFI_OUT_OF_RESOURCES
);
2673 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
2674 NewScriptFile
->CurrentCommand
->Data
= NULL
;
2675 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
2677 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2681 // Add this as the topmost script file
2683 ShellCommandSetNewScript (NewScriptFile
);
2686 // Now enumerate through the commands and run each one.
2688 CommandLine
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
2689 if (CommandLine
== NULL
) {
2690 DeleteScriptFileStruct(NewScriptFile
);
2691 return (EFI_OUT_OF_RESOURCES
);
2693 CommandLine2
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
2694 if (CommandLine2
== NULL
) {
2695 FreePool(CommandLine
);
2696 DeleteScriptFileStruct(NewScriptFile
);
2697 return (EFI_OUT_OF_RESOURCES
);
2700 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
2701 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
2702 ; // conditional increment in the body of the loop
2704 ASSERT(CommandLine2
!= NULL
);
2705 StrnCpy(CommandLine2
, NewScriptFile
->CurrentCommand
->Cl
, PcdGet16(PcdShellPrintBufferSize
)/sizeof(CHAR16
)-1);
2708 // NULL out comments
2710 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
2711 if (*CommandLine3
== L
'^') {
2712 if (*(CommandLine3
+1) == L
'#' || *(CommandLine3
+1) == L
':') {
2713 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
2715 } else if (*CommandLine3
== L
'#') {
2716 *CommandLine3
= CHAR_NULL
;
2720 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
2722 // Due to variability in starting the find and replace action we need to have both buffers the same.
2724 StrnCpy(CommandLine
, CommandLine2
, PcdGet16(PcdShellPrintBufferSize
)/sizeof(CHAR16
)-1);
2727 // Remove the %0 to %9 from the command line (if we have some arguments)
2729 if (NewScriptFile
->Argv
!= NULL
) {
2730 switch (NewScriptFile
->Argc
) {
2732 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", NewScriptFile
->Argv
[9], FALSE
, FALSE
);
2733 ASSERT_EFI_ERROR(Status
);
2735 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", NewScriptFile
->Argv
[8], FALSE
, FALSE
);
2736 ASSERT_EFI_ERROR(Status
);
2738 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", NewScriptFile
->Argv
[7], FALSE
, FALSE
);
2739 ASSERT_EFI_ERROR(Status
);
2741 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", NewScriptFile
->Argv
[6], FALSE
, FALSE
);
2742 ASSERT_EFI_ERROR(Status
);
2744 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", NewScriptFile
->Argv
[5], FALSE
, FALSE
);
2745 ASSERT_EFI_ERROR(Status
);
2747 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", NewScriptFile
->Argv
[4], FALSE
, FALSE
);
2748 ASSERT_EFI_ERROR(Status
);
2750 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", NewScriptFile
->Argv
[3], FALSE
, FALSE
);
2751 ASSERT_EFI_ERROR(Status
);
2753 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", NewScriptFile
->Argv
[2], FALSE
, FALSE
);
2754 ASSERT_EFI_ERROR(Status
);
2756 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", NewScriptFile
->Argv
[1], FALSE
, FALSE
);
2757 ASSERT_EFI_ERROR(Status
);
2759 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%0", NewScriptFile
->Argv
[0], FALSE
, FALSE
);
2760 ASSERT_EFI_ERROR(Status
);
2766 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", L
"\"\"", FALSE
, FALSE
);
2767 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", L
"\"\"", FALSE
, FALSE
);
2768 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", L
"\"\"", FALSE
, FALSE
);
2769 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", L
"\"\"", FALSE
, FALSE
);
2770 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", L
"\"\"", FALSE
, FALSE
);
2771 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", L
"\"\"", FALSE
, FALSE
);
2772 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", L
"\"\"", FALSE
, FALSE
);
2773 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", L
"\"\"", FALSE
, FALSE
);
2774 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", L
"\"\"", FALSE
, FALSE
);
2776 StrnCpy(CommandLine2
, CommandLine
, PcdGet16(PcdShellPrintBufferSize
)/sizeof(CHAR16
)-1);
2778 LastCommand
= NewScriptFile
->CurrentCommand
;
2780 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
2782 if (CommandLine3
!= NULL
&& CommandLine3
[0] == L
':' ) {
2784 // This line is a goto target / label
2787 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
2788 if (CommandLine3
[0] == L
'@') {
2790 // We need to save the current echo state
2791 // and disable echo for just this command.
2793 PreCommandEchoState
= ShellCommandGetEchoState();
2794 ShellCommandSetEchoState(FALSE
);
2795 Status
= RunCommand(CommandLine3
+1, NULL
);
2798 // If command was "@echo -off" or "@echo -on" then don't restore echo state
2800 if (StrCmp (L
"@echo -off", CommandLine3
) != 0 &&
2801 StrCmp (L
"@echo -on", CommandLine3
) != 0) {
2803 // Now restore the pre-'@' echo state.
2805 ShellCommandSetEchoState(PreCommandEchoState
);
2808 if (ShellCommandGetEchoState()) {
2809 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
2810 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
2811 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
2813 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
2815 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
2817 Status
= RunCommand(CommandLine3
, NULL
);
2821 if (ShellCommandGetScriptExit()) {
2823 // ShellCommandGetExitCode() always returns a UINT64
2825 CalleeExitStatus
= (SHELL_STATUS
) ShellCommandGetExitCode();
2826 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", CalleeExitStatus
);
2827 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
2828 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
2830 ShellCommandRegisterExit(FALSE
, 0);
2831 Status
= EFI_SUCCESS
;
2834 if (ShellGetExecutionBreakFlag()) {
2837 if (EFI_ERROR(Status
)) {
2838 CalleeExitStatus
= (SHELL_STATUS
) Status
;
2841 if (ShellCommandGetExit()) {
2842 CalleeExitStatus
= (SHELL_STATUS
) ShellCommandGetExitCode();
2847 // If that commend did not update the CurrentCommand then we need to advance it...
2849 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
2850 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2851 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2852 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2856 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2857 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2858 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2864 FreePool(CommandLine
);
2865 FreePool(CommandLine2
);
2866 ShellCommandSetNewScript (NULL
);
2869 // Only if this was the last script reset the state.
2871 if (ShellCommandGetCurrentScriptFile()==NULL
) {
2872 ShellCommandSetEchoState(PreScriptEchoState
);
2875 if (ExitStatus
!= NULL
) {
2876 *ExitStatus
= CalleeExitStatus
;
2879 return (EFI_SUCCESS
);
2883 Function to process a NSH script file.
2885 @param[in] ScriptPath Pointer to the script file name (including file system path).
2886 @param[in] Handle the handle of the script file already opened.
2887 @param[in] CmdLine the command line to run.
2888 @param[in] ParamProtocol the shell parameters protocol pointer
2890 @param[out] ExitStatus The exit code of the script. Ignored if NULL.
2892 @retval EFI_SUCCESS the script completed sucessfully
2897 IN CONST CHAR16
*ScriptPath
,
2898 IN SHELL_FILE_HANDLE Handle OPTIONAL
,
2899 IN CONST CHAR16
*CmdLine
,
2900 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2901 OUT SHELL_STATUS
*ExitStatus
2905 SHELL_FILE_HANDLE FileHandle
;
2909 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
2910 return (EFI_INVALID_PARAMETER
);
2914 // get the argc and argv updated for scripts
2916 Status
= UpdateArgcArgv(ParamProtocol
, CmdLine
, &Argv
, &Argc
);
2917 if (!EFI_ERROR(Status
)) {
2919 if (Handle
== NULL
) {
2923 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
2924 if (!EFI_ERROR(Status
)) {
2928 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
, ExitStatus
);
2931 // now close the file
2933 ShellCloseFile(&FileHandle
);
2936 Status
= RunScriptFileHandle(Handle
, ScriptPath
, ExitStatus
);
2941 // This is guarenteed to be called after UpdateArgcArgv no matter what else happened.
2942 // This is safe even if the update API failed. In this case, it may be a no-op.
2944 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);