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
= {
59 { NULL
,NULL
}, NULL
, NULL
62 { NULL
,NULL
}, NULL
, NULL
75 STATIC CONST CHAR16 mScriptExtension
[] = L
".NSH";
76 STATIC CONST CHAR16 mExecutableExtensions
[] = L
".NSH;.EFI";
77 STATIC CONST CHAR16 mStartupScript
[] = L
"startup.nsh";
78 CONST CHAR16 mNoNestingEnvVarName
[] = L
"nonesting";
79 CONST CHAR16 mNoNestingTrue
[] = L
"True";
80 CONST CHAR16 mNoNestingFalse
[] = L
"False";
83 Cleans off leading and trailing spaces and tabs.
85 @param[in] String pointer to the string to trim them off.
92 ASSERT (String
!= NULL
);
93 ASSERT (*String
!= NULL
);
95 // Remove any spaces and tabs at the beginning of the (*String).
97 while (((*String
)[0] == L
' ') || ((*String
)[0] == L
'\t')) {
98 CopyMem ((*String
), (*String
)+1, StrSize ((*String
)) - sizeof ((*String
)[0]));
102 // Remove any spaces and tabs at the end of the (*String).
104 while ((StrLen (*String
) > 0) && (((*String
)[StrLen ((*String
))-1] == L
' ') || ((*String
)[StrLen ((*String
))-1] == L
'\t'))) {
105 (*String
)[StrLen ((*String
))-1] = CHAR_NULL
;
108 return (EFI_SUCCESS
);
112 Parse for the next instance of one string within another string. Can optionally make sure that
113 the string was not escaped (^ character) per the shell specification.
115 @param[in] SourceString The string to search within
116 @param[in] FindString The string to look for
117 @param[in] CheckForEscapeCharacter TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances
121 IN CONST CHAR16
*SourceString
,
122 IN CONST CHAR16
*FindString
,
123 IN CONST BOOLEAN CheckForEscapeCharacter
128 if (SourceString
== NULL
) {
132 Temp
= StrStr (SourceString
, FindString
);
135 // If nothing found, or we don't care about escape characters
137 if ((Temp
== NULL
) || !CheckForEscapeCharacter
) {
142 // If we found an escaped character, try again on the remainder of the string
144 if ((Temp
> (SourceString
)) && (*(Temp
-1) == L
'^')) {
145 return FindNextInstance (Temp
+1, FindString
, CheckForEscapeCharacter
);
149 // we found the right character
155 Check whether the string between a pair of % is a valid environment variable name.
157 @param[in] BeginPercent pointer to the first percent.
158 @param[in] EndPercent pointer to the last percent.
160 @retval TRUE is a valid environment variable name.
161 @retval FALSE is NOT a valid environment variable name.
164 IsValidEnvironmentVariableName (
165 IN CONST CHAR16
*BeginPercent
,
166 IN CONST CHAR16
*EndPercent
169 CONST CHAR16
*Walker
;
173 ASSERT (BeginPercent
!= NULL
);
174 ASSERT (EndPercent
!= NULL
);
175 ASSERT (BeginPercent
< EndPercent
);
177 if ((BeginPercent
+ 1) == EndPercent
) {
181 for (Walker
= BeginPercent
+ 1; Walker
< EndPercent
; Walker
++) {
183 ((*Walker
>= L
'0') && (*Walker
<= L
'9')) ||
184 ((*Walker
>= L
'A') && (*Walker
<= L
'Z')) ||
185 ((*Walker
>= L
'a') && (*Walker
<= L
'z')) ||
189 if ((Walker
== BeginPercent
+ 1) && ((*Walker
>= L
'0') && (*Walker
<= L
'9'))) {
203 Determine if a command line contains a split operation
205 @param[in] CmdLine The command line to parse.
207 @retval TRUE CmdLine has a valid split.
208 @retval FALSE CmdLine does not have a valid split.
212 IN CONST CHAR16
*CmdLine
215 CONST CHAR16
*TempSpot
;
216 CONST CHAR16
*FirstQuote
;
217 CONST CHAR16
*SecondQuote
;
219 FirstQuote
= FindNextInstance (CmdLine
, L
"\"", TRUE
);
221 TempSpot
= FindFirstCharacter (CmdLine
, L
"|", L
'^');
223 if ((FirstQuote
== NULL
) ||
224 (TempSpot
== NULL
) ||
225 (TempSpot
== CHAR_NULL
) ||
226 (FirstQuote
> TempSpot
)
229 return (BOOLEAN
)((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
));
232 while ((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
)) {
233 if ((FirstQuote
== NULL
) || (FirstQuote
> TempSpot
)) {
237 SecondQuote
= FindNextInstance (FirstQuote
+ 1, L
"\"", TRUE
);
238 if (SecondQuote
== NULL
) {
242 if (SecondQuote
< TempSpot
) {
243 FirstQuote
= FindNextInstance (SecondQuote
+ 1, L
"\"", TRUE
);
246 FirstQuote
= FindNextInstance (SecondQuote
+ 1, L
"\"", TRUE
);
247 TempSpot
= FindFirstCharacter (TempSpot
+ 1, L
"|", L
'^');
252 return (BOOLEAN
)((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
));
256 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
257 feature's enabled state was not known when the shell initially launched.
259 @retval EFI_SUCCESS The feature is enabled.
260 @retval EFI_OUT_OF_RESOURCES There is not enough memory available.
263 InternalEfiShellStartCtrlSMonitor (
267 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
*SimpleEx
;
268 EFI_KEY_DATA KeyData
;
271 Status
= gBS
->OpenProtocol (
272 gST
->ConsoleInHandle
,
273 &gEfiSimpleTextInputExProtocolGuid
,
277 EFI_OPEN_PROTOCOL_GET_PROTOCOL
279 if (EFI_ERROR (Status
)) {
284 STRING_TOKEN (STR_SHELL_NO_IN_EX
),
285 ShellInfoObject
.HiiHandle
287 return (EFI_SUCCESS
);
290 KeyData
.KeyState
.KeyToggleState
= 0;
291 KeyData
.Key
.ScanCode
= 0;
292 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
293 KeyData
.Key
.UnicodeChar
= L
's';
295 Status
= SimpleEx
->RegisterKeyNotify (
298 NotificationFunction
,
299 &ShellInfoObject
.CtrlSNotifyHandle1
302 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
303 if (!EFI_ERROR (Status
)) {
304 Status
= SimpleEx
->RegisterKeyNotify (
307 NotificationFunction
,
308 &ShellInfoObject
.CtrlSNotifyHandle2
312 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
313 KeyData
.Key
.UnicodeChar
= 19;
315 if (!EFI_ERROR (Status
)) {
316 Status
= SimpleEx
->RegisterKeyNotify (
319 NotificationFunction
,
320 &ShellInfoObject
.CtrlSNotifyHandle3
324 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
325 if (!EFI_ERROR (Status
)) {
326 Status
= SimpleEx
->RegisterKeyNotify (
329 NotificationFunction
,
330 &ShellInfoObject
.CtrlSNotifyHandle4
338 The entry point for the application.
340 @param[in] ImageHandle The firmware allocated handle for the EFI image.
341 @param[in] SystemTable A pointer to the EFI System Table.
343 @retval EFI_SUCCESS The entry point is executed successfully.
344 @retval other Some error occurs when executing this entry point.
350 IN EFI_HANDLE ImageHandle
,
351 IN EFI_SYSTEM_TABLE
*SystemTable
357 EFI_HANDLE ConInHandle
;
358 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*OldConIn
;
361 if (PcdGet8 (PcdShellSupportLevel
) > 3) {
362 return (EFI_UNSUPPORTED
);
368 Status
= gST
->ConOut
->ClearScreen (gST
->ConOut
);
369 if (EFI_ERROR (Status
)) {
374 // Populate the global structure from PCDs
376 ShellInfoObject
.ImageDevPath
= NULL
;
377 ShellInfoObject
.FileDevPath
= NULL
;
378 ShellInfoObject
.PageBreakEnabled
= PcdGetBool (PcdShellPageBreakDefault
);
379 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool (PcdShellInsertModeDefault
);
380 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
383 // verify we dont allow for spec violation
385 ASSERT (ShellInfoObject
.LogScreenCount
>= 3);
388 // Initialize the LIST ENTRY objects...
390 InitializeListHead (&ShellInfoObject
.BufferToFreeList
.Link
);
391 InitializeListHead (&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
392 InitializeListHead (&ShellInfoObject
.SplitList
.Link
);
395 // Check PCDs for optional features that are not implemented yet.
397 if ( PcdGetBool (PcdShellSupportOldProtocols
)
398 || !FeaturePcdGet (PcdShellRequireHiiPlatform
)
399 || FeaturePcdGet (PcdShellSupportFrameworkHii
)
402 return (EFI_UNSUPPORTED
);
406 // turn off the watchdog timer
408 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
411 // install our console logger. This will keep a log of the output for back-browsing
413 Status
= ConsoleLoggerInstall (ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
414 if (!EFI_ERROR (Status
)) {
416 // Enable the cursor to be visible
418 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
421 // If supporting EFI 1.1 we need to install HII protocol
422 // only do this if PcdShellRequireHiiPlatform == FALSE
424 // remove EFI_UNSUPPORTED check above when complete.
425 /// @todo add support for Framework HII
428 // install our (solitary) HII package
430 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
431 if (ShellInfoObject
.HiiHandle
== NULL
) {
432 if (PcdGetBool (PcdShellSupportFrameworkHii
)) {
433 /// @todo Add our package into Framework HII
436 if (ShellInfoObject
.HiiHandle
== NULL
) {
437 Status
= EFI_NOT_STARTED
;
443 // create and install the EfiShellParametersProtocol
445 Status
= CreatePopulateInstallShellParametersProtocol (&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
446 ASSERT_EFI_ERROR (Status
);
447 ASSERT (ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
450 // create and install the EfiShellProtocol
452 Status
= CreatePopulateInstallShellProtocol (&ShellInfoObject
.NewEfiShellProtocol
);
453 ASSERT_EFI_ERROR (Status
);
454 ASSERT (ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
457 // Now initialize the shell library (it requires Shell Parameters protocol)
459 Status
= ShellInitialize ();
460 ASSERT_EFI_ERROR (Status
);
462 Status
= CommandInit ();
463 ASSERT_EFI_ERROR (Status
);
465 Status
= ShellInitEnvVarList ();
468 // Check the command line
470 Status
= ProcessCommandLine ();
471 if (EFI_ERROR (Status
)) {
476 // If shell support level is >= 1 create the mappings and paths
478 if (PcdGet8 (PcdShellSupportLevel
) >= 1) {
479 Status
= ShellCommandCreateInitialMappingsAndPaths ();
483 // Set the environment variable for nesting support
487 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoNest
) {
489 // No change. require nesting in Shell Protocol Execute()
506 Status
= InternalEfiShellSetEnv (mNoNestingEnvVarName
, TempString
, TRUE
);
507 SHELL_FREE_NON_NULL (TempString
);
511 // save the device path for the loaded image and the device path for the filepath (under loaded image)
512 // These are where to look for the startup.nsh file
514 Status
= GetDevicePathsForImageAndFile (&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
515 ASSERT_EFI_ERROR (Status
);
518 // Display the version
520 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
523 gST
->ConOut
->Mode
->CursorRow
,
525 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL
),
526 ShellInfoObject
.HiiHandle
,
527 SupportLevel
[PcdGet8 (PcdShellSupportLevel
)],
528 gEfiShellProtocol
->MajorVersion
,
529 gEfiShellProtocol
->MinorVersion
536 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER
),
537 ShellInfoObject
.HiiHandle
,
538 (CHAR16
*)PcdGetPtr (PcdShellSupplier
)
545 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI
),
546 ShellInfoObject
.HiiHandle
,
547 (gST
->Hdr
.Revision
&0xffff0000)>>16,
548 (gST
->Hdr
.Revision
&0x0000ffff),
550 gST
->FirmwareRevision
555 // Display the mapping
557 if ((PcdGet8 (PcdShellSupportLevel
) >= 2) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
558 Status
= RunCommand (L
"map");
559 ASSERT_EFI_ERROR (Status
);
563 // init all the built in alias'
565 Status
= SetBuiltInAlias ();
566 ASSERT_EFI_ERROR (Status
);
569 // Initialize environment variables
571 if (ShellCommandGetProfileList () != NULL
) {
572 Status
= InternalEfiShellSetEnv (L
"profiles", ShellCommandGetProfileList (), TRUE
);
573 ASSERT_EFI_ERROR (Status
);
577 TempString
= AllocateZeroPool (Size
);
579 UnicodeSPrint (TempString
, Size
, L
"%d", PcdGet8 (PcdShellSupportLevel
));
580 Status
= InternalEfiShellSetEnv (L
"uefishellsupport", TempString
, TRUE
);
581 ASSERT_EFI_ERROR (Status
);
583 UnicodeSPrint (TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
584 Status
= InternalEfiShellSetEnv (L
"uefishellversion", TempString
, TRUE
);
585 ASSERT_EFI_ERROR (Status
);
587 UnicodeSPrint (TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
588 Status
= InternalEfiShellSetEnv (L
"uefiversion", TempString
, TRUE
);
589 ASSERT_EFI_ERROR (Status
);
591 FreePool (TempString
);
593 if (!EFI_ERROR (Status
)) {
594 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
596 // Set up the event for CTRL-C monitoring...
598 Status
= InernalEfiShellStartMonitor ();
601 if (!EFI_ERROR (Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
603 // Set up the event for CTRL-S monitoring...
605 Status
= InternalEfiShellStartCtrlSMonitor ();
608 if (!EFI_ERROR (Status
) && ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
610 // close off the gST->ConIn
612 OldConIn
= gST
->ConIn
;
613 ConInHandle
= gST
->ConsoleInHandle
;
614 gST
->ConIn
= CreateSimpleTextInOnFile ((SHELL_FILE_HANDLE
)&FileInterfaceNulFile
, &gST
->ConsoleInHandle
);
620 if (!EFI_ERROR (Status
) && (PcdGet8 (PcdShellSupportLevel
) >= 1)) {
622 // process the startup script or launch the called app.
624 Status
= DoStartupScript (ShellInfoObject
.ImageDevPath
, ShellInfoObject
.FileDevPath
);
627 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
&& !ShellCommandGetExit () && ((PcdGet8 (PcdShellSupportLevel
) >= 3) || PcdGetBool (PcdShellForceConsole
)) && !EFI_ERROR (Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
629 // begin the UI waiting loop
633 // clean out all the memory allocated for CONST <something> * return values
634 // between each shell prompt presentation
636 if (!IsListEmpty (&ShellInfoObject
.BufferToFreeList
.Link
)) {
637 FreeBufferList (&ShellInfoObject
.BufferToFreeList
);
641 // Reset page break back to default.
643 ShellInfoObject
.PageBreakEnabled
= PcdGetBool (PcdShellPageBreakDefault
);
644 ASSERT (ShellInfoObject
.ConsoleInfo
!= NULL
);
645 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
646 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
651 Status
= DoShellPrompt ();
652 } while (!ShellCommandGetExit ());
655 if ((OldConIn
!= NULL
) && (ConInHandle
!= NULL
)) {
656 CloseSimpleTextInOnFile (gST
->ConIn
);
657 gST
->ConIn
= OldConIn
;
658 gST
->ConsoleInHandle
= ConInHandle
;
665 // uninstall protocols / free memory / etc...
667 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
668 gBS
->CloseEvent (ShellInfoObject
.UserBreakTimer
);
670 ShellInfoObject
.UserBreakTimer
= NULL
;
674 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
675 FreePool (ShellInfoObject
.ImageDevPath
);
677 ShellInfoObject
.ImageDevPath
= NULL
;
681 if (ShellInfoObject
.FileDevPath
!= NULL
) {
682 FreePool (ShellInfoObject
.FileDevPath
);
684 ShellInfoObject
.FileDevPath
= NULL
;
688 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
689 CleanUpShellParametersProtocol (ShellInfoObject
.NewShellParametersProtocol
);
691 ShellInfoObject
.NewShellParametersProtocol
= NULL
;
695 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
) {
696 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell ()) {
697 InternalEfiShellSetEnv (L
"cwd", NULL
, TRUE
);
700 CleanUpShellEnvironment (ShellInfoObject
.NewEfiShellProtocol
);
702 ShellInfoObject
.NewEfiShellProtocol
= NULL
;
706 if (!IsListEmpty (&ShellInfoObject
.BufferToFreeList
.Link
)) {
707 FreeBufferList (&ShellInfoObject
.BufferToFreeList
);
710 if (!IsListEmpty (&ShellInfoObject
.SplitList
.Link
)) {
711 ASSERT (FALSE
); /// @todo finish this de-allocation (free SplitStdIn/Out when needed).
713 for ( Split
= (SPLIT_LIST
*)GetFirstNode (&ShellInfoObject
.SplitList
.Link
)
714 ; !IsNull (&ShellInfoObject
.SplitList
.Link
, &Split
->Link
)
715 ; Split
= (SPLIT_LIST
*)GetNextNode (&ShellInfoObject
.SplitList
.Link
, &Split
->Link
)
718 RemoveEntryList (&Split
->Link
);
723 InitializeListHead (&ShellInfoObject
.SplitList
.Link
);
727 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
728 FreePool (ShellInfoObject
.ShellInitSettings
.FileName
);
730 ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;
734 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
735 FreePool (ShellInfoObject
.ShellInitSettings
.FileOptions
);
737 ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;
741 if (ShellInfoObject
.HiiHandle
!= NULL
) {
742 HiiRemovePackages (ShellInfoObject
.HiiHandle
);
744 ShellInfoObject
.HiiHandle
= NULL
;
748 if (!IsListEmpty (&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)) {
749 FreeBufferList (&ShellInfoObject
.ViewingSettings
.CommandHistory
);
752 ASSERT (ShellInfoObject
.ConsoleInfo
!= NULL
);
753 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
754 ConsoleLoggerUninstall (ShellInfoObject
.ConsoleInfo
);
755 FreePool (ShellInfoObject
.ConsoleInfo
);
757 ShellInfoObject
.ConsoleInfo
= NULL
;
761 ShellFreeEnvVarList ();
763 if (ShellCommandGetExit ()) {
764 return ((EFI_STATUS
)ShellCommandGetExitCode ());
771 Sets all the alias' that were registered with the ShellCommandLib library.
773 @retval EFI_SUCCESS all init commands were run successfully.
781 CONST ALIAS_LIST
*List
;
785 // Get all the commands we want to run
787 List
= ShellCommandGetInitAliasList ();
790 // for each command in the List
792 for ( Node
= (ALIAS_LIST
*)GetFirstNode (&List
->Link
)
793 ; !IsNull (&List
->Link
, &Node
->Link
)
794 ; Node
= (ALIAS_LIST
*)GetNextNode (&List
->Link
, &Node
->Link
)
798 // install the alias'
800 Status
= InternalSetAlias (Node
->CommandString
, Node
->Alias
, TRUE
);
801 ASSERT_EFI_ERROR (Status
);
804 return (EFI_SUCCESS
);
808 Internal function to determine if 2 command names are really the same.
810 @param[in] Command1 The pointer to the first command name.
811 @param[in] Command2 The pointer to the second command name.
813 @retval TRUE The 2 command names are the same.
814 @retval FALSE The 2 command names are not the same.
818 IN CONST CHAR16
*Command1
,
819 IN CONST CHAR16
*Command2
822 if (StringNoCaseCompare (&Command1
, &Command2
) == 0) {
830 Internal function to determine if a command is a script only command.
832 @param[in] CommandName The pointer to the command name.
834 @retval TRUE The command is a script only command.
835 @retval FALSE The command is not a script only command.
838 IsScriptOnlyCommand (
839 IN CONST CHAR16
*CommandName
842 if ( IsCommand (CommandName
, L
"for")
843 || IsCommand (CommandName
, L
"endfor")
844 || IsCommand (CommandName
, L
"if")
845 || IsCommand (CommandName
, L
"else")
846 || IsCommand (CommandName
, L
"endif")
847 || IsCommand (CommandName
, L
"goto"))
856 This function will populate the 2 device path protocol parameters based on the
857 global gImageHandle. The DevPath will point to the device path for the handle that has
858 loaded image protocol installed on it. The FilePath will point to the device path
859 for the file that was loaded.
861 @param[in, out] DevPath On a successful return the device path to the loaded image.
862 @param[in, out] FilePath On a successful return the device path to the file.
864 @retval EFI_SUCCESS The 2 device paths were successfully returned.
865 @retval other A error from gBS->HandleProtocol.
870 GetDevicePathsForImageAndFile (
871 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
872 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
876 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
877 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
879 ASSERT (DevPath
!= NULL
);
880 ASSERT (FilePath
!= NULL
);
882 Status
= gBS
->OpenProtocol (
884 &gEfiLoadedImageProtocolGuid
,
885 (VOID
**)&LoadedImage
,
888 EFI_OPEN_PROTOCOL_GET_PROTOCOL
890 if (!EFI_ERROR (Status
)) {
891 Status
= gBS
->OpenProtocol (
892 LoadedImage
->DeviceHandle
,
893 &gEfiDevicePathProtocolGuid
,
894 (VOID
**)&ImageDevicePath
,
897 EFI_OPEN_PROTOCOL_GET_PROTOCOL
899 if (!EFI_ERROR (Status
)) {
900 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
901 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
903 LoadedImage
->DeviceHandle
,
904 &gEfiDevicePathProtocolGuid
,
912 &gEfiLoadedImageProtocolGuid
,
922 Process all Uefi Shell 2.0 command line options.
924 see Uefi Shell 2.0 section 3.2 for full details.
926 the command line must resemble the following:
928 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
930 ShellOpt-options Options which control the initialization behavior of the shell.
931 These options are read from the EFI global variable "ShellOpt"
932 and are processed before options or file-name.
934 options Options which control the initialization behavior of the shell.
936 file-name The name of a UEFI shell application or script to be executed
937 after initialization is complete. By default, if file-name is
938 specified, then -nostartup is implied. Scripts are not supported
941 file-name-options The command-line options that are passed to file-name when it
944 This will initialize the ShellInfoObject.ShellInitSettings global variable.
946 @retval EFI_SUCCESS The variable is initialized.
956 CHAR16
*DelayValueStr
;
959 EFI_UNICODE_COLLATION_PROTOCOL
*UnicodeCollation
;
961 // `file-name-options` will contain arguments to `file-name` that we don't
962 // know about. This would cause ShellCommandLineParse to error, so we parse
963 // arguments manually, ignoring those after the first thing that doesn't look
964 // like a shell option (which is assumed to be `file-name`).
966 Status
= gBS
->LocateProtocol (
967 &gEfiUnicodeCollation2ProtocolGuid
,
969 (VOID
**)&UnicodeCollation
971 if (EFI_ERROR (Status
)) {
972 Status
= gBS
->LocateProtocol (
973 &gEfiUnicodeCollationProtocolGuid
,
975 (VOID
**)&UnicodeCollation
977 if (EFI_ERROR (Status
)) {
982 // Set default options
983 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= FALSE
;
984 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= FALSE
;
985 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= FALSE
;
986 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= FALSE
;
987 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= FALSE
;
988 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= FALSE
;
989 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= FALSE
;
990 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= FALSE
;
991 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= FALSE
;
992 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoNest
= FALSE
;
993 ShellInfoObject
.ShellInitSettings
.Delay
= PcdGet32 (PcdShellDefaultDelay
);
996 // Start LoopVar at 0 to parse only optional arguments at Argv[0]
997 // and parse other parameters from Argv[1]. This is for use case that
998 // UEFI Shell boot option is created, and OptionalData is provided
999 // that starts with shell command-line options.
1001 for (LoopVar
= 0; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
1002 CurrentArg
= gEfiShellParametersProtocol
->Argv
[LoopVar
];
1003 if (UnicodeCollation
->StriColl (
1009 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= TRUE
;
1010 } else if (UnicodeCollation
->StriColl (
1016 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= TRUE
;
1017 } else if (UnicodeCollation
->StriColl (
1023 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= TRUE
;
1024 } else if (UnicodeCollation
->StriColl (
1030 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= TRUE
;
1031 } else if (UnicodeCollation
->StriColl (
1037 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= TRUE
;
1038 } else if (UnicodeCollation
->StriColl (
1044 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= TRUE
;
1045 } else if (UnicodeCollation
->StriColl (
1051 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= TRUE
;
1052 } else if (UnicodeCollation
->StriColl (
1058 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoNest
= TRUE
;
1059 } else if (UnicodeCollation
->StriColl (
1065 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= TRUE
;
1066 // Check for optional delay value following "-delay"
1067 if ((LoopVar
+ 1) >= gEfiShellParametersProtocol
->Argc
) {
1068 DelayValueStr
= NULL
;
1070 DelayValueStr
= gEfiShellParametersProtocol
->Argv
[LoopVar
+ 1];
1073 if (DelayValueStr
!= NULL
) {
1074 if (*DelayValueStr
== L
':') {
1079 ShellConvertStringToUint64 (
1087 ShellInfoObject
.ShellInitSettings
.Delay
= (UINTN
)DelayValue
;
1091 } else if (UnicodeCollation
->StriColl (
1097 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= TRUE
;
1098 } else if (StrnCmp (L
"-", CurrentArg
, 1) == 0) {
1099 // Unrecognized option
1104 STRING_TOKEN (STR_GEN_PROBLEM
),
1105 ShellInfoObject
.HiiHandle
,
1108 return EFI_INVALID_PARAMETER
;
1111 // First argument should be Shell.efi image name
1117 ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;
1120 // If first argument contains a space, then add double quotes before the argument
1122 if (StrStr (CurrentArg
, L
" ") != NULL
) {
1123 StrnCatGrow (&ShellInfoObject
.ShellInitSettings
.FileName
, &Size
, L
"\"", 0);
1124 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
1125 return (EFI_OUT_OF_RESOURCES
);
1129 StrnCatGrow (&ShellInfoObject
.ShellInitSettings
.FileName
, &Size
, CurrentArg
, 0);
1130 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
1131 return (EFI_OUT_OF_RESOURCES
);
1135 // If first argument contains a space, then add double quotes after the argument
1137 if (StrStr (CurrentArg
, L
" ") != NULL
) {
1138 StrnCatGrow (&ShellInfoObject
.ShellInitSettings
.FileName
, &Size
, L
"\"", 0);
1139 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
1140 return (EFI_OUT_OF_RESOURCES
);
1145 // We found `file-name`.
1147 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
1150 // Add `file-name-options`
1151 for (Size
= 0; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
1152 ASSERT ((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
1154 // Add a space between arguments
1156 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1157 StrnCatGrow (&ShellInfoObject
.ShellInitSettings
.FileOptions
, &Size
, L
" ", 0);
1158 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1159 SHELL_FREE_NON_NULL (ShellInfoObject
.ShellInitSettings
.FileName
);
1160 return (EFI_OUT_OF_RESOURCES
);
1165 // If an argument contains a space, then add double quotes before the argument
1167 if (StrStr (gEfiShellParametersProtocol
->Argv
[LoopVar
], L
" ") != NULL
) {
1169 &ShellInfoObject
.ShellInitSettings
.FileOptions
,
1174 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1175 SHELL_FREE_NON_NULL (ShellInfoObject
.ShellInitSettings
.FileName
);
1176 return (EFI_OUT_OF_RESOURCES
);
1181 &ShellInfoObject
.ShellInitSettings
.FileOptions
,
1183 gEfiShellParametersProtocol
->Argv
[LoopVar
],
1186 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1187 SHELL_FREE_NON_NULL (ShellInfoObject
.ShellInitSettings
.FileName
);
1188 return (EFI_OUT_OF_RESOURCES
);
1192 // If an argument contains a space, then add double quotes after the argument
1194 if (StrStr (gEfiShellParametersProtocol
->Argv
[LoopVar
], L
" ") != NULL
) {
1196 &ShellInfoObject
.ShellInitSettings
.FileOptions
,
1201 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1202 SHELL_FREE_NON_NULL (ShellInfoObject
.ShellInitSettings
.FileName
);
1203 return (EFI_OUT_OF_RESOURCES
);
1210 // "-nointerrupt" overrides "-delay"
1211 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
1212 ShellInfoObject
.ShellInitSettings
.Delay
= 0;
1219 Function try to find location of the Startup.nsh file.
1221 The buffer is callee allocated and should be freed by the caller.
1223 @param ImageDevicePath The path to the image for shell. first place to look for the startup script
1224 @param FileDevicePath The path to the file for shell. second place to look for the startup script.
1226 @retval NULL No Startup.nsh file was found.
1227 @return !=NULL Pointer to NULL-terminated path.
1230 LocateStartupScript (
1231 IN EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
,
1232 IN EFI_DEVICE_PATH_PROTOCOL
*FileDevicePath
1235 CHAR16
*StartupScriptPath
;
1237 CONST CHAR16
*MapName
;
1240 StartupScriptPath
= NULL
;
1244 // Try to find 'Startup.nsh' in the directory where the shell itself was launched.
1246 MapName
= ShellInfoObject
.NewEfiShellProtocol
->GetMapFromDevicePath (&ImageDevicePath
);
1247 if (MapName
!= NULL
) {
1248 StartupScriptPath
= StrnCatGrow (&StartupScriptPath
, &Size
, MapName
, 0);
1249 if (StartupScriptPath
== NULL
) {
1251 // Do not locate the startup script in sys path when out of resource.
1256 TempSpot
= StrStr (StartupScriptPath
, L
";");
1257 if (TempSpot
!= NULL
) {
1258 *TempSpot
= CHAR_NULL
;
1261 InternalEfiShellSetEnv (L
"homefilesystem", StartupScriptPath
, TRUE
);
1263 StartupScriptPath
= StrnCatGrow (&StartupScriptPath
, &Size
, ((FILEPATH_DEVICE_PATH
*)FileDevicePath
)->PathName
, 0);
1264 PathRemoveLastItem (StartupScriptPath
);
1265 StartupScriptPath
= StrnCatGrow (&StartupScriptPath
, &Size
, mStartupScript
, 0);
1269 // Try to find 'Startup.nsh' in the execution path defined by the environment variable PATH.
1271 if ((StartupScriptPath
== NULL
) || EFI_ERROR (ShellIsFile (StartupScriptPath
))) {
1272 SHELL_FREE_NON_NULL (StartupScriptPath
);
1273 StartupScriptPath
= ShellFindFilePath (mStartupScript
);
1276 return StartupScriptPath
;
1280 Handles all interaction with the default startup script.
1282 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
1284 @param ImagePath the path to the image for shell. first place to look for the startup script
1285 @param FilePath the path to the file for shell. second place to look for the startup script.
1287 @retval EFI_SUCCESS the variable is initialized.
1291 IN EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
1292 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
1296 EFI_STATUS CalleeStatus
;
1299 CHAR16
*FileStringPath
;
1300 CHAR16
*FullFileStringPath
;
1303 Key
.UnicodeChar
= CHAR_NULL
;
1306 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
)) {
1308 // launch something else instead
1310 NewSize
= StrSize (ShellInfoObject
.ShellInitSettings
.FileName
);
1311 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1312 NewSize
+= StrSize (ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof (CHAR16
);
1315 FileStringPath
= AllocateZeroPool (NewSize
);
1316 if (FileStringPath
== NULL
) {
1317 return (EFI_OUT_OF_RESOURCES
);
1320 StrCpyS (FileStringPath
, NewSize
/sizeof (CHAR16
), ShellInfoObject
.ShellInitSettings
.FileName
);
1321 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1322 StrnCatS (FileStringPath
, NewSize
/sizeof (CHAR16
), L
" ", NewSize
/sizeof (CHAR16
) - StrLen (FileStringPath
) -1);
1323 StrnCatS (FileStringPath
, NewSize
/sizeof (CHAR16
), ShellInfoObject
.ShellInitSettings
.FileOptions
, NewSize
/sizeof (CHAR16
) - StrLen (FileStringPath
) -1);
1326 Status
= RunShellCommand (FileStringPath
, &CalleeStatus
);
1327 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
== TRUE
) {
1328 ShellCommandRegisterExit (gEfiShellProtocol
->BatchIsActive (), (UINT64
)CalleeStatus
);
1331 FreePool (FileStringPath
);
1336 // for shell level 0 we do no scripts
1337 // Without the Startup bit overriding we allow for nostartup to prevent scripts
1339 if ( (PcdGet8 (PcdShellSupportLevel
) < 1)
1340 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
1343 return (EFI_SUCCESS
);
1346 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
1348 // print out our warning and see if they press a key
1350 for ( Status
= EFI_UNSUPPORTED
, Delay
= ShellInfoObject
.ShellInitSettings
.Delay
1351 ; Delay
!= 0 && EFI_ERROR (Status
)
1355 ShellPrintHiiEx (0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
);
1356 gBS
->Stall (1000000);
1357 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
1358 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
1362 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
1363 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
1368 if ((Status
== EFI_SUCCESS
) && (Key
.UnicodeChar
== 0) && (Key
.ScanCode
== SCAN_ESC
)) {
1369 return (EFI_SUCCESS
);
1372 FileStringPath
= LocateStartupScript (ImagePath
, FilePath
);
1373 if (FileStringPath
!= NULL
) {
1374 FullFileStringPath
= FullyQualifyPath (FileStringPath
);
1375 if (FullFileStringPath
== NULL
) {
1376 Status
= RunScriptFile (FileStringPath
, NULL
, FileStringPath
, ShellInfoObject
.NewShellParametersProtocol
);
1378 Status
= RunScriptFile (FullFileStringPath
, NULL
, FullFileStringPath
, ShellInfoObject
.NewShellParametersProtocol
);
1379 FreePool (FullFileStringPath
);
1382 FreePool (FileStringPath
);
1385 // we return success since startup script is not mandatory.
1387 Status
= EFI_SUCCESS
;
1394 Function to perform the shell prompt looping. It will do a single prompt,
1395 dispatch the result, and then return. It is expected that the caller will
1396 call this function in a loop many times.
1399 @retval RETURN_ABORTED
1409 CONST CHAR16
*CurDir
;
1412 LIST_ENTRY OldBufferList
;
1417 // Get screen setting to decide size of the command line buffer
1419 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
1420 BufferSize
= Column
* Row
* sizeof (CHAR16
);
1421 CmdLine
= AllocateZeroPool (BufferSize
);
1422 if (CmdLine
== NULL
) {
1423 return EFI_OUT_OF_RESOURCES
;
1426 SaveBufferList (&OldBufferList
);
1427 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv (L
"cwd");
1432 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
1434 if ((CurDir
!= NULL
) && (StrLen (CurDir
) > 1)) {
1435 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1437 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1441 // Read a line from the console
1443 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile (ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
1446 // Null terminate the string and parse it
1448 if (!EFI_ERROR (Status
)) {
1450 // Reset the CTRL-C event just before running the command (yes we ignore the return values)
1452 Status
= gBS
->CheckEvent (ShellInfoObject
.NewEfiShellProtocol
->ExecutionBreak
);
1454 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
1455 Status
= RunCommand (CmdLine
);
1459 // Done with this command
1461 RestoreBufferList (&OldBufferList
);
1467 Add a buffer to the Buffer To Free List for safely returning buffers to other
1468 places without risking letting them modify internal shell information.
1470 @param Buffer Something to pass to FreePool when the shell is exiting.
1473 AddBufferToFreeList (
1477 BUFFER_LIST
*BufferListEntry
;
1479 if (Buffer
== NULL
) {
1483 BufferListEntry
= AllocateZeroPool (sizeof (BUFFER_LIST
));
1484 if (BufferListEntry
== NULL
) {
1488 BufferListEntry
->Buffer
= Buffer
;
1489 InsertTailList (&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
1494 Create a new buffer list and stores the old one to OldBufferList
1496 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
1500 OUT LIST_ENTRY
*OldBufferList
1503 CopyMem (OldBufferList
, &ShellInfoObject
.BufferToFreeList
.Link
, sizeof (LIST_ENTRY
));
1504 InitializeListHead (&ShellInfoObject
.BufferToFreeList
.Link
);
1508 Restore previous nodes into BufferToFreeList .
1510 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
1514 IN OUT LIST_ENTRY
*OldBufferList
1517 FreeBufferList (&ShellInfoObject
.BufferToFreeList
);
1518 CopyMem (&ShellInfoObject
.BufferToFreeList
.Link
, OldBufferList
, sizeof (LIST_ENTRY
));
1522 Add a buffer to the Line History List
1524 @param Buffer The line buffer to add.
1527 AddLineToCommandHistory (
1528 IN CONST CHAR16
*Buffer
1532 BUFFER_LIST
*Walker
;
1533 UINT16 MaxHistoryCmdCount
;
1537 MaxHistoryCmdCount
= PcdGet16 (PcdShellMaxHistoryCommandCount
);
1539 if (MaxHistoryCmdCount
== 0) {
1543 Node
= AllocateZeroPool (sizeof (BUFFER_LIST
));
1548 Node
->Buffer
= AllocateCopyPool (StrSize (Buffer
), Buffer
);
1549 if (Node
->Buffer
== NULL
) {
1554 for ( Walker
= (BUFFER_LIST
*)GetFirstNode (&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)
1555 ; !IsNull (&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Walker
->Link
)
1556 ; Walker
= (BUFFER_LIST
*)GetNextNode (&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Walker
->Link
)
1562 if (Count
< MaxHistoryCmdCount
) {
1563 InsertTailList (&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1565 Walker
= (BUFFER_LIST
*)GetFirstNode (&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
1566 RemoveEntryList (&Walker
->Link
);
1567 if (Walker
->Buffer
!= NULL
) {
1568 FreePool (Walker
->Buffer
);
1572 InsertTailList (&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1577 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1578 with the correct command name.
1580 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1581 command name if it was an alias. If it was not
1582 an alias it will be unchanged. This function may
1583 change the buffer to fit the command name.
1585 @retval EFI_SUCCESS The name was changed.
1586 @retval EFI_SUCCESS The name was not an alias.
1587 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1591 IN OUT CHAR16
**CommandString
1594 CONST CHAR16
*NewString
;
1596 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias (*CommandString
, NULL
);
1597 if (NewString
== NULL
) {
1598 return (EFI_SUCCESS
);
1601 FreePool (*CommandString
);
1602 *CommandString
= AllocateCopyPool (StrSize (NewString
), NewString
);
1603 if (*CommandString
== NULL
) {
1604 return (EFI_OUT_OF_RESOURCES
);
1607 return (EFI_SUCCESS
);
1611 This function will eliminate unreplaced (and therefore non-found) environment variables.
1613 @param[in,out] CmdLine The command line to update.
1616 StripUnreplacedEnvironmentVariables (
1617 IN OUT CHAR16
*CmdLine
1620 CHAR16
*FirstPercent
;
1622 CHAR16
*SecondPercent
;
1623 CHAR16
*SecondQuote
;
1624 CHAR16
*CurrentLocator
;
1626 for (CurrentLocator
= CmdLine
; CurrentLocator
!= NULL
; ) {
1627 FirstQuote
= FindNextInstance (CurrentLocator
, L
"\"", TRUE
);
1628 FirstPercent
= FindNextInstance (CurrentLocator
, L
"%", TRUE
);
1629 SecondPercent
= FirstPercent
!= NULL
? FindNextInstance (FirstPercent
+1, L
"%", TRUE
) : NULL
;
1630 if ((FirstPercent
== NULL
) || (SecondPercent
== NULL
)) {
1632 // If we ever don't have 2 % we are done.
1637 if ((FirstQuote
!= NULL
) && (FirstQuote
< FirstPercent
)) {
1638 SecondQuote
= FindNextInstance (FirstQuote
+1, L
"\"", TRUE
);
1640 // Quote is first found
1643 if (SecondQuote
< FirstPercent
) {
1645 // restart after the pair of "
1647 CurrentLocator
= SecondQuote
+ 1;
1649 /* FirstPercent < SecondQuote */
1651 // Restart on the first percent
1653 CurrentLocator
= FirstPercent
;
1659 if ((FirstQuote
== NULL
) || (SecondPercent
< FirstQuote
)) {
1660 if (IsValidEnvironmentVariableName (FirstPercent
, SecondPercent
)) {
1662 // We need to remove from FirstPercent to SecondPercent
1664 CopyMem (FirstPercent
, SecondPercent
+ 1, StrSize (SecondPercent
+ 1));
1666 // don't need to update the locator. both % characters are gone.
1669 CurrentLocator
= SecondPercent
+ 1;
1675 CurrentLocator
= FirstQuote
;
1678 return (EFI_SUCCESS
);
1682 Function allocates a new command line and replaces all instances of environment
1683 variable names that are correctly preset to their values.
1685 If the return value is not NULL the memory must be caller freed.
1687 @param[in] OriginalCommandLine The original command line
1689 @retval NULL An error occurred.
1690 @return The new command line with no environment variables present.
1693 ShellConvertVariables (
1694 IN CONST CHAR16
*OriginalCommandLine
1697 CONST CHAR16
*MasterEnvList
;
1699 CHAR16
*NewCommandLine1
;
1700 CHAR16
*NewCommandLine2
;
1704 SCRIPT_FILE
*CurrentScriptFile
;
1705 ALIAS_LIST
*AliasListNode
;
1707 ASSERT (OriginalCommandLine
!= NULL
);
1710 NewSize
= StrSize (OriginalCommandLine
);
1711 CurrentScriptFile
= ShellCommandGetCurrentScriptFile ();
1714 /// @todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1717 // calculate the size required for the post-conversion string...
1719 if (CurrentScriptFile
!= NULL
) {
1720 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode (&CurrentScriptFile
->SubstList
)
1721 ; !IsNull (&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1722 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode (&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1725 for (Temp
= StrStr (OriginalCommandLine
, AliasListNode
->Alias
)
1727 ; Temp
= StrStr (Temp
+1, AliasListNode
->Alias
)
1731 // we need a preceding and if there is space no ^ preceding (if no space ignore)
1733 if ((((Temp
-OriginalCommandLine
) > 2) && (*(Temp
-2) != L
'^')) || ((Temp
-OriginalCommandLine
) <= 2)) {
1734 NewSize
+= StrSize (AliasListNode
->CommandString
);
1740 for (MasterEnvList
= EfiShellGetEnv (NULL
)
1741 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
// && *(MasterEnvList+1) != CHAR_NULL
1742 ; MasterEnvList
+= StrLen (MasterEnvList
) + 1
1745 if (StrSize (MasterEnvList
) > ItemSize
) {
1746 ItemSize
= StrSize (MasterEnvList
);
1749 for (Temp
= StrStr (OriginalCommandLine
, MasterEnvList
)
1751 ; Temp
= StrStr (Temp
+1, MasterEnvList
)
1755 // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)
1757 if ((*(Temp
-1) == L
'%') && (*(Temp
+StrLen (MasterEnvList
)) == L
'%') &&
1758 ((((Temp
-OriginalCommandLine
) > 2) && (*(Temp
-2) != L
'^')) || ((Temp
-OriginalCommandLine
) <= 2)))
1760 NewSize
+= StrSize (EfiShellGetEnv (MasterEnvList
));
1766 // now do the replacements...
1768 NewCommandLine1
= AllocateZeroPool (NewSize
);
1769 NewCommandLine2
= AllocateZeroPool (NewSize
);
1770 ItemTemp
= AllocateZeroPool (ItemSize
+(2*sizeof (CHAR16
)));
1771 if ((NewCommandLine1
== NULL
) || (NewCommandLine2
== NULL
) || (ItemTemp
== NULL
)) {
1772 SHELL_FREE_NON_NULL (NewCommandLine1
);
1773 SHELL_FREE_NON_NULL (NewCommandLine2
);
1774 SHELL_FREE_NON_NULL (ItemTemp
);
1778 CopyMem (NewCommandLine1
, OriginalCommandLine
, StrSize (OriginalCommandLine
));
1780 for (MasterEnvList
= EfiShellGetEnv (NULL
)
1781 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
1782 ; MasterEnvList
+= StrLen (MasterEnvList
) + 1
1787 ((ItemSize
+(2*sizeof (CHAR16
)))/sizeof (CHAR16
)),
1792 ((ItemSize
+(2*sizeof (CHAR16
)))/sizeof (CHAR16
)),
1797 ((ItemSize
+(2*sizeof (CHAR16
)))/sizeof (CHAR16
)),
1800 ShellCopySearchAndReplace (NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv (MasterEnvList
), TRUE
, FALSE
);
1801 StrCpyS (NewCommandLine1
, NewSize
/sizeof (CHAR16
), NewCommandLine2
);
1804 if (CurrentScriptFile
!= NULL
) {
1805 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode (&CurrentScriptFile
->SubstList
)
1806 ; !IsNull (&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1807 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode (&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1810 ShellCopySearchAndReplace (NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1811 StrCpyS (NewCommandLine1
, NewSize
/sizeof (CHAR16
), NewCommandLine2
);
1816 // Remove non-existent environment variables
1818 StripUnreplacedEnvironmentVariables (NewCommandLine1
);
1821 // Now cleanup any straggler intentionally ignored "%" characters
1823 ShellCopySearchAndReplace (NewCommandLine1
, NewCommandLine2
, NewSize
, L
"^%", L
"%", TRUE
, FALSE
);
1824 StrCpyS (NewCommandLine1
, NewSize
/sizeof (CHAR16
), NewCommandLine2
);
1826 FreePool (NewCommandLine2
);
1827 FreePool (ItemTemp
);
1829 return (NewCommandLine1
);
1833 Internal function to run a command line with pipe usage.
1835 @param[in] CmdLine The pointer to the command line.
1836 @param[in] StdIn The pointer to the Standard input.
1837 @param[in] StdOut The pointer to the Standard output.
1839 @retval EFI_SUCCESS The split command is executed successfully.
1840 @retval other Some error occurs when executing the split command.
1844 IN CONST CHAR16
*CmdLine
,
1845 IN SHELL_FILE_HANDLE StdIn
,
1846 IN SHELL_FILE_HANDLE StdOut
1850 CHAR16
*NextCommandLine
;
1851 CHAR16
*OurCommandLine
;
1855 SHELL_FILE_HANDLE TempFileHandle
;
1858 ASSERT (StdOut
== NULL
);
1860 ASSERT (StrStr (CmdLine
, L
"|") != NULL
);
1862 Status
= EFI_SUCCESS
;
1863 NextCommandLine
= NULL
;
1864 OurCommandLine
= NULL
;
1868 NextCommandLine
= StrnCatGrow (&NextCommandLine
, &Size1
, StrStr (CmdLine
, L
"|")+1, 0);
1869 OurCommandLine
= StrnCatGrow (&OurCommandLine
, &Size2
, CmdLine
, StrStr (CmdLine
, L
"|") - CmdLine
);
1871 if ((NextCommandLine
== NULL
) || (OurCommandLine
== NULL
)) {
1872 SHELL_FREE_NON_NULL (OurCommandLine
);
1873 SHELL_FREE_NON_NULL (NextCommandLine
);
1874 return (EFI_OUT_OF_RESOURCES
);
1875 } else if ((StrStr (OurCommandLine
, L
"|") != NULL
) || (Size1
== 0) || (Size2
== 0)) {
1876 SHELL_FREE_NON_NULL (OurCommandLine
);
1877 SHELL_FREE_NON_NULL (NextCommandLine
);
1878 return (EFI_INVALID_PARAMETER
);
1879 } else if ((NextCommandLine
[0] == L
'a') &&
1880 ((NextCommandLine
[1] == L
' ') || (NextCommandLine
[1] == CHAR_NULL
))
1883 CopyMem (NextCommandLine
, NextCommandLine
+1, StrSize (NextCommandLine
) - sizeof (NextCommandLine
[0]));
1884 while (NextCommandLine
[0] == L
' ') {
1885 CopyMem (NextCommandLine
, NextCommandLine
+1, StrSize (NextCommandLine
) - sizeof (NextCommandLine
[0]));
1888 if (NextCommandLine
[0] == CHAR_NULL
) {
1889 SHELL_FREE_NON_NULL (OurCommandLine
);
1890 SHELL_FREE_NON_NULL (NextCommandLine
);
1891 return (EFI_INVALID_PARAMETER
);
1900 // make a SPLIT_LIST item and add to list
1902 Split
= AllocateZeroPool (sizeof (SPLIT_LIST
));
1903 if (Split
== NULL
) {
1904 return EFI_OUT_OF_RESOURCES
;
1907 Split
->SplitStdIn
= StdIn
;
1908 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (Unicode
), NULL
);
1909 ASSERT (Split
->SplitStdOut
!= NULL
);
1910 InsertHeadList (&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1912 Status
= RunCommand (OurCommandLine
);
1915 // move the output from the first to the in to the second.
1917 TempFileHandle
= Split
->SplitStdOut
;
1918 if (Split
->SplitStdIn
== StdIn
) {
1919 Split
->SplitStdOut
= NULL
;
1921 Split
->SplitStdOut
= Split
->SplitStdIn
;
1924 Split
->SplitStdIn
= TempFileHandle
;
1925 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition (Split
->SplitStdIn
, 0);
1927 if (!EFI_ERROR (Status
)) {
1928 Status
= RunCommand (NextCommandLine
);
1932 // remove the top level from the ScriptList
1934 ASSERT ((SPLIT_LIST
*)GetFirstNode (&ShellInfoObject
.SplitList
.Link
) == Split
);
1935 RemoveEntryList (&Split
->Link
);
1938 // Note that the original StdIn is now the StdOut...
1940 if (Split
->SplitStdOut
!= NULL
) {
1941 ShellInfoObject
.NewEfiShellProtocol
->CloseFile (Split
->SplitStdOut
);
1944 if (Split
->SplitStdIn
!= NULL
) {
1945 ShellInfoObject
.NewEfiShellProtocol
->CloseFile (Split
->SplitStdIn
);
1949 FreePool (NextCommandLine
);
1950 FreePool (OurCommandLine
);
1956 Take the original command line, substitute any variables, free
1957 the original string, return the modified copy.
1959 @param[in] CmdLine pointer to the command line to update.
1961 @retval EFI_SUCCESS the function was successful.
1962 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1965 ShellSubstituteVariables (
1971 NewCmdLine
= ShellConvertVariables (*CmdLine
);
1972 SHELL_FREE_NON_NULL (*CmdLine
);
1973 if (NewCmdLine
== NULL
) {
1974 return (EFI_OUT_OF_RESOURCES
);
1977 *CmdLine
= NewCmdLine
;
1978 return (EFI_SUCCESS
);
1982 Take the original command line, substitute any alias in the first group of space delimited characters, free
1983 the original string, return the modified copy.
1985 @param[in] CmdLine pointer to the command line to update.
1987 @retval EFI_SUCCESS the function was successful.
1988 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1991 ShellSubstituteAliases (
1996 CHAR16
*CommandName
;
1998 UINTN PostAliasSize
;
2000 ASSERT (CmdLine
!= NULL
);
2001 ASSERT (*CmdLine
!= NULL
);
2004 if (StrStr ((*CmdLine
), L
" ") == NULL
) {
2005 StrnCatGrow (&CommandName
, NULL
, (*CmdLine
), 0);
2007 StrnCatGrow (&CommandName
, NULL
, (*CmdLine
), StrStr ((*CmdLine
), L
" ") - (*CmdLine
));
2011 // This cannot happen 'inline' since the CmdLine can need extra space.
2014 if (!ShellCommandIsCommandOnList (CommandName
)) {
2016 // Convert via alias
2018 Status
= ShellConvertAlias (&CommandName
);
2019 if (EFI_ERROR (Status
)) {
2024 NewCmdLine
= StrnCatGrow (&NewCmdLine
, &PostAliasSize
, CommandName
, 0);
2025 if (NewCmdLine
== NULL
) {
2026 SHELL_FREE_NON_NULL (CommandName
);
2027 SHELL_FREE_NON_NULL (*CmdLine
);
2028 return (EFI_OUT_OF_RESOURCES
);
2031 NewCmdLine
= StrnCatGrow (&NewCmdLine
, &PostAliasSize
, StrStr ((*CmdLine
), L
" "), 0);
2032 if (NewCmdLine
== NULL
) {
2033 SHELL_FREE_NON_NULL (CommandName
);
2034 SHELL_FREE_NON_NULL (*CmdLine
);
2035 return (EFI_OUT_OF_RESOURCES
);
2038 NewCmdLine
= StrnCatGrow (&NewCmdLine
, NULL
, (*CmdLine
), 0);
2041 SHELL_FREE_NON_NULL (*CmdLine
);
2042 SHELL_FREE_NON_NULL (CommandName
);
2045 // re-assign the passed in double pointer to point to our newly allocated buffer
2047 *CmdLine
= NewCmdLine
;
2049 return (EFI_SUCCESS
);
2053 Takes the Argv[0] part of the command line and determine the meaning of it.
2055 @param[in] CmdName pointer to the command line to update.
2057 @retval Internal_Command The name is an internal command.
2058 @retval File_Sys_Change the name is a file system change.
2059 @retval Script_File_Name the name is a NSH script file.
2060 @retval Unknown_Invalid the name is unknown.
2061 @retval Efi_Application the name is an application (.EFI).
2063 SHELL_OPERATION_TYPES
2065 IN CONST CHAR16
*CmdName
2068 CHAR16
*FileWithPath
;
2069 CONST CHAR16
*TempLocation
;
2070 CONST CHAR16
*TempLocation2
;
2072 FileWithPath
= NULL
;
2074 // test for an internal command.
2076 if (ShellCommandIsCommandOnList (CmdName
)) {
2077 return (Internal_Command
);
2081 // Test for file system change request. anything ending with first : and cant have spaces.
2083 if (CmdName
[(StrLen (CmdName
)-1)] == L
':') {
2084 if ( (StrStr (CmdName
, L
" ") != NULL
)
2085 || (StrLen (StrStr (CmdName
, L
":")) > 1)
2088 return (Unknown_Invalid
);
2091 return (File_Sys_Change
);
2097 if ((FileWithPath
= ShellFindFilePathEx (CmdName
, mExecutableExtensions
)) != NULL
) {
2099 // See if that file has a script file extension
2101 if (StrLen (FileWithPath
) > 4) {
2102 TempLocation
= FileWithPath
+StrLen (FileWithPath
)-4;
2103 TempLocation2
= mScriptExtension
;
2104 if (StringNoCaseCompare ((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0) {
2105 SHELL_FREE_NON_NULL (FileWithPath
);
2106 return (Script_File_Name
);
2111 // Was a file, but not a script. we treat this as an application.
2113 SHELL_FREE_NON_NULL (FileWithPath
);
2114 return (Efi_Application
);
2117 SHELL_FREE_NON_NULL (FileWithPath
);
2119 // No clue what this is... return invalid flag...
2121 return (Unknown_Invalid
);
2125 Determine if the first item in a command line is valid.
2127 @param[in] CmdLine The command line to parse.
2129 @retval EFI_SUCCESS The item is valid.
2130 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
2131 @retval EFI_NOT_FOUND The operation type is unknown or invalid.
2135 IN CONST CHAR16
*CmdLine
2139 CHAR16
*FirstParameter
;
2145 Temp
= StrnCatGrow (&Temp
, NULL
, CmdLine
, 0);
2147 return (EFI_OUT_OF_RESOURCES
);
2150 FirstParameter
= StrStr (Temp
, L
"|");
2151 if (FirstParameter
!= NULL
) {
2152 *FirstParameter
= CHAR_NULL
;
2155 FirstParameter
= NULL
;
2158 // Process the command line
2160 Status
= ProcessCommandLineToFinal (&Temp
);
2162 if (!EFI_ERROR (Status
)) {
2163 FirstParameter
= AllocateZeroPool (StrSize (CmdLine
));
2164 if (FirstParameter
== NULL
) {
2165 SHELL_FREE_NON_NULL (Temp
);
2166 return (EFI_OUT_OF_RESOURCES
);
2169 TempWalker
= (CHAR16
*)Temp
;
2170 if (!EFI_ERROR (GetNextParameter (&TempWalker
, &FirstParameter
, StrSize (CmdLine
), TRUE
))) {
2171 if (GetOperationType (FirstParameter
) == Unknown_Invalid
) {
2172 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2173 SetLastError (SHELL_NOT_FOUND
);
2174 Status
= EFI_NOT_FOUND
;
2179 SHELL_FREE_NON_NULL (Temp
);
2180 SHELL_FREE_NON_NULL (FirstParameter
);
2185 Determine if a command line contains with a split contains only valid commands.
2187 @param[in] CmdLine The command line to parse.
2189 @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.
2190 @retval EFI_ABORTED CmdLine has at least one invalid command or application.
2194 IN CONST CHAR16
*CmdLine
2197 CONST CHAR16
*TempSpot
;
2201 // If this was the only item, then get out
2203 if (!ContainsSplit (CmdLine
)) {
2204 return (EFI_SUCCESS
);
2208 // Verify up to the pipe or end character
2210 Status
= IsValidSplit (CmdLine
);
2211 if (EFI_ERROR (Status
)) {
2216 // recurse to verify the next item
2218 TempSpot
= FindFirstCharacter (CmdLine
, L
"|", L
'^') + 1;
2219 if ((*TempSpot
== L
'a') &&
2220 ((*(TempSpot
+ 1) == L
' ') || (*(TempSpot
+ 1) == CHAR_NULL
))
2223 // If it's an ASCII pipe '|a'
2227 return (VerifySplit (TempSpot
));
2231 Process a split based operation.
2233 @param[in] CmdLine pointer to the command line to process
2235 @retval EFI_SUCCESS The operation was successful
2236 @return an error occurred.
2239 ProcessNewSplitCommandLine (
2240 IN CONST CHAR16
*CmdLine
2246 Status
= VerifySplit (CmdLine
);
2247 if (EFI_ERROR (Status
)) {
2254 // are we in an existing split???
2256 if (!IsListEmpty (&ShellInfoObject
.SplitList
.Link
)) {
2257 Split
= (SPLIT_LIST
*)GetFirstNode (&ShellInfoObject
.SplitList
.Link
);
2260 if (Split
== NULL
) {
2261 Status
= RunSplitCommand (CmdLine
, NULL
, NULL
);
2263 Status
= RunSplitCommand (CmdLine
, Split
->SplitStdIn
, Split
->SplitStdOut
);
2266 if (EFI_ERROR (Status
)) {
2267 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_SPLIT
), ShellInfoObject
.HiiHandle
, CmdLine
);
2274 Handle a request to change the current file system.
2276 @param[in] CmdLine The passed in command line.
2278 @retval EFI_SUCCESS The operation was successful.
2282 IN CONST CHAR16
*CmdLine
2287 Status
= EFI_SUCCESS
;
2290 // make sure we are the right operation
2292 ASSERT (CmdLine
[(StrLen (CmdLine
)-1)] == L
':' && StrStr (CmdLine
, L
" ") == NULL
);
2295 // Call the protocol API to do the work
2297 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir (NULL
, CmdLine
);
2300 // Report any errors
2302 if (EFI_ERROR (Status
)) {
2303 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, CmdLine
);
2310 Reprocess the command line to direct all -? to the help command.
2312 if found, will add "help" as argv[0], and move the rest later.
2314 @param[in,out] CmdLine pointer to the command line to update
2318 IN OUT CHAR16
**CmdLine
2321 CHAR16
*CurrentParameter
;
2323 CHAR16
*NewCommandLine
;
2325 UINTN NewCmdLineSize
;
2327 Status
= EFI_SUCCESS
;
2329 CurrentParameter
= AllocateZeroPool (StrSize (*CmdLine
));
2330 if (CurrentParameter
== NULL
) {
2331 return (EFI_OUT_OF_RESOURCES
);
2335 while (Walker
!= NULL
&& *Walker
!= CHAR_NULL
) {
2336 if (!EFI_ERROR (GetNextParameter (&Walker
, &CurrentParameter
, StrSize (*CmdLine
), TRUE
))) {
2337 if (StrStr (CurrentParameter
, L
"-?") == CurrentParameter
) {
2338 CurrentParameter
[0] = L
' ';
2339 CurrentParameter
[1] = L
' ';
2340 NewCmdLineSize
= StrSize (L
"help ") + StrSize (*CmdLine
);
2341 NewCommandLine
= AllocateZeroPool (NewCmdLineSize
);
2342 if (NewCommandLine
== NULL
) {
2343 Status
= EFI_OUT_OF_RESOURCES
;
2348 // We know the space is sufficient since we just calculated it.
2350 StrnCpyS (NewCommandLine
, NewCmdLineSize
/sizeof (CHAR16
), L
"help ", 5);
2351 StrnCatS (NewCommandLine
, NewCmdLineSize
/sizeof (CHAR16
), *CmdLine
, StrLen (*CmdLine
));
2352 SHELL_FREE_NON_NULL (*CmdLine
);
2353 *CmdLine
= NewCommandLine
;
2359 SHELL_FREE_NON_NULL (CurrentParameter
);
2365 Function to update the shell variable "lasterror".
2367 @param[in] ErrorCode the error code to put into lasterror.
2371 IN CONST SHELL_STATUS ErrorCode
2374 CHAR16 LeString
[19];
2376 if (sizeof (EFI_STATUS
) == sizeof (UINT64
)) {
2377 UnicodeSPrint (LeString
, sizeof (LeString
), L
"0x%Lx", ErrorCode
);
2379 UnicodeSPrint (LeString
, sizeof (LeString
), L
"0x%x", ErrorCode
);
2383 InternalEfiShellSetEnv (L
"debuglasterror", LeString
, TRUE
);
2385 InternalEfiShellSetEnv (L
"lasterror", LeString
, TRUE
);
2387 return (EFI_SUCCESS
);
2391 Converts the command line to its post-processed form. this replaces variables and alias' per UEFI Shell spec.
2393 @param[in,out] CmdLine pointer to the command line to update
2395 @retval EFI_SUCCESS The operation was successful
2396 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
2397 @return some other error occurred
2400 ProcessCommandLineToFinal (
2401 IN OUT CHAR16
**CmdLine
2406 TrimSpaces (CmdLine
);
2408 Status
= ShellSubstituteAliases (CmdLine
);
2409 if (EFI_ERROR (Status
)) {
2413 TrimSpaces (CmdLine
);
2415 Status
= ShellSubstituteVariables (CmdLine
);
2416 if (EFI_ERROR (Status
)) {
2420 ASSERT (*CmdLine
!= NULL
);
2422 TrimSpaces (CmdLine
);
2425 // update for help parsing
2427 if (StrStr (*CmdLine
, L
"?") != NULL
) {
2429 // This may do nothing if the ? does not indicate help.
2430 // Save all the details for in the API below.
2432 Status
= DoHelpUpdate (CmdLine
);
2435 TrimSpaces (CmdLine
);
2437 return (EFI_SUCCESS
);
2441 Run an internal shell command.
2443 This API will update the shell's environment since these commands are libraries.
2445 @param[in] CmdLine the command line to run.
2446 @param[in] FirstParameter the first parameter on the command line
2447 @param[in] ParamProtocol the shell parameters protocol pointer
2448 @param[out] CommandStatus the status from the command line.
2450 @retval EFI_SUCCESS The command was completed.
2451 @retval EFI_ABORTED The command's operation was aborted.
2454 RunInternalCommand (
2455 IN CONST CHAR16
*CmdLine
,
2456 IN CHAR16
*FirstParameter
,
2457 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2458 OUT EFI_STATUS
*CommandStatus
2464 SHELL_STATUS CommandReturnedStatus
;
2469 NewCmdLine
= AllocateCopyPool (StrSize (CmdLine
), CmdLine
);
2470 if (NewCmdLine
== NULL
) {
2471 return EFI_OUT_OF_RESOURCES
;
2474 for (Walker
= NewCmdLine
; Walker
!= NULL
&& *Walker
!= CHAR_NULL
; Walker
++) {
2475 if ((*Walker
== L
'^') && (*(Walker
+1) == L
'#')) {
2476 CopyMem (Walker
, Walker
+1, StrSize (Walker
) - sizeof (Walker
[0]));
2481 // get the argc and argv updated for internal commands
2483 Status
= UpdateArgcArgv (ParamProtocol
, NewCmdLine
, Internal_Command
, &Argv
, &Argc
);
2484 if (!EFI_ERROR (Status
)) {
2486 // Run the internal command.
2488 Status
= ShellCommandRunCommandHandler (FirstParameter
, &CommandReturnedStatus
, &LastError
);
2490 if (!EFI_ERROR (Status
)) {
2491 if (CommandStatus
!= NULL
) {
2492 if (CommandReturnedStatus
!= SHELL_SUCCESS
) {
2493 *CommandStatus
= (EFI_STATUS
)(CommandReturnedStatus
| MAX_BIT
);
2495 *CommandStatus
= EFI_SUCCESS
;
2500 // Update last error status.
2501 // some commands do not update last error.
2504 SetLastError (CommandReturnedStatus
);
2508 // Pass thru the exitcode from the app.
2510 if (ShellCommandGetExit ()) {
2512 // An Exit was requested ("exit" command), pass its value up.
2514 Status
= CommandReturnedStatus
;
2515 } else if ((CommandReturnedStatus
!= SHELL_SUCCESS
) && IsScriptOnlyCommand (FirstParameter
)) {
2517 // Always abort when a script only command fails for any reason
2519 Status
= EFI_ABORTED
;
2520 } else if ((ShellCommandGetCurrentScriptFile () != NULL
) && (CommandReturnedStatus
== SHELL_ABORTED
)) {
2522 // Abort when in a script and a command aborted
2524 Status
= EFI_ABORTED
;
2530 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
2531 // This is safe even if the update API failed. In this case, it may be a no-op.
2533 RestoreArgcArgv (ParamProtocol
, &Argv
, &Argc
);
2536 // If a script is running and the command is not a script only command, then
2537 // change return value to success so the script won't halt (unless aborted).
2539 // Script only commands have to be able halt the script since the script will
2540 // not operate if they are failing.
2542 if ( (ShellCommandGetCurrentScriptFile () != NULL
)
2543 && !IsScriptOnlyCommand (FirstParameter
)
2544 && (Status
!= EFI_ABORTED
)
2547 Status
= EFI_SUCCESS
;
2550 FreePool (NewCmdLine
);
2555 Function to run the command or file.
2557 @param[in] Type the type of operation being run.
2558 @param[in] CmdLine the command line to run.
2559 @param[in] FirstParameter the first parameter on the command line
2560 @param[in] ParamProtocol the shell parameters protocol pointer
2561 @param[out] CommandStatus the status from the command line.
2563 @retval EFI_SUCCESS The command was completed.
2564 @retval EFI_ABORTED The command's operation was aborted.
2568 IN SHELL_OPERATION_TYPES Type
,
2569 IN CONST CHAR16
*CmdLine
,
2570 IN CHAR16
*FirstParameter
,
2571 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2572 OUT EFI_STATUS
*CommandStatus
2576 EFI_STATUS StartStatus
;
2577 CHAR16
*CommandWithPath
;
2578 CHAR16
*FullCommandWithPath
;
2579 EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
2580 SHELL_STATUS CalleeExitStatus
;
2582 Status
= EFI_SUCCESS
;
2583 CommandWithPath
= NULL
;
2585 CalleeExitStatus
= SHELL_INVALID_PARAMETER
;
2588 case Internal_Command
:
2589 Status
= RunInternalCommand (CmdLine
, FirstParameter
, ParamProtocol
, CommandStatus
);
2591 case Script_File_Name
:
2592 case Efi_Application
:
2594 // Process a fully qualified path
2596 if (StrStr (FirstParameter
, L
":") != NULL
) {
2597 ASSERT (CommandWithPath
== NULL
);
2598 if (ShellIsFile (FirstParameter
) == EFI_SUCCESS
) {
2599 CommandWithPath
= StrnCatGrow (&CommandWithPath
, NULL
, FirstParameter
, 0);
2604 // Process a relative path and also check in the path environment variable
2606 if (CommandWithPath
== NULL
) {
2607 CommandWithPath
= ShellFindFilePathEx (FirstParameter
, mExecutableExtensions
);
2611 // This should be impossible now.
2613 ASSERT (CommandWithPath
!= NULL
);
2616 // Make sure that path is not just a directory (or not found)
2618 if (!EFI_ERROR (ShellIsDirectory (CommandWithPath
))) {
2619 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2620 SetLastError (SHELL_NOT_FOUND
);
2624 case Script_File_Name
:
2625 FullCommandWithPath
= FullyQualifyPath (CommandWithPath
);
2626 if (FullCommandWithPath
== NULL
) {
2627 Status
= RunScriptFile (CommandWithPath
, NULL
, CmdLine
, ParamProtocol
);
2629 Status
= RunScriptFile (FullCommandWithPath
, NULL
, CmdLine
, ParamProtocol
);
2630 FreePool (FullCommandWithPath
);
2634 case Efi_Application
:
2636 // Get the device path of the application image
2638 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath (CommandWithPath
);
2639 if (DevPath
== NULL
) {
2640 Status
= EFI_OUT_OF_RESOURCES
;
2645 // Execute the device path
2647 Status
= InternalShellExecuteDevicePath (
2655 SHELL_FREE_NON_NULL (DevPath
);
2657 if (EFI_ERROR (Status
)) {
2658 CalleeExitStatus
= (SHELL_STATUS
)(Status
& (~MAX_BIT
));
2660 CalleeExitStatus
= (SHELL_STATUS
)StartStatus
;
2663 if (CommandStatus
!= NULL
) {
2664 *CommandStatus
= CalleeExitStatus
;
2668 // Update last error status.
2670 // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
2671 SetLastError (CalleeExitStatus
);
2688 SHELL_FREE_NON_NULL (CommandWithPath
);
2694 Function to setup StdIn, StdErr, StdOut, and then run the command or file.
2696 @param[in] Type the type of operation being run.
2697 @param[in] CmdLine the command line to run.
2698 @param[in] FirstParameter the first parameter on the command line.
2699 @param[in] ParamProtocol the shell parameters protocol pointer
2700 @param[out] CommandStatus the status from the command line.
2702 @retval EFI_SUCCESS The command was completed.
2703 @retval EFI_ABORTED The command's operation was aborted.
2706 SetupAndRunCommandOrFile (
2707 IN SHELL_OPERATION_TYPES Type
,
2709 IN CHAR16
*FirstParameter
,
2710 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2711 OUT EFI_STATUS
*CommandStatus
2715 SHELL_FILE_HANDLE OriginalStdIn
;
2716 SHELL_FILE_HANDLE OriginalStdOut
;
2717 SHELL_FILE_HANDLE OriginalStdErr
;
2718 SYSTEM_TABLE_INFO OriginalSystemTableInfo
;
2719 CONST SCRIPT_FILE
*ConstScriptFile
;
2722 // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
2724 Status
= UpdateStdInStdOutStdErr (ParamProtocol
, CmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2727 // The StdIn, StdOut, and StdErr are set up.
2728 // Now run the command, script, or application
2730 if (!EFI_ERROR (Status
)) {
2731 TrimSpaces (&CmdLine
);
2732 Status
= RunCommandOrFile (Type
, CmdLine
, FirstParameter
, ParamProtocol
, CommandStatus
);
2738 if (EFI_ERROR (Status
)) {
2739 ConstScriptFile
= ShellCommandGetCurrentScriptFile ();
2740 if ((ConstScriptFile
== NULL
) || (ConstScriptFile
->CurrentCommand
== NULL
)) {
2741 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
));
2743 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR_SCRIPT
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
), ConstScriptFile
->CurrentCommand
->Line
);
2748 // put back the original StdIn, StdOut, and StdErr
2750 RestoreStdInStdOutStdErr (ParamProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2756 Function will process and run a command line.
2758 This will determine if the command line represents an internal shell
2759 command or dispatch an external application.
2761 @param[in] CmdLine The command line to parse.
2762 @param[out] CommandStatus The status from the command line.
2764 @retval EFI_SUCCESS The command was completed.
2765 @retval EFI_ABORTED The command's operation was aborted.
2769 IN CONST CHAR16
*CmdLine
,
2770 OUT EFI_STATUS
*CommandStatus
2774 CHAR16
*CleanOriginal
;
2775 CHAR16
*FirstParameter
;
2777 SHELL_OPERATION_TYPES Type
;
2778 CONST CHAR16
*CurDir
;
2780 ASSERT (CmdLine
!= NULL
);
2781 if (StrLen (CmdLine
) == 0) {
2782 return (EFI_SUCCESS
);
2785 Status
= EFI_SUCCESS
;
2786 CleanOriginal
= NULL
;
2788 CleanOriginal
= StrnCatGrow (&CleanOriginal
, NULL
, CmdLine
, 0);
2789 if (CleanOriginal
== NULL
) {
2790 return (EFI_OUT_OF_RESOURCES
);
2793 TrimSpaces (&CleanOriginal
);
2796 // NULL out comments (leveraged from RunScriptFileHandle() ).
2797 // The # character on a line is used to denote that all characters on the same line
2798 // and to the right of the # are to be ignored by the shell.
2799 // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.
2801 for (TempWalker
= CleanOriginal
; TempWalker
!= NULL
&& *TempWalker
!= CHAR_NULL
; TempWalker
++) {
2802 if (*TempWalker
== L
'^') {
2803 if (*(TempWalker
+ 1) == L
'#') {
2806 } else if (*TempWalker
== L
'#') {
2807 *TempWalker
= CHAR_NULL
;
2811 TrimSpaces (&CleanOriginal
);
2814 // Handle case that passed in command line is just 1 or more " " characters.
2816 if (StrLen (CleanOriginal
) == 0) {
2817 SHELL_FREE_NON_NULL (CleanOriginal
);
2818 return (EFI_SUCCESS
);
2821 Status
= ProcessCommandLineToFinal (&CleanOriginal
);
2822 if (EFI_ERROR (Status
)) {
2823 SHELL_FREE_NON_NULL (CleanOriginal
);
2828 // We don't do normal processing with a split command line (output from one command input to another)
2830 if (ContainsSplit (CleanOriginal
)) {
2831 Status
= ProcessNewSplitCommandLine (CleanOriginal
);
2832 SHELL_FREE_NON_NULL (CleanOriginal
);
2837 // We need the first parameter information so we can determine the operation type
2839 FirstParameter
= AllocateZeroPool (StrSize (CleanOriginal
));
2840 if (FirstParameter
== NULL
) {
2841 SHELL_FREE_NON_NULL (CleanOriginal
);
2842 return (EFI_OUT_OF_RESOURCES
);
2845 TempWalker
= CleanOriginal
;
2846 if (!EFI_ERROR (GetNextParameter (&TempWalker
, &FirstParameter
, StrSize (CleanOriginal
), TRUE
))) {
2848 // Depending on the first parameter we change the behavior
2850 switch (Type
= GetOperationType (FirstParameter
)) {
2851 case File_Sys_Change
:
2852 Status
= ChangeMappedDrive (FirstParameter
);
2854 case Internal_Command
:
2855 case Script_File_Name
:
2856 case Efi_Application
:
2857 Status
= SetupAndRunCommandOrFile (Type
, CleanOriginal
, FirstParameter
, ShellInfoObject
.NewShellParametersProtocol
, CommandStatus
);
2861 // Whatever was typed, it was invalid.
2863 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2864 SetLastError (SHELL_NOT_FOUND
);
2868 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2869 SetLastError (SHELL_NOT_FOUND
);
2873 // Check whether the current file system still exists. If not exist, we need update "cwd" and gShellCurMapping.
2875 CurDir
= EfiShellGetCurDir (NULL
);
2876 if (CurDir
!= NULL
) {
2877 if (EFI_ERROR (ShellFileExists (CurDir
))) {
2879 // EfiShellSetCurDir() cannot set current directory to NULL.
2880 // EfiShellSetEnv() is not allowed to set the "cwd" variable.
2881 // Only InternalEfiShellSetEnv () is allowed setting the "cwd" variable.
2883 InternalEfiShellSetEnv (L
"cwd", NULL
, TRUE
);
2884 gShellCurMapping
= NULL
;
2888 SHELL_FREE_NON_NULL (CleanOriginal
);
2889 SHELL_FREE_NON_NULL (FirstParameter
);
2895 Function will process and run a command line.
2897 This will determine if the command line represents an internal shell
2898 command or dispatch an external application.
2900 @param[in] CmdLine The command line to parse.
2902 @retval EFI_SUCCESS The command was completed.
2903 @retval EFI_ABORTED The command's operation was aborted.
2907 IN CONST CHAR16
*CmdLine
2910 return (RunShellCommand (CmdLine
, NULL
));
2914 Function to process a NSH script file via SHELL_FILE_HANDLE.
2916 @param[in] Handle The handle to the already opened file.
2917 @param[in] Name The name of the script file.
2919 @retval EFI_SUCCESS the script completed successfully
2922 RunScriptFileHandle (
2923 IN SHELL_FILE_HANDLE Handle
,
2924 IN CONST CHAR16
*Name
2928 SCRIPT_FILE
*NewScriptFile
;
2930 UINTN PrintBuffSize
;
2931 CHAR16
*CommandLine
;
2932 CHAR16
*CommandLine2
;
2933 CHAR16
*CommandLine3
;
2934 SCRIPT_COMMAND_LIST
*LastCommand
;
2936 BOOLEAN PreScriptEchoState
;
2937 BOOLEAN PreCommandEchoState
;
2938 CONST CHAR16
*CurDir
;
2940 CHAR16 LeString
[50];
2941 LIST_ENTRY OldBufferList
;
2943 ASSERT (!ShellCommandGetScriptExit ());
2945 PreScriptEchoState
= ShellCommandGetEchoState ();
2946 PrintBuffSize
= PcdGet16 (PcdShellPrintBufferSize
);
2948 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool (sizeof (SCRIPT_FILE
));
2949 if (NewScriptFile
== NULL
) {
2950 return (EFI_OUT_OF_RESOURCES
);
2956 ASSERT (NewScriptFile
->ScriptName
== NULL
);
2957 NewScriptFile
->ScriptName
= StrnCatGrow (&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
2958 if (NewScriptFile
->ScriptName
== NULL
) {
2959 DeleteScriptFileStruct (NewScriptFile
);
2960 return (EFI_OUT_OF_RESOURCES
);
2964 // Save the parameters (used to replace %0 to %9 later on)
2966 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
2967 if (NewScriptFile
->Argc
!= 0) {
2968 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool (NewScriptFile
->Argc
* sizeof (CHAR16
*));
2969 if (NewScriptFile
->Argv
== NULL
) {
2970 DeleteScriptFileStruct (NewScriptFile
);
2971 return (EFI_OUT_OF_RESOURCES
);
2975 // Put the full path of the script file into Argv[0] as required by section
2976 // 3.6.2 of version 2.2 of the shell specification.
2978 NewScriptFile
->Argv
[0] = StrnCatGrow (&NewScriptFile
->Argv
[0], NULL
, NewScriptFile
->ScriptName
, 0);
2979 for (LoopVar
= 1; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
2980 ASSERT (NewScriptFile
->Argv
[LoopVar
] == NULL
);
2981 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow (&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
2982 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
2983 DeleteScriptFileStruct (NewScriptFile
);
2984 return (EFI_OUT_OF_RESOURCES
);
2988 NewScriptFile
->Argv
= NULL
;
2991 InitializeListHead (&NewScriptFile
->CommandList
);
2992 InitializeListHead (&NewScriptFile
->SubstList
);
2995 // Now build the list of all script commands.
2998 while (!ShellFileHandleEof (Handle
)) {
2999 CommandLine
= ShellFileHandleReturnLine (Handle
, &Ascii
);
3001 if ((CommandLine
== NULL
) || (StrLen (CommandLine
) == 0) || (CommandLine
[0] == '#')) {
3002 SHELL_FREE_NON_NULL (CommandLine
);
3006 NewScriptFile
->CurrentCommand
= AllocateZeroPool (sizeof (SCRIPT_COMMAND_LIST
));
3007 if (NewScriptFile
->CurrentCommand
== NULL
) {
3008 SHELL_FREE_NON_NULL (CommandLine
);
3009 DeleteScriptFileStruct (NewScriptFile
);
3010 return (EFI_OUT_OF_RESOURCES
);
3013 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
3014 NewScriptFile
->CurrentCommand
->Data
= NULL
;
3015 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
3017 InsertTailList (&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
3021 // Add this as the topmost script file
3023 ShellCommandSetNewScript (NewScriptFile
);
3026 // Now enumerate through the commands and run each one.
3028 CommandLine
= AllocateZeroPool (PrintBuffSize
);
3029 if (CommandLine
== NULL
) {
3030 DeleteScriptFileStruct (NewScriptFile
);
3031 return (EFI_OUT_OF_RESOURCES
);
3034 CommandLine2
= AllocateZeroPool (PrintBuffSize
);
3035 if (CommandLine2
== NULL
) {
3036 FreePool (CommandLine
);
3037 DeleteScriptFileStruct (NewScriptFile
);
3038 return (EFI_OUT_OF_RESOURCES
);
3041 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode (&NewScriptFile
->CommandList
)
3042 ; !IsNull (&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
3043 ; // conditional increment in the body of the loop
3046 ASSERT (CommandLine2
!= NULL
);
3049 PrintBuffSize
/sizeof (CHAR16
),
3050 NewScriptFile
->CurrentCommand
->Cl
,
3051 PrintBuffSize
/sizeof (CHAR16
) - 1
3054 SaveBufferList (&OldBufferList
);
3057 // NULL out comments
3059 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
3060 if (*CommandLine3
== L
'^') {
3061 if ( *(CommandLine3
+1) == L
':') {
3062 CopyMem (CommandLine3
, CommandLine3
+1, StrSize (CommandLine3
) - sizeof (CommandLine3
[0]));
3063 } else if (*(CommandLine3
+1) == L
'#') {
3066 } else if (*CommandLine3
== L
'#') {
3067 *CommandLine3
= CHAR_NULL
;
3071 if ((CommandLine2
!= NULL
) && (StrLen (CommandLine2
) >= 1)) {
3073 // Due to variability in starting the find and replace action we need to have both buffers the same.
3077 PrintBuffSize
/sizeof (CHAR16
),
3079 PrintBuffSize
/sizeof (CHAR16
) - 1
3083 // Remove the %0 to %9 from the command line (if we have some arguments)
3085 if (NewScriptFile
->Argv
!= NULL
) {
3086 switch (NewScriptFile
->Argc
) {
3088 Status
= ShellCopySearchAndReplace (CommandLine2
, CommandLine
, PrintBuffSize
, L
"%9", NewScriptFile
->Argv
[9], FALSE
, FALSE
);
3089 ASSERT_EFI_ERROR (Status
);
3091 Status
= ShellCopySearchAndReplace (CommandLine
, CommandLine2
, PrintBuffSize
, L
"%8", NewScriptFile
->Argv
[8], FALSE
, FALSE
);
3092 ASSERT_EFI_ERROR (Status
);
3094 Status
= ShellCopySearchAndReplace (CommandLine2
, CommandLine
, PrintBuffSize
, L
"%7", NewScriptFile
->Argv
[7], FALSE
, FALSE
);
3095 ASSERT_EFI_ERROR (Status
);
3097 Status
= ShellCopySearchAndReplace (CommandLine
, CommandLine2
, PrintBuffSize
, L
"%6", NewScriptFile
->Argv
[6], FALSE
, FALSE
);
3098 ASSERT_EFI_ERROR (Status
);
3100 Status
= ShellCopySearchAndReplace (CommandLine2
, CommandLine
, PrintBuffSize
, L
"%5", NewScriptFile
->Argv
[5], FALSE
, FALSE
);
3101 ASSERT_EFI_ERROR (Status
);
3103 Status
= ShellCopySearchAndReplace (CommandLine
, CommandLine2
, PrintBuffSize
, L
"%4", NewScriptFile
->Argv
[4], FALSE
, FALSE
);
3104 ASSERT_EFI_ERROR (Status
);
3106 Status
= ShellCopySearchAndReplace (CommandLine2
, CommandLine
, PrintBuffSize
, L
"%3", NewScriptFile
->Argv
[3], FALSE
, FALSE
);
3107 ASSERT_EFI_ERROR (Status
);
3109 Status
= ShellCopySearchAndReplace (CommandLine
, CommandLine2
, PrintBuffSize
, L
"%2", NewScriptFile
->Argv
[2], FALSE
, FALSE
);
3110 ASSERT_EFI_ERROR (Status
);
3112 Status
= ShellCopySearchAndReplace (CommandLine2
, CommandLine
, PrintBuffSize
, L
"%1", NewScriptFile
->Argv
[1], FALSE
, FALSE
);
3113 ASSERT_EFI_ERROR (Status
);
3115 Status
= ShellCopySearchAndReplace (CommandLine
, CommandLine2
, PrintBuffSize
, L
"%0", NewScriptFile
->Argv
[0], FALSE
, FALSE
);
3116 ASSERT_EFI_ERROR (Status
);
3123 Status
= ShellCopySearchAndReplace (CommandLine2
, CommandLine
, PrintBuffSize
, L
"%1", L
"\"\"", FALSE
, FALSE
);
3124 Status
= ShellCopySearchAndReplace (CommandLine
, CommandLine2
, PrintBuffSize
, L
"%2", L
"\"\"", FALSE
, FALSE
);
3125 Status
= ShellCopySearchAndReplace (CommandLine2
, CommandLine
, PrintBuffSize
, L
"%3", L
"\"\"", FALSE
, FALSE
);
3126 Status
= ShellCopySearchAndReplace (CommandLine
, CommandLine2
, PrintBuffSize
, L
"%4", L
"\"\"", FALSE
, FALSE
);
3127 Status
= ShellCopySearchAndReplace (CommandLine2
, CommandLine
, PrintBuffSize
, L
"%5", L
"\"\"", FALSE
, FALSE
);
3128 Status
= ShellCopySearchAndReplace (CommandLine
, CommandLine2
, PrintBuffSize
, L
"%6", L
"\"\"", FALSE
, FALSE
);
3129 Status
= ShellCopySearchAndReplace (CommandLine2
, CommandLine
, PrintBuffSize
, L
"%7", L
"\"\"", FALSE
, FALSE
);
3130 Status
= ShellCopySearchAndReplace (CommandLine
, CommandLine2
, PrintBuffSize
, L
"%8", L
"\"\"", FALSE
, FALSE
);
3131 Status
= ShellCopySearchAndReplace (CommandLine2
, CommandLine
, PrintBuffSize
, L
"%9", L
"\"\"", FALSE
, FALSE
);
3135 PrintBuffSize
/sizeof (CHAR16
),
3137 PrintBuffSize
/sizeof (CHAR16
) - 1
3140 LastCommand
= NewScriptFile
->CurrentCommand
;
3142 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' '; CommandLine3
++) {
3145 if ((CommandLine3
!= NULL
) && (CommandLine3
[0] == L
':')) {
3147 // This line is a goto target / label
3150 if ((CommandLine3
!= NULL
) && (StrLen (CommandLine3
) > 0)) {
3151 if (CommandLine3
[0] == L
'@') {
3153 // We need to save the current echo state
3154 // and disable echo for just this command.
3156 PreCommandEchoState
= ShellCommandGetEchoState ();
3157 ShellCommandSetEchoState (FALSE
);
3158 Status
= RunCommand (CommandLine3
+1);
3161 // If command was "@echo -off" or "@echo -on" then don't restore echo state
3163 if ((StrCmp (L
"@echo -off", CommandLine3
) != 0) &&
3164 (StrCmp (L
"@echo -on", CommandLine3
) != 0))
3167 // Now restore the pre-'@' echo state.
3169 ShellCommandSetEchoState (PreCommandEchoState
);
3172 if (ShellCommandGetEchoState ()) {
3173 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv (L
"cwd");
3174 if ((CurDir
!= NULL
) && (StrLen (CurDir
) > 1)) {
3175 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
3177 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
3180 ShellPrintEx (-1, -1, L
"%s\r\n", CommandLine2
);
3183 Status
= RunCommand (CommandLine3
);
3187 if (ShellCommandGetScriptExit ()) {
3189 // ShellCommandGetExitCode() always returns a UINT64
3191 UnicodeSPrint (LeString
, sizeof (LeString
), L
"0x%Lx", ShellCommandGetExitCode ());
3193 InternalEfiShellSetEnv (L
"debuglasterror", LeString
, TRUE
);
3195 InternalEfiShellSetEnv (L
"lasterror", LeString
, TRUE
);
3197 ShellCommandRegisterExit (FALSE
, 0);
3198 Status
= EFI_SUCCESS
;
3199 RestoreBufferList (&OldBufferList
);
3203 if (ShellGetExecutionBreakFlag ()) {
3204 RestoreBufferList (&OldBufferList
);
3208 if (EFI_ERROR (Status
)) {
3209 RestoreBufferList (&OldBufferList
);
3213 if (ShellCommandGetExit ()) {
3214 RestoreBufferList (&OldBufferList
);
3220 // If that commend did not update the CurrentCommand then we need to advance it...
3222 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
3223 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode (&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
3224 if (!IsNull (&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
3225 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
3229 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode (&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
3230 if (!IsNull (&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
3231 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
3235 RestoreBufferList (&OldBufferList
);
3238 FreePool (CommandLine
);
3239 FreePool (CommandLine2
);
3240 ShellCommandSetNewScript (NULL
);
3243 // Only if this was the last script reset the state.
3245 if (ShellCommandGetCurrentScriptFile () == NULL
) {
3246 ShellCommandSetEchoState (PreScriptEchoState
);
3249 return (EFI_SUCCESS
);
3253 Function to process a NSH script file.
3255 @param[in] ScriptPath Pointer to the script file name (including file system path).
3256 @param[in] Handle the handle of the script file already opened.
3257 @param[in] CmdLine the command line to run.
3258 @param[in] ParamProtocol the shell parameters protocol pointer
3260 @retval EFI_SUCCESS the script completed successfully
3264 IN CONST CHAR16
*ScriptPath
,
3265 IN SHELL_FILE_HANDLE Handle OPTIONAL
,
3266 IN CONST CHAR16
*CmdLine
,
3267 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
3271 SHELL_FILE_HANDLE FileHandle
;
3275 if (ShellIsFile (ScriptPath
) != EFI_SUCCESS
) {
3276 return (EFI_INVALID_PARAMETER
);
3280 // get the argc and argv updated for scripts
3282 Status
= UpdateArgcArgv (ParamProtocol
, CmdLine
, Script_File_Name
, &Argv
, &Argc
);
3283 if (!EFI_ERROR (Status
)) {
3284 if (Handle
== NULL
) {
3288 Status
= ShellOpenFileByName (ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
3289 if (!EFI_ERROR (Status
)) {
3293 Status
= RunScriptFileHandle (FileHandle
, ScriptPath
);
3296 // now close the file
3298 ShellCloseFile (&FileHandle
);
3301 Status
= RunScriptFileHandle (Handle
, ScriptPath
);
3306 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
3307 // This is safe even if the update API failed. In this case, it may be a no-op.
3309 RestoreArgcArgv (ParamProtocol
, &Argv
, &Argc
);
3315 Return the pointer to the first occurrence of any character from a list of characters.
3317 @param[in] String the string to parse
3318 @param[in] CharacterList the list of character to look for
3319 @param[in] EscapeCharacter An escape character to skip
3321 @return the location of the first character in the string
3322 @retval CHAR_NULL no instance of any character in CharacterList was found in String
3325 FindFirstCharacter (
3326 IN CONST CHAR16
*String
,
3327 IN CONST CHAR16
*CharacterList
,
3328 IN CONST CHAR16 EscapeCharacter
3334 for (WalkStr
= 0; WalkStr
< StrLen (String
); WalkStr
++) {
3335 if (String
[WalkStr
] == EscapeCharacter
) {
3340 for (WalkChar
= 0; WalkChar
< StrLen (CharacterList
); WalkChar
++) {
3341 if (String
[WalkStr
] == CharacterList
[WalkChar
]) {
3342 return (&String
[WalkStr
]);
3347 return (String
+ StrLen (String
));