2 This is THE shell (application)
4 Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.<BR>
6 Copyright 2015-2018 Dell Technologies.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
14 // Initialize the global structure
16 SHELL_INFO ShellInfoObject
= {
52 {{NULL
, NULL
}, NULL
, NULL
},
53 {{NULL
, NULL
}, NULL
, NULL
},
65 STATIC CONST CHAR16 mScriptExtension
[] = L
".NSH";
66 STATIC CONST CHAR16 mExecutableExtensions
[] = L
".NSH;.EFI";
67 STATIC CONST CHAR16 mStartupScript
[] = L
"startup.nsh";
68 CONST CHAR16 mNoNestingEnvVarName
[] = L
"nonesting";
69 CONST CHAR16 mNoNestingTrue
[] = L
"True";
70 CONST CHAR16 mNoNestingFalse
[] = L
"False";
73 Cleans off leading and trailing spaces and tabs.
75 @param[in] String pointer to the string to trim them off.
82 ASSERT(String
!= NULL
);
83 ASSERT(*String
!= NULL
);
85 // Remove any spaces and tabs at the beginning of the (*String).
87 while (((*String
)[0] == L
' ') || ((*String
)[0] == L
'\t')) {
88 CopyMem((*String
), (*String
)+1, StrSize((*String
)) - sizeof((*String
)[0]));
92 // Remove any spaces and tabs at the end of the (*String).
94 while ((StrLen (*String
) > 0) && (((*String
)[StrLen((*String
))-1] == L
' ') || ((*String
)[StrLen((*String
))-1] == L
'\t'))) {
95 (*String
)[StrLen((*String
))-1] = CHAR_NULL
;
102 Parse for the next instance of one string within another string. Can optionally make sure that
103 the string was not escaped (^ character) per the shell specification.
105 @param[in] SourceString The string to search within
106 @param[in] FindString The string to look for
107 @param[in] CheckForEscapeCharacter TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances
111 IN CONST CHAR16
*SourceString
,
112 IN CONST CHAR16
*FindString
,
113 IN CONST BOOLEAN CheckForEscapeCharacter
117 if (SourceString
== NULL
) {
120 Temp
= StrStr(SourceString
, FindString
);
123 // If nothing found, or we don't care about escape characters
125 if (Temp
== NULL
|| !CheckForEscapeCharacter
) {
130 // If we found an escaped character, try again on the remainder of the string
132 if ((Temp
> (SourceString
)) && *(Temp
-1) == L
'^') {
133 return FindNextInstance(Temp
+1, FindString
, CheckForEscapeCharacter
);
137 // we found the right character
143 Check whether the string between a pair of % is a valid environment variable name.
145 @param[in] BeginPercent pointer to the first percent.
146 @param[in] EndPercent pointer to the last percent.
148 @retval TRUE is a valid environment variable name.
149 @retval FALSE is NOT a valid environment variable name.
152 IsValidEnvironmentVariableName(
153 IN CONST CHAR16
*BeginPercent
,
154 IN CONST CHAR16
*EndPercent
157 CONST CHAR16
*Walker
;
161 ASSERT (BeginPercent
!= NULL
);
162 ASSERT (EndPercent
!= NULL
);
163 ASSERT (BeginPercent
< EndPercent
);
165 if ((BeginPercent
+ 1) == EndPercent
) {
169 for (Walker
= BeginPercent
+ 1; Walker
< EndPercent
; Walker
++) {
171 (*Walker
>= L
'0' && *Walker
<= L
'9') ||
172 (*Walker
>= L
'A' && *Walker
<= L
'Z') ||
173 (*Walker
>= L
'a' && *Walker
<= L
'z') ||
176 if (Walker
== BeginPercent
+ 1 && (*Walker
>= L
'0' && *Walker
<= L
'9')) {
190 Determine if a command line contains a split operation
192 @param[in] CmdLine The command line to parse.
194 @retval TRUE CmdLine has a valid split.
195 @retval FALSE CmdLine does not have a valid split.
199 IN CONST CHAR16
*CmdLine
202 CONST CHAR16
*TempSpot
;
203 CONST CHAR16
*FirstQuote
;
204 CONST CHAR16
*SecondQuote
;
206 FirstQuote
= FindNextInstance (CmdLine
, L
"\"", TRUE
);
208 TempSpot
= FindFirstCharacter(CmdLine
, L
"|", L
'^');
210 if (FirstQuote
== NULL
||
212 TempSpot
== CHAR_NULL
||
213 FirstQuote
> TempSpot
215 return (BOOLEAN
) ((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
));
218 while ((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
)) {
219 if (FirstQuote
== NULL
|| FirstQuote
> TempSpot
) {
222 SecondQuote
= FindNextInstance (FirstQuote
+ 1, L
"\"", TRUE
);
223 if (SecondQuote
== NULL
) {
226 if (SecondQuote
< TempSpot
) {
227 FirstQuote
= FindNextInstance (SecondQuote
+ 1, L
"\"", TRUE
);
230 FirstQuote
= FindNextInstance (SecondQuote
+ 1, L
"\"", TRUE
);
231 TempSpot
= FindFirstCharacter(TempSpot
+ 1, L
"|", L
'^');
236 return (BOOLEAN
) ((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
));
240 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
241 feature's enabled state was not known when the shell initially launched.
243 @retval EFI_SUCCESS The feature is enabled.
244 @retval EFI_OUT_OF_RESOURCES There is not enough memory available.
247 InternalEfiShellStartCtrlSMonitor(
251 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
*SimpleEx
;
252 EFI_KEY_DATA KeyData
;
255 Status
= gBS
->OpenProtocol(
256 gST
->ConsoleInHandle
,
257 &gEfiSimpleTextInputExProtocolGuid
,
261 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
262 if (EFI_ERROR(Status
)) {
267 STRING_TOKEN (STR_SHELL_NO_IN_EX
),
268 ShellInfoObject
.HiiHandle
);
269 return (EFI_SUCCESS
);
272 KeyData
.KeyState
.KeyToggleState
= 0;
273 KeyData
.Key
.ScanCode
= 0;
274 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
275 KeyData
.Key
.UnicodeChar
= L
's';
277 Status
= SimpleEx
->RegisterKeyNotify(
280 NotificationFunction
,
281 &ShellInfoObject
.CtrlSNotifyHandle1
);
283 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
284 if (!EFI_ERROR(Status
)) {
285 Status
= SimpleEx
->RegisterKeyNotify(
288 NotificationFunction
,
289 &ShellInfoObject
.CtrlSNotifyHandle2
);
291 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
292 KeyData
.Key
.UnicodeChar
= 19;
294 if (!EFI_ERROR(Status
)) {
295 Status
= SimpleEx
->RegisterKeyNotify(
298 NotificationFunction
,
299 &ShellInfoObject
.CtrlSNotifyHandle3
);
301 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
302 if (!EFI_ERROR(Status
)) {
303 Status
= SimpleEx
->RegisterKeyNotify(
306 NotificationFunction
,
307 &ShellInfoObject
.CtrlSNotifyHandle4
);
315 The entry point for the application.
317 @param[in] ImageHandle The firmware allocated handle for the EFI image.
318 @param[in] SystemTable A pointer to the EFI System Table.
320 @retval EFI_SUCCESS The entry point is executed successfully.
321 @retval other Some error occurs when executing this entry point.
327 IN EFI_HANDLE ImageHandle
,
328 IN EFI_SYSTEM_TABLE
*SystemTable
334 EFI_HANDLE ConInHandle
;
335 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*OldConIn
;
338 if (PcdGet8(PcdShellSupportLevel
) > 3) {
339 return (EFI_UNSUPPORTED
);
345 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
346 if (EFI_ERROR(Status
)) {
351 // Populate the global structure from PCDs
353 ShellInfoObject
.ImageDevPath
= NULL
;
354 ShellInfoObject
.FileDevPath
= NULL
;
355 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
356 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
357 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
360 // verify we dont allow for spec violation
362 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
365 // Initialize the LIST ENTRY objects...
367 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
368 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
369 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
372 // Check PCDs for optional features that are not implemented yet.
374 if ( PcdGetBool(PcdShellSupportOldProtocols
)
375 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
376 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
378 return (EFI_UNSUPPORTED
);
382 // turn off the watchdog timer
384 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
387 // install our console logger. This will keep a log of the output for back-browsing
389 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
390 if (!EFI_ERROR(Status
)) {
392 // Enable the cursor to be visible
394 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
397 // If supporting EFI 1.1 we need to install HII protocol
398 // only do this if PcdShellRequireHiiPlatform == FALSE
400 // remove EFI_UNSUPPORTED check above when complete.
401 ///@todo add support for Framework HII
404 // install our (solitary) HII package
406 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
407 if (ShellInfoObject
.HiiHandle
== NULL
) {
408 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
409 ///@todo Add our package into Framework HII
411 if (ShellInfoObject
.HiiHandle
== NULL
) {
412 Status
= EFI_NOT_STARTED
;
418 // create and install the EfiShellParametersProtocol
420 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
421 ASSERT_EFI_ERROR(Status
);
422 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
425 // create and install the EfiShellProtocol
427 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
428 ASSERT_EFI_ERROR(Status
);
429 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
432 // Now initialize the shell library (it requires Shell Parameters protocol)
434 Status
= ShellInitialize();
435 ASSERT_EFI_ERROR(Status
);
437 Status
= CommandInit();
438 ASSERT_EFI_ERROR(Status
);
440 Status
= ShellInitEnvVarList ();
443 // Check the command line
445 Status
= ProcessCommandLine ();
446 if (EFI_ERROR (Status
)) {
451 // If shell support level is >= 1 create the mappings and paths
453 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
454 Status
= ShellCommandCreateInitialMappingsAndPaths();
458 // Set the environment variable for nesting support
462 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoNest
) {
464 // No change. require nesting in Shell Protocol Execute()
466 StrnCatGrow(&TempString
,
471 StrnCatGrow(&TempString
,
476 Status
= InternalEfiShellSetEnv(mNoNestingEnvVarName
, TempString
, TRUE
);
477 SHELL_FREE_NON_NULL(TempString
);
481 // save the device path for the loaded image and the device path for the filepath (under loaded image)
482 // These are where to look for the startup.nsh file
484 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
485 ASSERT_EFI_ERROR(Status
);
488 // Display the version
490 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
493 gST
->ConOut
->Mode
->CursorRow
,
495 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL
),
496 ShellInfoObject
.HiiHandle
,
497 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
498 gEfiShellProtocol
->MajorVersion
,
499 gEfiShellProtocol
->MinorVersion
506 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER
),
507 ShellInfoObject
.HiiHandle
,
508 (CHAR16
*) PcdGetPtr (PcdShellSupplier
)
515 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI
),
516 ShellInfoObject
.HiiHandle
,
517 (gST
->Hdr
.Revision
&0xffff0000)>>16,
518 (gST
->Hdr
.Revision
&0x0000ffff),
520 gST
->FirmwareRevision
525 // Display the mapping
527 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
528 Status
= RunCommand(L
"map");
529 ASSERT_EFI_ERROR(Status
);
533 // init all the built in alias'
535 Status
= SetBuiltInAlias();
536 ASSERT_EFI_ERROR(Status
);
539 // Initialize environment variables
541 if (ShellCommandGetProfileList() != NULL
) {
542 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
543 ASSERT_EFI_ERROR(Status
);
547 TempString
= AllocateZeroPool(Size
);
549 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
550 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
551 ASSERT_EFI_ERROR(Status
);
553 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
554 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
555 ASSERT_EFI_ERROR(Status
);
557 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
558 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
559 ASSERT_EFI_ERROR(Status
);
561 FreePool(TempString
);
563 if (!EFI_ERROR(Status
)) {
564 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
566 // Set up the event for CTRL-C monitoring...
568 Status
= InernalEfiShellStartMonitor();
571 if (!EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
573 // Set up the event for CTRL-S monitoring...
575 Status
= InternalEfiShellStartCtrlSMonitor();
578 if (!EFI_ERROR(Status
) && ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
580 // close off the gST->ConIn
582 OldConIn
= gST
->ConIn
;
583 ConInHandle
= gST
->ConsoleInHandle
;
584 gST
->ConIn
= CreateSimpleTextInOnFile((SHELL_FILE_HANDLE
)&FileInterfaceNulFile
, &gST
->ConsoleInHandle
);
590 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
592 // process the startup script or launch the called app.
594 Status
= DoStartupScript(ShellInfoObject
.ImageDevPath
, ShellInfoObject
.FileDevPath
);
597 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
&& !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
599 // begin the UI waiting loop
603 // clean out all the memory allocated for CONST <something> * return values
604 // between each shell prompt presentation
606 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
607 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
611 // Reset page break back to default.
613 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
614 ASSERT (ShellInfoObject
.ConsoleInfo
!= NULL
);
615 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
616 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
621 Status
= DoShellPrompt();
622 } while (!ShellCommandGetExit());
624 if (OldConIn
!= NULL
&& ConInHandle
!= NULL
) {
625 CloseSimpleTextInOnFile (gST
->ConIn
);
626 gST
->ConIn
= OldConIn
;
627 gST
->ConsoleInHandle
= ConInHandle
;
634 // uninstall protocols / free memory / etc...
636 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
637 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
638 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
640 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
641 FreePool(ShellInfoObject
.ImageDevPath
);
642 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
644 if (ShellInfoObject
.FileDevPath
!= NULL
) {
645 FreePool(ShellInfoObject
.FileDevPath
);
646 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
648 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
649 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
650 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
652 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
653 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
654 InternalEfiShellSetEnv(L
"cwd", NULL
, TRUE
);
656 CleanUpShellEnvironment (ShellInfoObject
.NewEfiShellProtocol
);
657 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
660 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
661 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
664 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
665 ASSERT(FALSE
); ///@todo finish this de-allocation (free SplitStdIn/Out when needed).
667 for ( Split
= (SPLIT_LIST
*)GetFirstNode (&ShellInfoObject
.SplitList
.Link
)
668 ; !IsNull (&ShellInfoObject
.SplitList
.Link
, &Split
->Link
)
669 ; Split
= (SPLIT_LIST
*)GetNextNode (&ShellInfoObject
.SplitList
.Link
, &Split
->Link
)
671 RemoveEntryList (&Split
->Link
);
675 DEBUG_CODE (InitializeListHead (&ShellInfoObject
.SplitList
.Link
););
678 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
679 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
680 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
683 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
684 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
685 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
688 if (ShellInfoObject
.HiiHandle
!= NULL
) {
689 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
690 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
693 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
694 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
697 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
698 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
699 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
700 FreePool(ShellInfoObject
.ConsoleInfo
);
701 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
704 ShellFreeEnvVarList ();
706 if (ShellCommandGetExit()) {
707 return ((EFI_STATUS
)ShellCommandGetExitCode());
713 Sets all the alias' that were registered with the ShellCommandLib library.
715 @retval EFI_SUCCESS all init commands were run successfully.
723 CONST ALIAS_LIST
*List
;
727 // Get all the commands we want to run
729 List
= ShellCommandGetInitAliasList();
732 // for each command in the List
734 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
735 ; !IsNull (&List
->Link
, &Node
->Link
)
736 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
739 // install the alias'
741 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
742 ASSERT_EFI_ERROR(Status
);
744 return (EFI_SUCCESS
);
748 Internal function to determine if 2 command names are really the same.
750 @param[in] Command1 The pointer to the first command name.
751 @param[in] Command2 The pointer to the second command name.
753 @retval TRUE The 2 command names are the same.
754 @retval FALSE The 2 command names are not the same.
758 IN CONST CHAR16
*Command1
,
759 IN CONST CHAR16
*Command2
762 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
769 Internal function to determine if a command is a script only command.
771 @param[in] CommandName The pointer to the command name.
773 @retval TRUE The command is a script only command.
774 @retval FALSE The command is not a script only command.
778 IN CONST CHAR16
*CommandName
781 if (IsCommand(CommandName
, L
"for")
782 ||IsCommand(CommandName
, L
"endfor")
783 ||IsCommand(CommandName
, L
"if")
784 ||IsCommand(CommandName
, L
"else")
785 ||IsCommand(CommandName
, L
"endif")
786 ||IsCommand(CommandName
, L
"goto")) {
793 This function will populate the 2 device path protocol parameters based on the
794 global gImageHandle. The DevPath will point to the device path for the handle that has
795 loaded image protocol installed on it. The FilePath will point to the device path
796 for the file that was loaded.
798 @param[in, out] DevPath On a successful return the device path to the loaded image.
799 @param[in, out] FilePath On a successful return the device path to the file.
801 @retval EFI_SUCCESS The 2 device paths were successfully returned.
802 @retval other A error from gBS->HandleProtocol.
807 GetDevicePathsForImageAndFile (
808 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
809 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
813 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
814 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
816 ASSERT(DevPath
!= NULL
);
817 ASSERT(FilePath
!= NULL
);
819 Status
= gBS
->OpenProtocol (
821 &gEfiLoadedImageProtocolGuid
,
822 (VOID
**)&LoadedImage
,
825 EFI_OPEN_PROTOCOL_GET_PROTOCOL
827 if (!EFI_ERROR (Status
)) {
828 Status
= gBS
->OpenProtocol (
829 LoadedImage
->DeviceHandle
,
830 &gEfiDevicePathProtocolGuid
,
831 (VOID
**)&ImageDevicePath
,
834 EFI_OPEN_PROTOCOL_GET_PROTOCOL
836 if (!EFI_ERROR (Status
)) {
837 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
838 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
840 LoadedImage
->DeviceHandle
,
841 &gEfiDevicePathProtocolGuid
,
847 &gEfiLoadedImageProtocolGuid
,
855 Process all Uefi Shell 2.0 command line options.
857 see Uefi Shell 2.0 section 3.2 for full details.
859 the command line must resemble the following:
861 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
863 ShellOpt-options Options which control the initialization behavior of the shell.
864 These options are read from the EFI global variable "ShellOpt"
865 and are processed before options or file-name.
867 options Options which control the initialization behavior of the shell.
869 file-name The name of a UEFI shell application or script to be executed
870 after initialization is complete. By default, if file-name is
871 specified, then -nostartup is implied. Scripts are not supported
874 file-name-options The command-line options that are passed to file-name when it
877 This will initialize the ShellInfoObject.ShellInitSettings global variable.
879 @retval EFI_SUCCESS The variable is initialized.
889 CHAR16
*DelayValueStr
;
892 EFI_UNICODE_COLLATION_PROTOCOL
*UnicodeCollation
;
894 // `file-name-options` will contain arguments to `file-name` that we don't
895 // know about. This would cause ShellCommandLineParse to error, so we parse
896 // arguments manually, ignoring those after the first thing that doesn't look
897 // like a shell option (which is assumed to be `file-name`).
899 Status
= gBS
->LocateProtocol (
900 &gEfiUnicodeCollation2ProtocolGuid
,
902 (VOID
**) &UnicodeCollation
904 if (EFI_ERROR (Status
)) {
905 Status
= gBS
->LocateProtocol (
906 &gEfiUnicodeCollationProtocolGuid
,
908 (VOID
**) &UnicodeCollation
910 if (EFI_ERROR (Status
)) {
915 // Set default options
916 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= FALSE
;
917 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= FALSE
;
918 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= FALSE
;
919 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= FALSE
;
920 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= FALSE
;
921 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= FALSE
;
922 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= FALSE
;
923 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= FALSE
;
924 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= FALSE
;
925 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoNest
= FALSE
;
926 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
929 // Start LoopVar at 0 to parse only optional arguments at Argv[0]
930 // and parse other parameters from Argv[1]. This is for use case that
931 // UEFI Shell boot option is created, and OptionalData is provided
932 // that starts with shell command-line options.
934 for (LoopVar
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
935 CurrentArg
= gEfiShellParametersProtocol
->Argv
[LoopVar
];
936 if (UnicodeCollation
->StriColl (
941 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= TRUE
;
943 else if (UnicodeCollation
->StriColl (
948 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= TRUE
;
950 else if (UnicodeCollation
->StriColl (
955 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= TRUE
;
957 else if (UnicodeCollation
->StriColl (
962 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= TRUE
;
964 else if (UnicodeCollation
->StriColl (
969 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= TRUE
;
971 else if (UnicodeCollation
->StriColl (
976 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= TRUE
;
978 else if (UnicodeCollation
->StriColl (
983 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= TRUE
;
985 else if (UnicodeCollation
->StriColl (
990 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoNest
= TRUE
;
992 else if (UnicodeCollation
->StriColl (
997 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= TRUE
;
998 // Check for optional delay value following "-delay"
999 if ((LoopVar
+ 1) >= gEfiShellParametersProtocol
->Argc
) {
1000 DelayValueStr
= NULL
;
1002 DelayValueStr
= gEfiShellParametersProtocol
->Argv
[LoopVar
+ 1];
1004 if (DelayValueStr
!= NULL
){
1005 if (*DelayValueStr
== L
':') {
1008 if (!EFI_ERROR(ShellConvertStringToUint64 (
1014 ShellInfoObject
.ShellInitSettings
.Delay
= (UINTN
)DelayValue
;
1018 } else if (UnicodeCollation
->StriColl (
1023 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= TRUE
;
1024 } else if (StrnCmp (L
"-", CurrentArg
, 1) == 0) {
1025 // Unrecognized option
1026 ShellPrintHiiEx(-1, -1, NULL
,
1027 STRING_TOKEN (STR_GEN_PROBLEM
),
1028 ShellInfoObject
.HiiHandle
,
1031 return EFI_INVALID_PARAMETER
;
1034 // First argument should be Shell.efi image name
1040 ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;
1043 // If first argument contains a space, then add double quotes before the argument
1045 if (StrStr (CurrentArg
, L
" ") != NULL
) {
1046 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileName
, &Size
, L
"\"", 0);
1047 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
1048 return (EFI_OUT_OF_RESOURCES
);
1051 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileName
, &Size
, CurrentArg
, 0);
1052 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
1053 return (EFI_OUT_OF_RESOURCES
);
1056 // If first argument contains a space, then add double quotes after the argument
1058 if (StrStr (CurrentArg
, L
" ") != NULL
) {
1059 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileName
, &Size
, L
"\"", 0);
1060 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
1061 return (EFI_OUT_OF_RESOURCES
);
1065 // We found `file-name`.
1067 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
1070 // Add `file-name-options`
1071 for (Size
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
1072 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
1074 // Add a space between arguments
1076 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1077 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
, &Size
, L
" ", 0);
1078 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1079 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
1080 return (EFI_OUT_OF_RESOURCES
);
1084 // If an argumnent contains a space, then add double quotes before the argument
1086 if (StrStr (gEfiShellParametersProtocol
->Argv
[LoopVar
], L
" ") != NULL
) {
1087 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
1091 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1092 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
1093 return (EFI_OUT_OF_RESOURCES
);
1096 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
1098 gEfiShellParametersProtocol
->Argv
[LoopVar
],
1100 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1101 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
1102 return (EFI_OUT_OF_RESOURCES
);
1105 // If an argumnent contains a space, then add double quotes after the argument
1107 if (StrStr (gEfiShellParametersProtocol
->Argv
[LoopVar
], L
" ") != NULL
) {
1108 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
1112 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1113 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
1114 return (EFI_OUT_OF_RESOURCES
);
1121 // "-nointerrupt" overrides "-delay"
1122 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
1123 ShellInfoObject
.ShellInitSettings
.Delay
= 0;
1130 Function try to find location of the Startup.nsh file.
1132 The buffer is callee allocated and should be freed by the caller.
1134 @param ImageDevicePath The path to the image for shell. first place to look for the startup script
1135 @param FileDevicePath The path to the file for shell. second place to look for the startup script.
1137 @retval NULL No Startup.nsh file was found.
1138 @return !=NULL Pointer to NULL-terminated path.
1141 LocateStartupScript (
1142 IN EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
,
1143 IN EFI_DEVICE_PATH_PROTOCOL
*FileDevicePath
1146 CHAR16
*StartupScriptPath
;
1148 CONST CHAR16
*MapName
;
1151 StartupScriptPath
= NULL
;
1155 // Try to find 'Startup.nsh' in the directory where the shell itself was launched.
1157 MapName
= ShellInfoObject
.NewEfiShellProtocol
->GetMapFromDevicePath (&ImageDevicePath
);
1158 if (MapName
!= NULL
) {
1159 StartupScriptPath
= StrnCatGrow (&StartupScriptPath
, &Size
, MapName
, 0);
1160 if (StartupScriptPath
== NULL
) {
1162 // Do not locate the startup script in sys path when out of resource.
1166 TempSpot
= StrStr (StartupScriptPath
, L
";");
1167 if (TempSpot
!= NULL
) {
1168 *TempSpot
= CHAR_NULL
;
1171 InternalEfiShellSetEnv(L
"homefilesystem", StartupScriptPath
, TRUE
);
1173 StartupScriptPath
= StrnCatGrow (&StartupScriptPath
, &Size
, ((FILEPATH_DEVICE_PATH
*)FileDevicePath
)->PathName
, 0);
1174 PathRemoveLastItem (StartupScriptPath
);
1175 StartupScriptPath
= StrnCatGrow (&StartupScriptPath
, &Size
, mStartupScript
, 0);
1179 // Try to find 'Startup.nsh' in the execution path defined by the envrionment variable PATH.
1181 if ((StartupScriptPath
== NULL
) || EFI_ERROR (ShellIsFile (StartupScriptPath
))) {
1182 SHELL_FREE_NON_NULL (StartupScriptPath
);
1183 StartupScriptPath
= ShellFindFilePath (mStartupScript
);
1186 return StartupScriptPath
;
1190 Handles all interaction with the default startup script.
1192 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
1194 @param ImagePath the path to the image for shell. first place to look for the startup script
1195 @param FilePath the path to the file for shell. second place to look for the startup script.
1197 @retval EFI_SUCCESS the variable is initialized.
1201 IN EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
1202 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
1206 EFI_STATUS CalleeStatus
;
1209 CHAR16
*FileStringPath
;
1210 CHAR16
*FullFileStringPath
;
1213 Key
.UnicodeChar
= CHAR_NULL
;
1216 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
1218 // launch something else instead
1220 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
1221 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1222 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
1224 FileStringPath
= AllocateZeroPool(NewSize
);
1225 if (FileStringPath
== NULL
) {
1226 return (EFI_OUT_OF_RESOURCES
);
1228 StrCpyS(FileStringPath
, NewSize
/sizeof(CHAR16
), ShellInfoObject
.ShellInitSettings
.FileName
);
1229 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1230 StrnCatS(FileStringPath
, NewSize
/sizeof(CHAR16
), L
" ", NewSize
/sizeof(CHAR16
) - StrLen(FileStringPath
) -1);
1231 StrnCatS(FileStringPath
, NewSize
/sizeof(CHAR16
), ShellInfoObject
.ShellInitSettings
.FileOptions
, NewSize
/sizeof(CHAR16
) - StrLen(FileStringPath
) -1);
1233 Status
= RunShellCommand(FileStringPath
, &CalleeStatus
);
1234 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
== TRUE
) {
1235 ShellCommandRegisterExit(gEfiShellProtocol
->BatchIsActive(), (UINT64
)CalleeStatus
);
1237 FreePool(FileStringPath
);
1243 // for shell level 0 we do no scripts
1244 // Without the Startup bit overriding we allow for nostartup to prevent scripts
1246 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
1247 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
1249 return (EFI_SUCCESS
);
1252 gST
->ConOut
->EnableCursor(gST
->ConOut
, FALSE
);
1254 // print out our warning and see if they press a key
1256 for ( Status
= EFI_UNSUPPORTED
, Delay
= ShellInfoObject
.ShellInitSettings
.Delay
1257 ; Delay
!= 0 && EFI_ERROR(Status
)
1260 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
);
1261 gBS
->Stall (1000000);
1262 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
1263 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
1266 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
1267 gST
->ConOut
->EnableCursor(gST
->ConOut
, TRUE
);
1272 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
1273 return (EFI_SUCCESS
);
1276 FileStringPath
= LocateStartupScript (ImagePath
, FilePath
);
1277 if (FileStringPath
!= NULL
) {
1278 FullFileStringPath
= FullyQualifyPath(FileStringPath
);
1279 if (FullFileStringPath
== NULL
) {
1280 Status
= RunScriptFile (FileStringPath
, NULL
, FileStringPath
, ShellInfoObject
.NewShellParametersProtocol
);
1282 Status
= RunScriptFile (FullFileStringPath
, NULL
, FullFileStringPath
, ShellInfoObject
.NewShellParametersProtocol
);
1283 FreePool(FullFileStringPath
);
1285 FreePool (FileStringPath
);
1288 // we return success since startup script is not mandatory.
1290 Status
= EFI_SUCCESS
;
1297 Function to perform the shell prompt looping. It will do a single prompt,
1298 dispatch the result, and then return. It is expected that the caller will
1299 call this function in a loop many times.
1302 @retval RETURN_ABORTED
1312 CONST CHAR16
*CurDir
;
1315 LIST_ENTRY OldBufferList
;
1320 // Get screen setting to decide size of the command line buffer
1322 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
1323 BufferSize
= Column
* Row
* sizeof (CHAR16
);
1324 CmdLine
= AllocateZeroPool (BufferSize
);
1325 if (CmdLine
== NULL
) {
1326 return EFI_OUT_OF_RESOURCES
;
1329 SaveBufferList(&OldBufferList
);
1330 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1335 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
1337 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1338 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1340 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1344 // Read a line from the console
1346 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
1349 // Null terminate the string and parse it
1351 if (!EFI_ERROR (Status
)) {
1353 // Reset the CTRL-C event just before running the command (yes we ignore the return values)
1355 Status
= gBS
->CheckEvent (ShellInfoObject
.NewEfiShellProtocol
->ExecutionBreak
);
1357 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
1358 Status
= RunCommand(CmdLine
);
1362 // Done with this command
1364 RestoreBufferList(&OldBufferList
);
1370 Add a buffer to the Buffer To Free List for safely returning buffers to other
1371 places without risking letting them modify internal shell information.
1373 @param Buffer Something to pass to FreePool when the shell is exiting.
1376 AddBufferToFreeList (
1380 BUFFER_LIST
*BufferListEntry
;
1382 if (Buffer
== NULL
) {
1386 BufferListEntry
= AllocateZeroPool (sizeof (BUFFER_LIST
));
1387 if (BufferListEntry
== NULL
) {
1391 BufferListEntry
->Buffer
= Buffer
;
1392 InsertTailList (&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
1398 Create a new buffer list and stores the old one to OldBufferList
1400 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
1404 OUT LIST_ENTRY
*OldBufferList
1407 CopyMem (OldBufferList
, &ShellInfoObject
.BufferToFreeList
.Link
, sizeof (LIST_ENTRY
));
1408 InitializeListHead (&ShellInfoObject
.BufferToFreeList
.Link
);
1412 Restore previous nodes into BufferToFreeList .
1414 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
1418 IN OUT LIST_ENTRY
*OldBufferList
1421 FreeBufferList (&ShellInfoObject
.BufferToFreeList
);
1422 CopyMem (&ShellInfoObject
.BufferToFreeList
.Link
, OldBufferList
, sizeof (LIST_ENTRY
));
1427 Add a buffer to the Line History List
1429 @param Buffer The line buffer to add.
1432 AddLineToCommandHistory(
1433 IN CONST CHAR16
*Buffer
1437 BUFFER_LIST
*Walker
;
1438 UINT16 MaxHistoryCmdCount
;
1442 MaxHistoryCmdCount
= PcdGet16(PcdShellMaxHistoryCommandCount
);
1444 if (MaxHistoryCmdCount
== 0) {
1449 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1454 Node
->Buffer
= AllocateCopyPool (StrSize (Buffer
), Buffer
);
1455 if (Node
->Buffer
== NULL
) {
1460 for ( Walker
= (BUFFER_LIST
*)GetFirstNode(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)
1461 ; !IsNull(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Walker
->Link
)
1462 ; Walker
= (BUFFER_LIST
*)GetNextNode(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Walker
->Link
)
1466 if (Count
< MaxHistoryCmdCount
){
1467 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1469 Walker
= (BUFFER_LIST
*)GetFirstNode(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
1470 RemoveEntryList(&Walker
->Link
);
1471 if (Walker
->Buffer
!= NULL
) {
1472 FreePool(Walker
->Buffer
);
1475 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1480 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1481 with the correct command name.
1483 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1484 command name if it was an alias. If it was not
1485 an alias it will be unchanged. This function may
1486 change the buffer to fit the command name.
1488 @retval EFI_SUCCESS The name was changed.
1489 @retval EFI_SUCCESS The name was not an alias.
1490 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1494 IN OUT CHAR16
**CommandString
1497 CONST CHAR16
*NewString
;
1499 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
1500 if (NewString
== NULL
) {
1501 return (EFI_SUCCESS
);
1503 FreePool(*CommandString
);
1504 *CommandString
= AllocateCopyPool(StrSize(NewString
), NewString
);
1505 if (*CommandString
== NULL
) {
1506 return (EFI_OUT_OF_RESOURCES
);
1508 return (EFI_SUCCESS
);
1512 This function will eliminate unreplaced (and therefore non-found) environment variables.
1514 @param[in,out] CmdLine The command line to update.
1517 StripUnreplacedEnvironmentVariables(
1518 IN OUT CHAR16
*CmdLine
1521 CHAR16
*FirstPercent
;
1523 CHAR16
*SecondPercent
;
1524 CHAR16
*SecondQuote
;
1525 CHAR16
*CurrentLocator
;
1527 for (CurrentLocator
= CmdLine
; CurrentLocator
!= NULL
; ) {
1528 FirstQuote
= FindNextInstance(CurrentLocator
, L
"\"", TRUE
);
1529 FirstPercent
= FindNextInstance(CurrentLocator
, L
"%", TRUE
);
1530 SecondPercent
= FirstPercent
!=NULL
?FindNextInstance(FirstPercent
+1, L
"%", TRUE
):NULL
;
1531 if (FirstPercent
== NULL
|| SecondPercent
== NULL
) {
1533 // If we ever don't have 2 % we are done.
1538 if (FirstQuote
!= NULL
&& FirstQuote
< FirstPercent
) {
1539 SecondQuote
= FindNextInstance(FirstQuote
+1, L
"\"", TRUE
);
1541 // Quote is first found
1544 if (SecondQuote
< FirstPercent
) {
1546 // restart after the pair of "
1548 CurrentLocator
= SecondQuote
+ 1;
1549 } else /* FirstPercent < SecondQuote */{
1551 // Restart on the first percent
1553 CurrentLocator
= FirstPercent
;
1558 if (FirstQuote
== NULL
|| SecondPercent
< FirstQuote
) {
1559 if (IsValidEnvironmentVariableName(FirstPercent
, SecondPercent
)) {
1561 // We need to remove from FirstPercent to SecondPercent
1563 CopyMem(FirstPercent
, SecondPercent
+ 1, StrSize(SecondPercent
+ 1));
1565 // don't need to update the locator. both % characters are gone.
1568 CurrentLocator
= SecondPercent
+ 1;
1572 CurrentLocator
= FirstQuote
;
1574 return (EFI_SUCCESS
);
1578 Function allocates a new command line and replaces all instances of environment
1579 variable names that are correctly preset to their values.
1581 If the return value is not NULL the memory must be caller freed.
1583 @param[in] OriginalCommandLine The original command line
1585 @retval NULL An error occurred.
1586 @return The new command line with no environment variables present.
1589 ShellConvertVariables (
1590 IN CONST CHAR16
*OriginalCommandLine
1593 CONST CHAR16
*MasterEnvList
;
1595 CHAR16
*NewCommandLine1
;
1596 CHAR16
*NewCommandLine2
;
1600 SCRIPT_FILE
*CurrentScriptFile
;
1601 ALIAS_LIST
*AliasListNode
;
1603 ASSERT(OriginalCommandLine
!= NULL
);
1606 NewSize
= StrSize(OriginalCommandLine
);
1607 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
1610 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1613 // calculate the size required for the post-conversion string...
1615 if (CurrentScriptFile
!= NULL
) {
1616 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1617 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1618 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1620 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
1622 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
1625 // we need a preceding and if there is space no ^ preceding (if no space ignore)
1627 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
1628 NewSize
+= StrSize(AliasListNode
->CommandString
);
1634 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1635 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1636 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1638 if (StrSize(MasterEnvList
) > ItemSize
) {
1639 ItemSize
= StrSize(MasterEnvList
);
1641 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
1643 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
1646 // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)
1648 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
1649 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
1650 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
1656 // now do the replacements...
1658 NewCommandLine1
= AllocateZeroPool (NewSize
);
1659 NewCommandLine2
= AllocateZeroPool(NewSize
);
1660 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
1661 if (NewCommandLine1
== NULL
|| NewCommandLine2
== NULL
|| ItemTemp
== NULL
) {
1662 SHELL_FREE_NON_NULL(NewCommandLine1
);
1663 SHELL_FREE_NON_NULL(NewCommandLine2
);
1664 SHELL_FREE_NON_NULL(ItemTemp
);
1667 CopyMem (NewCommandLine1
, OriginalCommandLine
, StrSize (OriginalCommandLine
));
1669 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1670 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
1671 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1674 ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
)),
1678 ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
)),
1682 ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
)),
1685 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1686 StrCpyS(NewCommandLine1
, NewSize
/sizeof(CHAR16
), NewCommandLine2
);
1688 if (CurrentScriptFile
!= NULL
) {
1689 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1690 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1691 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1693 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1694 StrCpyS(NewCommandLine1
, NewSize
/sizeof(CHAR16
), NewCommandLine2
);
1699 // Remove non-existent environment variables
1701 StripUnreplacedEnvironmentVariables(NewCommandLine1
);
1704 // Now cleanup any straggler intentionally ignored "%" characters
1706 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, L
"^%", L
"%", TRUE
, FALSE
);
1707 StrCpyS(NewCommandLine1
, NewSize
/sizeof(CHAR16
), NewCommandLine2
);
1709 FreePool(NewCommandLine2
);
1712 return (NewCommandLine1
);
1716 Internal function to run a command line with pipe usage.
1718 @param[in] CmdLine The pointer to the command line.
1719 @param[in] StdIn The pointer to the Standard input.
1720 @param[in] StdOut The pointer to the Standard output.
1722 @retval EFI_SUCCESS The split command is executed successfully.
1723 @retval other Some error occurs when executing the split command.
1727 IN CONST CHAR16
*CmdLine
,
1728 IN SHELL_FILE_HANDLE StdIn
,
1729 IN SHELL_FILE_HANDLE StdOut
1733 CHAR16
*NextCommandLine
;
1734 CHAR16
*OurCommandLine
;
1738 SHELL_FILE_HANDLE TempFileHandle
;
1741 ASSERT(StdOut
== NULL
);
1743 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1745 Status
= EFI_SUCCESS
;
1746 NextCommandLine
= NULL
;
1747 OurCommandLine
= NULL
;
1751 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1752 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1754 if (NextCommandLine
== NULL
|| OurCommandLine
== NULL
) {
1755 SHELL_FREE_NON_NULL(OurCommandLine
);
1756 SHELL_FREE_NON_NULL(NextCommandLine
);
1757 return (EFI_OUT_OF_RESOURCES
);
1758 } else if (StrStr(OurCommandLine
, L
"|") != NULL
|| Size1
== 0 || Size2
== 0) {
1759 SHELL_FREE_NON_NULL(OurCommandLine
);
1760 SHELL_FREE_NON_NULL(NextCommandLine
);
1761 return (EFI_INVALID_PARAMETER
);
1762 } else if (NextCommandLine
[0] == L
'a' &&
1763 (NextCommandLine
[1] == L
' ' || NextCommandLine
[1] == CHAR_NULL
)
1765 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1766 while (NextCommandLine
[0] == L
' ') {
1767 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1769 if (NextCommandLine
[0] == CHAR_NULL
) {
1770 SHELL_FREE_NON_NULL(OurCommandLine
);
1771 SHELL_FREE_NON_NULL(NextCommandLine
);
1772 return (EFI_INVALID_PARAMETER
);
1781 // make a SPLIT_LIST item and add to list
1783 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1784 if (Split
== NULL
) {
1785 return EFI_OUT_OF_RESOURCES
;
1787 Split
->SplitStdIn
= StdIn
;
1788 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1789 ASSERT(Split
->SplitStdOut
!= NULL
);
1790 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1792 Status
= RunCommand(OurCommandLine
);
1795 // move the output from the first to the in to the second.
1797 TempFileHandle
= Split
->SplitStdOut
;
1798 if (Split
->SplitStdIn
== StdIn
) {
1799 Split
->SplitStdOut
= NULL
;
1801 Split
->SplitStdOut
= Split
->SplitStdIn
;
1803 Split
->SplitStdIn
= TempFileHandle
;
1804 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition (Split
->SplitStdIn
, 0);
1806 if (!EFI_ERROR(Status
)) {
1807 Status
= RunCommand(NextCommandLine
);
1811 // remove the top level from the ScriptList
1813 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1814 RemoveEntryList(&Split
->Link
);
1817 // Note that the original StdIn is now the StdOut...
1819 if (Split
->SplitStdOut
!= NULL
) {
1820 ShellInfoObject
.NewEfiShellProtocol
->CloseFile (Split
->SplitStdOut
);
1822 if (Split
->SplitStdIn
!= NULL
) {
1823 ShellInfoObject
.NewEfiShellProtocol
->CloseFile (Split
->SplitStdIn
);
1827 FreePool(NextCommandLine
);
1828 FreePool(OurCommandLine
);
1834 Take the original command line, substitute any variables, free
1835 the original string, return the modified copy.
1837 @param[in] CmdLine pointer to the command line to update.
1839 @retval EFI_SUCCESS the function was successful.
1840 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1843 ShellSubstituteVariables(
1848 NewCmdLine
= ShellConvertVariables(*CmdLine
);
1849 SHELL_FREE_NON_NULL(*CmdLine
);
1850 if (NewCmdLine
== NULL
) {
1851 return (EFI_OUT_OF_RESOURCES
);
1853 *CmdLine
= NewCmdLine
;
1854 return (EFI_SUCCESS
);
1858 Take the original command line, substitute any alias in the first group of space delimited characters, free
1859 the original string, return the modified copy.
1861 @param[in] CmdLine pointer to the command line to update.
1863 @retval EFI_SUCCESS the function was successful.
1864 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1867 ShellSubstituteAliases(
1872 CHAR16
*CommandName
;
1874 UINTN PostAliasSize
;
1875 ASSERT(CmdLine
!= NULL
);
1876 ASSERT(*CmdLine
!= NULL
);
1880 if (StrStr((*CmdLine
), L
" ") == NULL
){
1881 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), 0);
1883 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), StrStr((*CmdLine
), L
" ") - (*CmdLine
));
1887 // This cannot happen 'inline' since the CmdLine can need extra space.
1890 if (!ShellCommandIsCommandOnList(CommandName
)) {
1892 // Convert via alias
1894 Status
= ShellConvertAlias(&CommandName
);
1895 if (EFI_ERROR(Status
)){
1899 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, CommandName
, 0);
1900 if (NewCmdLine
== NULL
) {
1901 SHELL_FREE_NON_NULL(CommandName
);
1902 SHELL_FREE_NON_NULL(*CmdLine
);
1903 return (EFI_OUT_OF_RESOURCES
);
1905 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, StrStr((*CmdLine
), L
" "), 0);
1906 if (NewCmdLine
== NULL
) {
1907 SHELL_FREE_NON_NULL(CommandName
);
1908 SHELL_FREE_NON_NULL(*CmdLine
);
1909 return (EFI_OUT_OF_RESOURCES
);
1912 NewCmdLine
= StrnCatGrow(&NewCmdLine
, NULL
, (*CmdLine
), 0);
1915 SHELL_FREE_NON_NULL(*CmdLine
);
1916 SHELL_FREE_NON_NULL(CommandName
);
1919 // re-assign the passed in double pointer to point to our newly allocated buffer
1921 *CmdLine
= NewCmdLine
;
1923 return (EFI_SUCCESS
);
1927 Takes the Argv[0] part of the command line and determine the meaning of it.
1929 @param[in] CmdName pointer to the command line to update.
1931 @retval Internal_Command The name is an internal command.
1932 @retval File_Sys_Change the name is a file system change.
1933 @retval Script_File_Name the name is a NSH script file.
1934 @retval Unknown_Invalid the name is unknown.
1935 @retval Efi_Application the name is an application (.EFI).
1937 SHELL_OPERATION_TYPES
1939 IN CONST CHAR16
*CmdName
1942 CHAR16
* FileWithPath
;
1943 CONST CHAR16
* TempLocation
;
1944 CONST CHAR16
* TempLocation2
;
1946 FileWithPath
= NULL
;
1948 // test for an internal command.
1950 if (ShellCommandIsCommandOnList(CmdName
)) {
1951 return (Internal_Command
);
1955 // Test for file system change request. anything ending with first : and cant have spaces.
1957 if (CmdName
[(StrLen(CmdName
)-1)] == L
':') {
1958 if ( StrStr(CmdName
, L
" ") != NULL
1959 || StrLen(StrStr(CmdName
, L
":")) > 1
1961 return (Unknown_Invalid
);
1963 return (File_Sys_Change
);
1969 if ((FileWithPath
= ShellFindFilePathEx(CmdName
, mExecutableExtensions
)) != NULL
) {
1971 // See if that file has a script file extension
1973 if (StrLen(FileWithPath
) > 4) {
1974 TempLocation
= FileWithPath
+StrLen(FileWithPath
)-4;
1975 TempLocation2
= mScriptExtension
;
1976 if (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0) {
1977 SHELL_FREE_NON_NULL(FileWithPath
);
1978 return (Script_File_Name
);
1983 // Was a file, but not a script. we treat this as an application.
1985 SHELL_FREE_NON_NULL(FileWithPath
);
1986 return (Efi_Application
);
1989 SHELL_FREE_NON_NULL(FileWithPath
);
1991 // No clue what this is... return invalid flag...
1993 return (Unknown_Invalid
);
1997 Determine if the first item in a command line is valid.
1999 @param[in] CmdLine The command line to parse.
2001 @retval EFI_SUCCESS The item is valid.
2002 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
2003 @retval EFI_NOT_FOUND The operation type is unknown or invalid.
2007 IN CONST CHAR16
*CmdLine
2011 CHAR16
*FirstParameter
;
2017 Temp
= StrnCatGrow(&Temp
, NULL
, CmdLine
, 0);
2019 return (EFI_OUT_OF_RESOURCES
);
2022 FirstParameter
= StrStr(Temp
, L
"|");
2023 if (FirstParameter
!= NULL
) {
2024 *FirstParameter
= CHAR_NULL
;
2027 FirstParameter
= NULL
;
2030 // Process the command line
2032 Status
= ProcessCommandLineToFinal(&Temp
);
2034 if (!EFI_ERROR(Status
)) {
2035 FirstParameter
= AllocateZeroPool(StrSize(CmdLine
));
2036 if (FirstParameter
== NULL
) {
2037 SHELL_FREE_NON_NULL(Temp
);
2038 return (EFI_OUT_OF_RESOURCES
);
2040 TempWalker
= (CHAR16
*)Temp
;
2041 if (!EFI_ERROR(GetNextParameter(&TempWalker
, &FirstParameter
, StrSize(CmdLine
), TRUE
))) {
2042 if (GetOperationType(FirstParameter
) == Unknown_Invalid
) {
2043 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2044 SetLastError(SHELL_NOT_FOUND
);
2045 Status
= EFI_NOT_FOUND
;
2050 SHELL_FREE_NON_NULL(Temp
);
2051 SHELL_FREE_NON_NULL(FirstParameter
);
2056 Determine if a command line contains with a split contains only valid commands.
2058 @param[in] CmdLine The command line to parse.
2060 @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.
2061 @retval EFI_ABORTED CmdLine has at least one invalid command or application.
2065 IN CONST CHAR16
*CmdLine
2068 CONST CHAR16
*TempSpot
;
2072 // If this was the only item, then get out
2074 if (!ContainsSplit(CmdLine
)) {
2075 return (EFI_SUCCESS
);
2079 // Verify up to the pipe or end character
2081 Status
= IsValidSplit(CmdLine
);
2082 if (EFI_ERROR(Status
)) {
2087 // recurse to verify the next item
2089 TempSpot
= FindFirstCharacter(CmdLine
, L
"|", L
'^') + 1;
2090 if (*TempSpot
== L
'a' &&
2091 (*(TempSpot
+ 1) == L
' ' || *(TempSpot
+ 1) == CHAR_NULL
)
2093 // If it's an ASCII pipe '|a'
2097 return (VerifySplit(TempSpot
));
2101 Process a split based operation.
2103 @param[in] CmdLine pointer to the command line to process
2105 @retval EFI_SUCCESS The operation was successful
2106 @return an error occurred.
2109 ProcessNewSplitCommandLine(
2110 IN CONST CHAR16
*CmdLine
2116 Status
= VerifySplit(CmdLine
);
2117 if (EFI_ERROR(Status
)) {
2124 // are we in an existing split???
2126 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
2127 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
2130 if (Split
== NULL
) {
2131 Status
= RunSplitCommand(CmdLine
, NULL
, NULL
);
2133 Status
= RunSplitCommand(CmdLine
, Split
->SplitStdIn
, Split
->SplitStdOut
);
2135 if (EFI_ERROR(Status
)) {
2136 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_SPLIT
), ShellInfoObject
.HiiHandle
, CmdLine
);
2142 Handle a request to change the current file system.
2144 @param[in] CmdLine The passed in command line.
2146 @retval EFI_SUCCESS The operation was successful.
2150 IN CONST CHAR16
*CmdLine
2154 Status
= EFI_SUCCESS
;
2157 // make sure we are the right operation
2159 ASSERT(CmdLine
[(StrLen(CmdLine
)-1)] == L
':' && StrStr(CmdLine
, L
" ") == NULL
);
2162 // Call the protocol API to do the work
2164 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, CmdLine
);
2167 // Report any errors
2169 if (EFI_ERROR(Status
)) {
2170 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, CmdLine
);
2177 Reprocess the command line to direct all -? to the help command.
2179 if found, will add "help" as argv[0], and move the rest later.
2181 @param[in,out] CmdLine pointer to the command line to update
2185 IN OUT CHAR16
**CmdLine
2188 CHAR16
*CurrentParameter
;
2190 CHAR16
*NewCommandLine
;
2192 UINTN NewCmdLineSize
;
2194 Status
= EFI_SUCCESS
;
2196 CurrentParameter
= AllocateZeroPool(StrSize(*CmdLine
));
2197 if (CurrentParameter
== NULL
) {
2198 return (EFI_OUT_OF_RESOURCES
);
2202 while(Walker
!= NULL
&& *Walker
!= CHAR_NULL
) {
2203 if (!EFI_ERROR(GetNextParameter(&Walker
, &CurrentParameter
, StrSize(*CmdLine
), TRUE
))) {
2204 if (StrStr(CurrentParameter
, L
"-?") == CurrentParameter
) {
2205 CurrentParameter
[0] = L
' ';
2206 CurrentParameter
[1] = L
' ';
2207 NewCmdLineSize
= StrSize(L
"help ") + StrSize(*CmdLine
);
2208 NewCommandLine
= AllocateZeroPool(NewCmdLineSize
);
2209 if (NewCommandLine
== NULL
) {
2210 Status
= EFI_OUT_OF_RESOURCES
;
2215 // We know the space is sufficient since we just calculated it.
2217 StrnCpyS(NewCommandLine
, NewCmdLineSize
/sizeof(CHAR16
), L
"help ", 5);
2218 StrnCatS(NewCommandLine
, NewCmdLineSize
/sizeof(CHAR16
), *CmdLine
, StrLen(*CmdLine
));
2219 SHELL_FREE_NON_NULL(*CmdLine
);
2220 *CmdLine
= NewCommandLine
;
2226 SHELL_FREE_NON_NULL(CurrentParameter
);
2232 Function to update the shell variable "lasterror".
2234 @param[in] ErrorCode the error code to put into lasterror.
2238 IN CONST SHELL_STATUS ErrorCode
2241 CHAR16 LeString
[19];
2242 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
2243 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ErrorCode
);
2245 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", ErrorCode
);
2247 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
2248 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
2250 return (EFI_SUCCESS
);
2254 Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.
2256 @param[in,out] CmdLine pointer to the command line to update
2258 @retval EFI_SUCCESS The operation was successful
2259 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
2260 @return some other error occurred
2263 ProcessCommandLineToFinal(
2264 IN OUT CHAR16
**CmdLine
2268 TrimSpaces(CmdLine
);
2270 Status
= ShellSubstituteAliases(CmdLine
);
2271 if (EFI_ERROR(Status
)) {
2275 TrimSpaces(CmdLine
);
2277 Status
= ShellSubstituteVariables(CmdLine
);
2278 if (EFI_ERROR(Status
)) {
2281 ASSERT (*CmdLine
!= NULL
);
2283 TrimSpaces(CmdLine
);
2286 // update for help parsing
2288 if (StrStr(*CmdLine
, L
"?") != NULL
) {
2290 // This may do nothing if the ? does not indicate help.
2291 // Save all the details for in the API below.
2293 Status
= DoHelpUpdate(CmdLine
);
2296 TrimSpaces(CmdLine
);
2298 return (EFI_SUCCESS
);
2302 Run an internal shell command.
2304 This API will update the shell's environment since these commands are libraries.
2306 @param[in] CmdLine the command line to run.
2307 @param[in] FirstParameter the first parameter on the command line
2308 @param[in] ParamProtocol the shell parameters protocol pointer
2309 @param[out] CommandStatus the status from the command line.
2311 @retval EFI_SUCCESS The command was completed.
2312 @retval EFI_ABORTED The command's operation was aborted.
2316 IN CONST CHAR16
*CmdLine
,
2317 IN CHAR16
*FirstParameter
,
2318 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2319 OUT EFI_STATUS
*CommandStatus
2325 SHELL_STATUS CommandReturnedStatus
;
2330 NewCmdLine
= AllocateCopyPool (StrSize (CmdLine
), CmdLine
);
2331 if (NewCmdLine
== NULL
) {
2332 return EFI_OUT_OF_RESOURCES
;
2335 for (Walker
= NewCmdLine
; Walker
!= NULL
&& *Walker
!= CHAR_NULL
; Walker
++) {
2336 if (*Walker
== L
'^' && *(Walker
+1) == L
'#') {
2337 CopyMem(Walker
, Walker
+1, StrSize(Walker
) - sizeof(Walker
[0]));
2342 // get the argc and argv updated for internal commands
2344 Status
= UpdateArgcArgv(ParamProtocol
, NewCmdLine
, Internal_Command
, &Argv
, &Argc
);
2345 if (!EFI_ERROR(Status
)) {
2347 // Run the internal command.
2349 Status
= ShellCommandRunCommandHandler(FirstParameter
, &CommandReturnedStatus
, &LastError
);
2351 if (!EFI_ERROR(Status
)) {
2352 if (CommandStatus
!= NULL
) {
2353 if (CommandReturnedStatus
!= SHELL_SUCCESS
) {
2354 *CommandStatus
= (EFI_STATUS
)(CommandReturnedStatus
| MAX_BIT
);
2356 *CommandStatus
= EFI_SUCCESS
;
2361 // Update last error status.
2362 // some commands do not update last error.
2365 SetLastError(CommandReturnedStatus
);
2369 // Pass thru the exitcode from the app.
2371 if (ShellCommandGetExit()) {
2373 // An Exit was requested ("exit" command), pass its value up.
2375 Status
= CommandReturnedStatus
;
2376 } else if (CommandReturnedStatus
!= SHELL_SUCCESS
&& IsScriptOnlyCommand(FirstParameter
)) {
2378 // Always abort when a script only command fails for any reason
2380 Status
= EFI_ABORTED
;
2381 } else if (ShellCommandGetCurrentScriptFile() != NULL
&& CommandReturnedStatus
== SHELL_ABORTED
) {
2383 // Abort when in a script and a command aborted
2385 Status
= EFI_ABORTED
;
2391 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
2392 // This is safe even if the update API failed. In this case, it may be a no-op.
2394 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);
2397 // If a script is running and the command is not a script only command, then
2398 // change return value to success so the script won't halt (unless aborted).
2400 // Script only commands have to be able halt the script since the script will
2401 // not operate if they are failing.
2403 if ( ShellCommandGetCurrentScriptFile() != NULL
2404 && !IsScriptOnlyCommand(FirstParameter
)
2405 && Status
!= EFI_ABORTED
2407 Status
= EFI_SUCCESS
;
2410 FreePool (NewCmdLine
);
2415 Function to run the command or file.
2417 @param[in] Type the type of operation being run.
2418 @param[in] CmdLine the command line to run.
2419 @param[in] FirstParameter the first parameter on the command line
2420 @param[in] ParamProtocol the shell parameters protocol pointer
2421 @param[out] CommandStatus the status from the command line.
2423 @retval EFI_SUCCESS The command was completed.
2424 @retval EFI_ABORTED The command's operation was aborted.
2428 IN SHELL_OPERATION_TYPES Type
,
2429 IN CONST CHAR16
*CmdLine
,
2430 IN CHAR16
*FirstParameter
,
2431 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2432 OUT EFI_STATUS
*CommandStatus
2436 EFI_STATUS StartStatus
;
2437 CHAR16
*CommandWithPath
;
2438 CHAR16
*FullCommandWithPath
;
2439 EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
2440 SHELL_STATUS CalleeExitStatus
;
2442 Status
= EFI_SUCCESS
;
2443 CommandWithPath
= NULL
;
2445 CalleeExitStatus
= SHELL_INVALID_PARAMETER
;
2448 case Internal_Command
:
2449 Status
= RunInternalCommand(CmdLine
, FirstParameter
, ParamProtocol
, CommandStatus
);
2451 case Script_File_Name
:
2452 case Efi_Application
:
2454 // Process a fully qualified path
2456 if (StrStr(FirstParameter
, L
":") != NULL
) {
2457 ASSERT (CommandWithPath
== NULL
);
2458 if (ShellIsFile(FirstParameter
) == EFI_SUCCESS
) {
2459 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, FirstParameter
, 0);
2464 // Process a relative path and also check in the path environment variable
2466 if (CommandWithPath
== NULL
) {
2467 CommandWithPath
= ShellFindFilePathEx(FirstParameter
, mExecutableExtensions
);
2471 // This should be impossible now.
2473 ASSERT(CommandWithPath
!= NULL
);
2476 // Make sure that path is not just a directory (or not found)
2478 if (!EFI_ERROR(ShellIsDirectory(CommandWithPath
))) {
2479 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2480 SetLastError(SHELL_NOT_FOUND
);
2483 case Script_File_Name
:
2484 FullCommandWithPath
= FullyQualifyPath(CommandWithPath
);
2485 if (FullCommandWithPath
== NULL
) {
2486 Status
= RunScriptFile (CommandWithPath
, NULL
, CmdLine
, ParamProtocol
);
2488 Status
= RunScriptFile (FullCommandWithPath
, NULL
, CmdLine
, ParamProtocol
);
2489 FreePool(FullCommandWithPath
);
2492 case Efi_Application
:
2494 // Get the device path of the application image
2496 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
2497 if (DevPath
== NULL
){
2498 Status
= EFI_OUT_OF_RESOURCES
;
2503 // Execute the device path
2505 Status
= InternalShellExecuteDevicePath(
2513 SHELL_FREE_NON_NULL(DevPath
);
2515 if(EFI_ERROR (Status
)) {
2516 CalleeExitStatus
= (SHELL_STATUS
) (Status
& (~MAX_BIT
));
2518 CalleeExitStatus
= (SHELL_STATUS
) StartStatus
;
2521 if (CommandStatus
!= NULL
) {
2522 *CommandStatus
= CalleeExitStatus
;
2526 // Update last error status.
2528 // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
2529 SetLastError(CalleeExitStatus
);
2545 SHELL_FREE_NON_NULL(CommandWithPath
);
2551 Function to setup StdIn, StdErr, StdOut, and then run the command or file.
2553 @param[in] Type the type of operation being run.
2554 @param[in] CmdLine the command line to run.
2555 @param[in] FirstParameter the first parameter on the command line.
2556 @param[in] ParamProtocol the shell parameters protocol pointer
2557 @param[out] CommandStatus the status from the command line.
2559 @retval EFI_SUCCESS The command was completed.
2560 @retval EFI_ABORTED The command's operation was aborted.
2563 SetupAndRunCommandOrFile(
2564 IN SHELL_OPERATION_TYPES Type
,
2566 IN CHAR16
*FirstParameter
,
2567 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2568 OUT EFI_STATUS
*CommandStatus
2572 SHELL_FILE_HANDLE OriginalStdIn
;
2573 SHELL_FILE_HANDLE OriginalStdOut
;
2574 SHELL_FILE_HANDLE OriginalStdErr
;
2575 SYSTEM_TABLE_INFO OriginalSystemTableInfo
;
2576 CONST SCRIPT_FILE
*ConstScriptFile
;
2579 // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
2581 Status
= UpdateStdInStdOutStdErr(ParamProtocol
, CmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2584 // The StdIn, StdOut, and StdErr are set up.
2585 // Now run the command, script, or application
2587 if (!EFI_ERROR(Status
)) {
2588 TrimSpaces(&CmdLine
);
2589 Status
= RunCommandOrFile(Type
, CmdLine
, FirstParameter
, ParamProtocol
, CommandStatus
);
2595 if (EFI_ERROR(Status
)) {
2596 ConstScriptFile
= ShellCommandGetCurrentScriptFile();
2597 if (ConstScriptFile
== NULL
|| ConstScriptFile
->CurrentCommand
== NULL
) {
2598 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
));
2600 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR_SCRIPT
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
), ConstScriptFile
->CurrentCommand
->Line
);
2605 // put back the original StdIn, StdOut, and StdErr
2607 RestoreStdInStdOutStdErr(ParamProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2613 Function will process and run a command line.
2615 This will determine if the command line represents an internal shell
2616 command or dispatch an external application.
2618 @param[in] CmdLine The command line to parse.
2619 @param[out] CommandStatus The status from the command line.
2621 @retval EFI_SUCCESS The command was completed.
2622 @retval EFI_ABORTED The command's operation was aborted.
2626 IN CONST CHAR16
*CmdLine
,
2627 OUT EFI_STATUS
*CommandStatus
2631 CHAR16
*CleanOriginal
;
2632 CHAR16
*FirstParameter
;
2634 SHELL_OPERATION_TYPES Type
;
2635 CONST CHAR16
*CurDir
;
2637 ASSERT(CmdLine
!= NULL
);
2638 if (StrLen(CmdLine
) == 0) {
2639 return (EFI_SUCCESS
);
2642 Status
= EFI_SUCCESS
;
2643 CleanOriginal
= NULL
;
2645 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
2646 if (CleanOriginal
== NULL
) {
2647 return (EFI_OUT_OF_RESOURCES
);
2650 TrimSpaces(&CleanOriginal
);
2653 // NULL out comments (leveraged from RunScriptFileHandle() ).
2654 // The # character on a line is used to denote that all characters on the same line
2655 // and to the right of the # are to be ignored by the shell.
2656 // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.
2658 for (TempWalker
= CleanOriginal
; TempWalker
!= NULL
&& *TempWalker
!= CHAR_NULL
; TempWalker
++) {
2659 if (*TempWalker
== L
'^') {
2660 if (*(TempWalker
+ 1) == L
'#') {
2663 } else if (*TempWalker
== L
'#') {
2664 *TempWalker
= CHAR_NULL
;
2668 TrimSpaces(&CleanOriginal
);
2671 // Handle case that passed in command line is just 1 or more " " characters.
2673 if (StrLen (CleanOriginal
) == 0) {
2674 SHELL_FREE_NON_NULL(CleanOriginal
);
2675 return (EFI_SUCCESS
);
2678 Status
= ProcessCommandLineToFinal(&CleanOriginal
);
2679 if (EFI_ERROR(Status
)) {
2680 SHELL_FREE_NON_NULL(CleanOriginal
);
2685 // We don't do normal processing with a split command line (output from one command input to another)
2687 if (ContainsSplit(CleanOriginal
)) {
2688 Status
= ProcessNewSplitCommandLine(CleanOriginal
);
2689 SHELL_FREE_NON_NULL(CleanOriginal
);
2694 // We need the first parameter information so we can determine the operation type
2696 FirstParameter
= AllocateZeroPool(StrSize(CleanOriginal
));
2697 if (FirstParameter
== NULL
) {
2698 SHELL_FREE_NON_NULL(CleanOriginal
);
2699 return (EFI_OUT_OF_RESOURCES
);
2701 TempWalker
= CleanOriginal
;
2702 if (!EFI_ERROR(GetNextParameter(&TempWalker
, &FirstParameter
, StrSize(CleanOriginal
), TRUE
))) {
2704 // Depending on the first parameter we change the behavior
2706 switch (Type
= GetOperationType(FirstParameter
)) {
2707 case File_Sys_Change
:
2708 Status
= ChangeMappedDrive (FirstParameter
);
2710 case Internal_Command
:
2711 case Script_File_Name
:
2712 case Efi_Application
:
2713 Status
= SetupAndRunCommandOrFile(Type
, CleanOriginal
, FirstParameter
, ShellInfoObject
.NewShellParametersProtocol
, CommandStatus
);
2717 // Whatever was typed, it was invalid.
2719 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2720 SetLastError(SHELL_NOT_FOUND
);
2724 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2725 SetLastError(SHELL_NOT_FOUND
);
2728 // Check whether the current file system still exists. If not exist, we need update "cwd" and gShellCurMapping.
2730 CurDir
= EfiShellGetCurDir (NULL
);
2731 if (CurDir
!= NULL
) {
2732 if (EFI_ERROR(ShellFileExists (CurDir
))) {
2734 // EfiShellSetCurDir() cannot set current directory to NULL.
2735 // EfiShellSetEnv() is not allowed to set the "cwd" variable.
2736 // Only InternalEfiShellSetEnv () is allowed setting the "cwd" variable.
2738 InternalEfiShellSetEnv (L
"cwd", NULL
, TRUE
);
2739 gShellCurMapping
= NULL
;
2743 SHELL_FREE_NON_NULL(CleanOriginal
);
2744 SHELL_FREE_NON_NULL(FirstParameter
);
2750 Function will process and run a command line.
2752 This will determine if the command line represents an internal shell
2753 command or dispatch an external application.
2755 @param[in] CmdLine The command line to parse.
2757 @retval EFI_SUCCESS The command was completed.
2758 @retval EFI_ABORTED The command's operation was aborted.
2762 IN CONST CHAR16
*CmdLine
2765 return (RunShellCommand(CmdLine
, NULL
));
2769 Function to process a NSH script file via SHELL_FILE_HANDLE.
2771 @param[in] Handle The handle to the already opened file.
2772 @param[in] Name The name of the script file.
2774 @retval EFI_SUCCESS the script completed successfully
2777 RunScriptFileHandle (
2778 IN SHELL_FILE_HANDLE Handle
,
2779 IN CONST CHAR16
*Name
2783 SCRIPT_FILE
*NewScriptFile
;
2785 UINTN PrintBuffSize
;
2786 CHAR16
*CommandLine
;
2787 CHAR16
*CommandLine2
;
2788 CHAR16
*CommandLine3
;
2789 SCRIPT_COMMAND_LIST
*LastCommand
;
2791 BOOLEAN PreScriptEchoState
;
2792 BOOLEAN PreCommandEchoState
;
2793 CONST CHAR16
*CurDir
;
2795 CHAR16 LeString
[50];
2796 LIST_ENTRY OldBufferList
;
2798 ASSERT(!ShellCommandGetScriptExit());
2800 PreScriptEchoState
= ShellCommandGetEchoState();
2801 PrintBuffSize
= PcdGet16(PcdShellPrintBufferSize
);
2803 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
2804 if (NewScriptFile
== NULL
) {
2805 return (EFI_OUT_OF_RESOURCES
);
2811 ASSERT(NewScriptFile
->ScriptName
== NULL
);
2812 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
2813 if (NewScriptFile
->ScriptName
== NULL
) {
2814 DeleteScriptFileStruct(NewScriptFile
);
2815 return (EFI_OUT_OF_RESOURCES
);
2819 // Save the parameters (used to replace %0 to %9 later on)
2821 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
2822 if (NewScriptFile
->Argc
!= 0) {
2823 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
2824 if (NewScriptFile
->Argv
== NULL
) {
2825 DeleteScriptFileStruct(NewScriptFile
);
2826 return (EFI_OUT_OF_RESOURCES
);
2829 // Put the full path of the script file into Argv[0] as required by section
2830 // 3.6.2 of version 2.2 of the shell specification.
2832 NewScriptFile
->Argv
[0] = StrnCatGrow(&NewScriptFile
->Argv
[0], NULL
, NewScriptFile
->ScriptName
, 0);
2833 for (LoopVar
= 1 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
2834 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
2835 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
2836 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
2837 DeleteScriptFileStruct(NewScriptFile
);
2838 return (EFI_OUT_OF_RESOURCES
);
2842 NewScriptFile
->Argv
= NULL
;
2845 InitializeListHead(&NewScriptFile
->CommandList
);
2846 InitializeListHead(&NewScriptFile
->SubstList
);
2849 // Now build the list of all script commands.
2852 while(!ShellFileHandleEof(Handle
)) {
2853 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
2855 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0 || CommandLine
[0] == '#') {
2856 SHELL_FREE_NON_NULL(CommandLine
);
2859 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
2860 if (NewScriptFile
->CurrentCommand
== NULL
) {
2861 SHELL_FREE_NON_NULL(CommandLine
);
2862 DeleteScriptFileStruct(NewScriptFile
);
2863 return (EFI_OUT_OF_RESOURCES
);
2866 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
2867 NewScriptFile
->CurrentCommand
->Data
= NULL
;
2868 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
2870 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2874 // Add this as the topmost script file
2876 ShellCommandSetNewScript (NewScriptFile
);
2879 // Now enumerate through the commands and run each one.
2881 CommandLine
= AllocateZeroPool(PrintBuffSize
);
2882 if (CommandLine
== NULL
) {
2883 DeleteScriptFileStruct(NewScriptFile
);
2884 return (EFI_OUT_OF_RESOURCES
);
2886 CommandLine2
= AllocateZeroPool(PrintBuffSize
);
2887 if (CommandLine2
== NULL
) {
2888 FreePool(CommandLine
);
2889 DeleteScriptFileStruct(NewScriptFile
);
2890 return (EFI_OUT_OF_RESOURCES
);
2893 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
2894 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
2895 ; // conditional increment in the body of the loop
2897 ASSERT(CommandLine2
!= NULL
);
2898 StrnCpyS( CommandLine2
,
2899 PrintBuffSize
/sizeof(CHAR16
),
2900 NewScriptFile
->CurrentCommand
->Cl
,
2901 PrintBuffSize
/sizeof(CHAR16
) - 1
2904 SaveBufferList(&OldBufferList
);
2907 // NULL out comments
2909 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
2910 if (*CommandLine3
== L
'^') {
2911 if ( *(CommandLine3
+1) == L
':') {
2912 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
2913 } else if (*(CommandLine3
+1) == L
'#') {
2916 } else if (*CommandLine3
== L
'#') {
2917 *CommandLine3
= CHAR_NULL
;
2921 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
2923 // Due to variability in starting the find and replace action we need to have both buffers the same.
2925 StrnCpyS( CommandLine
,
2926 PrintBuffSize
/sizeof(CHAR16
),
2928 PrintBuffSize
/sizeof(CHAR16
) - 1
2932 // Remove the %0 to %9 from the command line (if we have some arguments)
2934 if (NewScriptFile
->Argv
!= NULL
) {
2935 switch (NewScriptFile
->Argc
) {
2937 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%9", NewScriptFile
->Argv
[9], FALSE
, FALSE
);
2938 ASSERT_EFI_ERROR(Status
);
2940 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%8", NewScriptFile
->Argv
[8], FALSE
, FALSE
);
2941 ASSERT_EFI_ERROR(Status
);
2943 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%7", NewScriptFile
->Argv
[7], FALSE
, FALSE
);
2944 ASSERT_EFI_ERROR(Status
);
2946 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%6", NewScriptFile
->Argv
[6], FALSE
, FALSE
);
2947 ASSERT_EFI_ERROR(Status
);
2949 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%5", NewScriptFile
->Argv
[5], FALSE
, FALSE
);
2950 ASSERT_EFI_ERROR(Status
);
2952 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%4", NewScriptFile
->Argv
[4], FALSE
, FALSE
);
2953 ASSERT_EFI_ERROR(Status
);
2955 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%3", NewScriptFile
->Argv
[3], FALSE
, FALSE
);
2956 ASSERT_EFI_ERROR(Status
);
2958 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%2", NewScriptFile
->Argv
[2], FALSE
, FALSE
);
2959 ASSERT_EFI_ERROR(Status
);
2961 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%1", NewScriptFile
->Argv
[1], FALSE
, FALSE
);
2962 ASSERT_EFI_ERROR(Status
);
2964 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%0", NewScriptFile
->Argv
[0], FALSE
, FALSE
);
2965 ASSERT_EFI_ERROR(Status
);
2971 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%1", L
"\"\"", FALSE
, FALSE
);
2972 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%2", L
"\"\"", FALSE
, FALSE
);
2973 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%3", L
"\"\"", FALSE
, FALSE
);
2974 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%4", L
"\"\"", FALSE
, FALSE
);
2975 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%5", L
"\"\"", FALSE
, FALSE
);
2976 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%6", L
"\"\"", FALSE
, FALSE
);
2977 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%7", L
"\"\"", FALSE
, FALSE
);
2978 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%8", L
"\"\"", FALSE
, FALSE
);
2979 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%9", L
"\"\"", FALSE
, FALSE
);
2981 StrnCpyS( CommandLine2
,
2982 PrintBuffSize
/sizeof(CHAR16
),
2984 PrintBuffSize
/sizeof(CHAR16
) - 1
2987 LastCommand
= NewScriptFile
->CurrentCommand
;
2989 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
2991 if (CommandLine3
!= NULL
&& CommandLine3
[0] == L
':' ) {
2993 // This line is a goto target / label
2996 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
2997 if (CommandLine3
[0] == L
'@') {
2999 // We need to save the current echo state
3000 // and disable echo for just this command.
3002 PreCommandEchoState
= ShellCommandGetEchoState();
3003 ShellCommandSetEchoState(FALSE
);
3004 Status
= RunCommand(CommandLine3
+1);
3007 // If command was "@echo -off" or "@echo -on" then don't restore echo state
3009 if (StrCmp (L
"@echo -off", CommandLine3
) != 0 &&
3010 StrCmp (L
"@echo -on", CommandLine3
) != 0) {
3012 // Now restore the pre-'@' echo state.
3014 ShellCommandSetEchoState(PreCommandEchoState
);
3017 if (ShellCommandGetEchoState()) {
3018 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
3019 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
3020 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
3022 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
3024 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
3026 Status
= RunCommand(CommandLine3
);
3030 if (ShellCommandGetScriptExit()) {
3032 // ShellCommandGetExitCode() always returns a UINT64
3034 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ShellCommandGetExitCode());
3035 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
3036 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
3038 ShellCommandRegisterExit(FALSE
, 0);
3039 Status
= EFI_SUCCESS
;
3040 RestoreBufferList(&OldBufferList
);
3043 if (ShellGetExecutionBreakFlag()) {
3044 RestoreBufferList(&OldBufferList
);
3047 if (EFI_ERROR(Status
)) {
3048 RestoreBufferList(&OldBufferList
);
3051 if (ShellCommandGetExit()) {
3052 RestoreBufferList(&OldBufferList
);
3057 // If that commend did not update the CurrentCommand then we need to advance it...
3059 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
3060 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
3061 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
3062 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
3066 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
3067 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
3068 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
3071 RestoreBufferList(&OldBufferList
);
3075 FreePool(CommandLine
);
3076 FreePool(CommandLine2
);
3077 ShellCommandSetNewScript (NULL
);
3080 // Only if this was the last script reset the state.
3082 if (ShellCommandGetCurrentScriptFile()==NULL
) {
3083 ShellCommandSetEchoState(PreScriptEchoState
);
3085 return (EFI_SUCCESS
);
3089 Function to process a NSH script file.
3091 @param[in] ScriptPath Pointer to the script file name (including file system path).
3092 @param[in] Handle the handle of the script file already opened.
3093 @param[in] CmdLine the command line to run.
3094 @param[in] ParamProtocol the shell parameters protocol pointer
3096 @retval EFI_SUCCESS the script completed successfully
3100 IN CONST CHAR16
*ScriptPath
,
3101 IN SHELL_FILE_HANDLE Handle OPTIONAL
,
3102 IN CONST CHAR16
*CmdLine
,
3103 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
3107 SHELL_FILE_HANDLE FileHandle
;
3111 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
3112 return (EFI_INVALID_PARAMETER
);
3116 // get the argc and argv updated for scripts
3118 Status
= UpdateArgcArgv(ParamProtocol
, CmdLine
, Script_File_Name
, &Argv
, &Argc
);
3119 if (!EFI_ERROR(Status
)) {
3121 if (Handle
== NULL
) {
3125 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
3126 if (!EFI_ERROR(Status
)) {
3130 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
);
3133 // now close the file
3135 ShellCloseFile(&FileHandle
);
3138 Status
= RunScriptFileHandle(Handle
, ScriptPath
);
3143 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
3144 // This is safe even if the update API failed. In this case, it may be a no-op.
3146 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);
3152 Return the pointer to the first occurrence of any character from a list of characters.
3154 @param[in] String the string to parse
3155 @param[in] CharacterList the list of character to look for
3156 @param[in] EscapeCharacter An escape character to skip
3158 @return the location of the first character in the string
3159 @retval CHAR_NULL no instance of any character in CharacterList was found in String
3163 IN CONST CHAR16
*String
,
3164 IN CONST CHAR16
*CharacterList
,
3165 IN CONST CHAR16 EscapeCharacter
3171 for (WalkStr
= 0; WalkStr
< StrLen(String
); WalkStr
++) {
3172 if (String
[WalkStr
] == EscapeCharacter
) {
3176 for (WalkChar
= 0; WalkChar
< StrLen(CharacterList
); WalkChar
++) {
3177 if (String
[WalkStr
] == CharacterList
[WalkChar
]) {
3178 return (&String
[WalkStr
]);
3182 return (String
+ StrLen(String
));