2 This is THE shell (application)
4 Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 // Initialize the global structure
21 SHELL_INFO ShellInfoObject
= {
57 {{NULL
, NULL
}, NULL
, NULL
},
58 {{NULL
, NULL
}, NULL
, NULL
},
70 STATIC CONST CHAR16 mScriptExtension
[] = L
".NSH";
71 STATIC CONST CHAR16 mExecutableExtensions
[] = L
".NSH;.EFI";
72 STATIC CONST CHAR16 mStartupScript
[] = L
"startup.nsh";
73 CONST CHAR16 mNoNestingEnvVarName
[] = L
"nonesting";
74 CONST CHAR16 mNoNestingTrue
[] = L
"True";
75 CONST CHAR16 mNoNestingFalse
[] = L
"False";
78 Cleans off leading and trailing spaces and tabs.
80 @param[in] String pointer to the string to trim them off.
88 ASSERT(String
!= NULL
);
89 ASSERT(*String
!= NULL
);
91 // Remove any spaces and tabs at the beginning of the (*String).
93 while (((*String
)[0] == L
' ') || ((*String
)[0] == L
'\t')) {
94 CopyMem((*String
), (*String
)+1, StrSize((*String
)) - sizeof((*String
)[0]));
98 // Remove any spaces and tabs at the end of the (*String).
100 while ((StrLen (*String
) > 0) && (((*String
)[StrLen((*String
))-1] == L
' ') || ((*String
)[StrLen((*String
))-1] == L
'\t'))) {
101 (*String
)[StrLen((*String
))-1] = CHAR_NULL
;
104 return (EFI_SUCCESS
);
108 Parse for the next instance of one string within another string. Can optionally make sure that
109 the string was not escaped (^ character) per the shell specification.
111 @param[in] SourceString The string to search within
112 @param[in] FindString The string to look for
113 @param[in] CheckForEscapeCharacter TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances
118 IN CONST CHAR16
*SourceString
,
119 IN CONST CHAR16
*FindString
,
120 IN CONST BOOLEAN CheckForEscapeCharacter
124 if (SourceString
== NULL
) {
127 Temp
= StrStr(SourceString
, FindString
);
130 // If nothing found, or we don't care about escape characters
132 if (Temp
== NULL
|| !CheckForEscapeCharacter
) {
137 // If we found an escaped character, try again on the remainder of the string
139 if ((Temp
> (SourceString
)) && *(Temp
-1) == L
'^') {
140 return FindNextInstance(Temp
+1, FindString
, CheckForEscapeCharacter
);
144 // we found the right character
150 Check whether the string between a pair of % is a valid environment variable name.
152 @param[in] BeginPercent pointer to the first percent.
153 @param[in] EndPercent pointer to the last percent.
155 @retval TRUE is a valid environment variable name.
156 @retval FALSE is NOT a valid environment variable name.
159 IsValidEnvironmentVariableName(
160 IN CONST CHAR16
*BeginPercent
,
161 IN CONST CHAR16
*EndPercent
164 CONST CHAR16
*Walker
;
168 ASSERT (BeginPercent
!= NULL
);
169 ASSERT (EndPercent
!= NULL
);
170 ASSERT (BeginPercent
< EndPercent
);
172 if ((BeginPercent
+ 1) == EndPercent
) {
176 for (Walker
= BeginPercent
+ 1; Walker
< EndPercent
; Walker
++) {
178 (*Walker
>= L
'0' && *Walker
<= L
'9') ||
179 (*Walker
>= L
'A' && *Walker
<= L
'Z') ||
180 (*Walker
>= L
'a' && *Walker
<= L
'z') ||
183 if (Walker
== BeginPercent
+ 1 && (*Walker
>= L
'0' && *Walker
<= L
'9')) {
197 Determine if a command line contains a split operation
199 @param[in] CmdLine The command line to parse.
201 @retval TRUE CmdLine has a valid split.
202 @retval FALSE CmdLine does not have a valid split.
207 IN CONST CHAR16
*CmdLine
210 CONST CHAR16
*TempSpot
;
211 CONST CHAR16
*FirstQuote
;
212 CONST CHAR16
*SecondQuote
;
214 FirstQuote
= FindNextInstance (CmdLine
, L
"\"", TRUE
);
216 TempSpot
= FindFirstCharacter(CmdLine
, L
"|", L
'^');
218 if (FirstQuote
== NULL
||
220 TempSpot
== CHAR_NULL
||
221 FirstQuote
> TempSpot
223 return (BOOLEAN
) ((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
));
226 while ((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
)) {
227 if (FirstQuote
== NULL
|| FirstQuote
> TempSpot
) {
230 SecondQuote
= FindNextInstance (FirstQuote
+ 1, L
"\"", TRUE
);
231 if (SecondQuote
== NULL
) {
234 if (SecondQuote
< TempSpot
) {
235 FirstQuote
= FindNextInstance (SecondQuote
+ 1, L
"\"", TRUE
);
238 FirstQuote
= FindNextInstance (SecondQuote
+ 1, L
"\"", TRUE
);
239 TempSpot
= FindFirstCharacter(TempSpot
+ 1, L
"|", L
'^');
244 return (BOOLEAN
) ((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
));
248 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
249 feature's enabled state was not known when the shell initially launched.
251 @retval EFI_SUCCESS The feature is enabled.
252 @retval EFI_OUT_OF_RESOURCES There is not enough memory available.
256 InternalEfiShellStartCtrlSMonitor(
260 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
*SimpleEx
;
261 EFI_KEY_DATA KeyData
;
264 Status
= gBS
->OpenProtocol(
265 gST
->ConsoleInHandle
,
266 &gEfiSimpleTextInputExProtocolGuid
,
270 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
271 if (EFI_ERROR(Status
)) {
276 STRING_TOKEN (STR_SHELL_NO_IN_EX
),
277 ShellInfoObject
.HiiHandle
);
278 return (EFI_SUCCESS
);
281 KeyData
.KeyState
.KeyToggleState
= 0;
282 KeyData
.Key
.ScanCode
= 0;
283 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
284 KeyData
.Key
.UnicodeChar
= L
's';
286 Status
= SimpleEx
->RegisterKeyNotify(
289 NotificationFunction
,
290 &ShellInfoObject
.CtrlSNotifyHandle1
);
292 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
293 if (!EFI_ERROR(Status
)) {
294 Status
= SimpleEx
->RegisterKeyNotify(
297 NotificationFunction
,
298 &ShellInfoObject
.CtrlSNotifyHandle2
);
300 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
301 KeyData
.Key
.UnicodeChar
= 19;
303 if (!EFI_ERROR(Status
)) {
304 Status
= SimpleEx
->RegisterKeyNotify(
307 NotificationFunction
,
308 &ShellInfoObject
.CtrlSNotifyHandle3
);
310 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
311 if (!EFI_ERROR(Status
)) {
312 Status
= SimpleEx
->RegisterKeyNotify(
315 NotificationFunction
,
316 &ShellInfoObject
.CtrlSNotifyHandle4
);
324 The entry point for the application.
326 @param[in] ImageHandle The firmware allocated handle for the EFI image.
327 @param[in] SystemTable A pointer to the EFI System Table.
329 @retval EFI_SUCCESS The entry point is executed successfully.
330 @retval other Some error occurs when executing this entry point.
336 IN EFI_HANDLE ImageHandle
,
337 IN EFI_SYSTEM_TABLE
*SystemTable
343 EFI_HANDLE ConInHandle
;
344 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*OldConIn
;
346 if (PcdGet8(PcdShellSupportLevel
) > 3) {
347 return (EFI_UNSUPPORTED
);
353 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
354 if (EFI_ERROR(Status
)) {
359 // Populate the global structure from PCDs
361 ShellInfoObject
.ImageDevPath
= NULL
;
362 ShellInfoObject
.FileDevPath
= NULL
;
363 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
364 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
365 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
368 // verify we dont allow for spec violation
370 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
373 // Initialize the LIST ENTRY objects...
375 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
376 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
377 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
380 // Check PCDs for optional features that are not implemented yet.
382 if ( PcdGetBool(PcdShellSupportOldProtocols
)
383 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
384 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
386 return (EFI_UNSUPPORTED
);
390 // turn off the watchdog timer
392 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
395 // install our console logger. This will keep a log of the output for back-browsing
397 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
398 if (!EFI_ERROR(Status
)) {
400 // Enable the cursor to be visible
402 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
405 // If supporting EFI 1.1 we need to install HII protocol
406 // only do this if PcdShellRequireHiiPlatform == FALSE
408 // remove EFI_UNSUPPORTED check above when complete.
409 ///@todo add support for Framework HII
412 // install our (solitary) HII package
414 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
415 if (ShellInfoObject
.HiiHandle
== NULL
) {
416 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
417 ///@todo Add our package into Framework HII
419 if (ShellInfoObject
.HiiHandle
== NULL
) {
420 Status
= EFI_NOT_STARTED
;
426 // create and install the EfiShellParametersProtocol
428 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
429 ASSERT_EFI_ERROR(Status
);
430 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
433 // create and install the EfiShellProtocol
435 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
436 ASSERT_EFI_ERROR(Status
);
437 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
440 // Now initialize the shell library (it requires Shell Parameters protocol)
442 Status
= ShellInitialize();
443 ASSERT_EFI_ERROR(Status
);
445 Status
= CommandInit();
446 ASSERT_EFI_ERROR(Status
);
448 Status
= ShellInitEnvVarList ();
451 // Check the command line
453 Status
= ProcessCommandLine ();
454 if (EFI_ERROR (Status
)) {
459 // If shell support level is >= 1 create the mappings and paths
461 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
462 Status
= ShellCommandCreateInitialMappingsAndPaths();
466 // Set the environment variable for nesting support
470 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoNest
) {
472 // No change. require nesting in Shell Protocol Execute()
474 StrnCatGrow(&TempString
,
479 StrnCatGrow(&TempString
,
484 Status
= InternalEfiShellSetEnv(mNoNestingEnvVarName
, TempString
, TRUE
);
485 SHELL_FREE_NON_NULL(TempString
);
489 // save the device path for the loaded image and the device path for the filepath (under loaded image)
490 // These are where to look for the startup.nsh file
492 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
493 ASSERT_EFI_ERROR(Status
);
496 // Display the version
498 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
501 gST
->ConOut
->Mode
->CursorRow
,
503 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL
),
504 ShellInfoObject
.HiiHandle
,
505 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
506 gEfiShellProtocol
->MajorVersion
,
507 gEfiShellProtocol
->MinorVersion
514 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER
),
515 ShellInfoObject
.HiiHandle
,
516 (CHAR16
*) PcdGetPtr (PcdShellSupplier
)
523 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI
),
524 ShellInfoObject
.HiiHandle
,
525 (gST
->Hdr
.Revision
&0xffff0000)>>16,
526 (gST
->Hdr
.Revision
&0x0000ffff),
528 gST
->FirmwareRevision
533 // Display the mapping
535 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
536 Status
= RunCommand(L
"map");
537 ASSERT_EFI_ERROR(Status
);
541 // init all the built in alias'
543 Status
= SetBuiltInAlias();
544 ASSERT_EFI_ERROR(Status
);
547 // Initialize environment variables
549 if (ShellCommandGetProfileList() != NULL
) {
550 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
551 ASSERT_EFI_ERROR(Status
);
555 TempString
= AllocateZeroPool(Size
);
557 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
558 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
559 ASSERT_EFI_ERROR(Status
);
561 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
562 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
563 ASSERT_EFI_ERROR(Status
);
565 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
566 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
567 ASSERT_EFI_ERROR(Status
);
569 FreePool(TempString
);
571 if (!EFI_ERROR(Status
)) {
572 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
574 // Set up the event for CTRL-C monitoring...
576 Status
= InernalEfiShellStartMonitor();
579 if (!EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
581 // Set up the event for CTRL-S monitoring...
583 Status
= InternalEfiShellStartCtrlSMonitor();
586 if (!EFI_ERROR(Status
) && ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
588 // close off the gST->ConIn
590 OldConIn
= gST
->ConIn
;
591 ConInHandle
= gST
->ConsoleInHandle
;
592 gST
->ConIn
= CreateSimpleTextInOnFile((SHELL_FILE_HANDLE
)&FileInterfaceNulFile
, &gST
->ConsoleInHandle
);
598 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
600 // process the startup script or launch the called app.
602 Status
= DoStartupScript(ShellInfoObject
.ImageDevPath
, ShellInfoObject
.FileDevPath
);
605 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
&& !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
607 // begin the UI waiting loop
611 // clean out all the memory allocated for CONST <something> * return values
612 // between each shell prompt presentation
614 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
615 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
619 // Reset page break back to default.
621 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
622 ASSERT (ShellInfoObject
.ConsoleInfo
!= NULL
);
623 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
624 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
627 // Reset the CTRL-C event (yes we ignore the return values)
629 Status
= gBS
->CheckEvent (ShellInfoObject
.NewEfiShellProtocol
->ExecutionBreak
);
634 Status
= DoShellPrompt();
635 } while (!ShellCommandGetExit());
637 if (OldConIn
!= NULL
&& ConInHandle
!= NULL
) {
638 CloseSimpleTextInOnFile (gST
->ConIn
);
639 gST
->ConIn
= OldConIn
;
640 gST
->ConsoleInHandle
= ConInHandle
;
647 // uninstall protocols / free memory / etc...
649 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
650 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
651 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
653 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
654 FreePool(ShellInfoObject
.ImageDevPath
);
655 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
657 if (ShellInfoObject
.FileDevPath
!= NULL
) {
658 FreePool(ShellInfoObject
.FileDevPath
);
659 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
661 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
662 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
663 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
665 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
666 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
667 InternalEfiShellSetEnv(L
"cwd", NULL
, TRUE
);
669 CleanUpShellProtocol(ShellInfoObject
.NewEfiShellProtocol
);
670 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
673 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
674 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
677 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
678 ASSERT(FALSE
); ///@todo finish this de-allocation.
681 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
682 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
683 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
686 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
687 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
688 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
691 if (ShellInfoObject
.HiiHandle
!= NULL
) {
692 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
693 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
696 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
697 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
700 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
701 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
702 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
703 FreePool(ShellInfoObject
.ConsoleInfo
);
704 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
707 ShellFreeEnvVarList ();
709 if (ShellCommandGetExit()) {
710 return ((EFI_STATUS
)ShellCommandGetExitCode());
716 Sets all the alias' that were registered with the ShellCommandLib library.
718 @retval EFI_SUCCESS all init commands were run successfully.
726 CONST ALIAS_LIST
*List
;
730 // Get all the commands we want to run
732 List
= ShellCommandGetInitAliasList();
735 // for each command in the List
737 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
738 ; !IsNull (&List
->Link
, &Node
->Link
)
739 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
742 // install the alias'
744 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
745 ASSERT_EFI_ERROR(Status
);
747 return (EFI_SUCCESS
);
751 Internal function to determine if 2 command names are really the same.
753 @param[in] Command1 The pointer to the first command name.
754 @param[in] Command2 The pointer to the second command name.
756 @retval TRUE The 2 command names are the same.
757 @retval FALSE The 2 command names are not the same.
762 IN CONST CHAR16
*Command1
,
763 IN CONST CHAR16
*Command2
766 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
773 Internal function to determine if a command is a script only command.
775 @param[in] CommandName The pointer to the command name.
777 @retval TRUE The command is a script only command.
778 @retval FALSE The command is not a script only command.
783 IN CONST CHAR16
*CommandName
786 if (IsCommand(CommandName
, L
"for")
787 ||IsCommand(CommandName
, L
"endfor")
788 ||IsCommand(CommandName
, L
"if")
789 ||IsCommand(CommandName
, L
"else")
790 ||IsCommand(CommandName
, L
"endif")
791 ||IsCommand(CommandName
, L
"goto")) {
798 This function will populate the 2 device path protocol parameters based on the
799 global gImageHandle. The DevPath will point to the device path for the handle that has
800 loaded image protocol installed on it. The FilePath will point to the device path
801 for the file that was loaded.
803 @param[in, out] DevPath On a successful return the device path to the loaded image.
804 @param[in, out] FilePath On a successful return the device path to the file.
806 @retval EFI_SUCCESS The 2 device paths were successfully returned.
807 @retval other A error from gBS->HandleProtocol.
813 GetDevicePathsForImageAndFile (
814 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
815 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
819 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
820 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
822 ASSERT(DevPath
!= NULL
);
823 ASSERT(FilePath
!= NULL
);
825 Status
= gBS
->OpenProtocol (
827 &gEfiLoadedImageProtocolGuid
,
828 (VOID
**)&LoadedImage
,
831 EFI_OPEN_PROTOCOL_GET_PROTOCOL
833 if (!EFI_ERROR (Status
)) {
834 Status
= gBS
->OpenProtocol (
835 LoadedImage
->DeviceHandle
,
836 &gEfiDevicePathProtocolGuid
,
837 (VOID
**)&ImageDevicePath
,
840 EFI_OPEN_PROTOCOL_GET_PROTOCOL
842 if (!EFI_ERROR (Status
)) {
843 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
844 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
846 LoadedImage
->DeviceHandle
,
847 &gEfiDevicePathProtocolGuid
,
853 &gEfiLoadedImageProtocolGuid
,
861 Process all Uefi Shell 2.0 command line options.
863 see Uefi Shell 2.0 section 3.2 for full details.
865 the command line must resemble the following:
867 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
869 ShellOpt-options Options which control the initialization behavior of the shell.
870 These options are read from the EFI global variable "ShellOpt"
871 and are processed before options or file-name.
873 options Options which control the initialization behavior of the shell.
875 file-name The name of a UEFI shell application or script to be executed
876 after initialization is complete. By default, if file-name is
877 specified, then -nostartup is implied. Scripts are not supported
880 file-name-options The command-line options that are passed to file-name when it
883 This will initialize the ShellInfoObject.ShellInitSettings global variable.
885 @retval EFI_SUCCESS The variable is initialized.
896 CHAR16
*DelayValueStr
;
899 EFI_UNICODE_COLLATION_PROTOCOL
*UnicodeCollation
;
901 // `file-name-options` will contain arguments to `file-name` that we don't
902 // know about. This would cause ShellCommandLineParse to error, so we parse
903 // arguments manually, ignoring those after the first thing that doesn't look
904 // like a shell option (which is assumed to be `file-name`).
906 Status
= gBS
->LocateProtocol (
907 &gEfiUnicodeCollationProtocolGuid
,
909 (VOID
**) &UnicodeCollation
911 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 DelayValueStr
= gEfiShellParametersProtocol
->Argv
[LoopVar
+ 1];
1000 if (DelayValueStr
!= NULL
){
1001 if (*DelayValueStr
== L
':') {
1004 if (!EFI_ERROR(ShellConvertStringToUint64 (
1010 ShellInfoObject
.ShellInitSettings
.Delay
= (UINTN
)DelayValue
;
1014 } else if (UnicodeCollation
->StriColl (
1019 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= TRUE
;
1020 } else if (StrnCmp (L
"-", CurrentArg
, 1) == 0) {
1021 // Unrecognized option
1022 ShellPrintHiiEx(-1, -1, NULL
,
1023 STRING_TOKEN (STR_GEN_PROBLEM
),
1024 ShellInfoObject
.HiiHandle
,
1027 return EFI_INVALID_PARAMETER
;
1030 // First argument should be Shell.efi image name
1036 ShellInfoObject
.ShellInitSettings
.FileName
= AllocateCopyPool(StrSize(CurrentArg
), CurrentArg
);
1037 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
1038 return (EFI_OUT_OF_RESOURCES
);
1041 // We found `file-name`.
1043 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
1046 // Add `file-name-options`
1047 for (Size
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
1048 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
1049 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
1053 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1054 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
1055 return (EFI_OUT_OF_RESOURCES
);
1057 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
1059 gEfiShellParametersProtocol
->Argv
[LoopVar
],
1061 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1062 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
1063 return (EFI_OUT_OF_RESOURCES
);
1069 // "-nointerrupt" overrides "-delay"
1070 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
1071 ShellInfoObject
.ShellInitSettings
.Delay
= 0;
1078 Handles all interaction with the default startup script.
1080 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
1082 @param ImagePath the path to the image for shell. first place to look for the startup script
1083 @param FilePath the path to the file for shell. second place to look for the startup script.
1085 @retval EFI_SUCCESS the variable is initialized.
1090 IN EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
1091 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
1095 EFI_STATUS CalleeStatus
;
1098 SHELL_FILE_HANDLE FileHandle
;
1099 EFI_DEVICE_PATH_PROTOCOL
*NewPath
;
1100 EFI_DEVICE_PATH_PROTOCOL
*NamePath
;
1101 CHAR16
*FileStringPath
;
1104 CONST CHAR16
*MapName
;
1106 Key
.UnicodeChar
= CHAR_NULL
;
1110 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
1112 // launch something else instead
1114 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
1115 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1116 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
1118 FileStringPath
= AllocateZeroPool(NewSize
);
1119 if (FileStringPath
== NULL
) {
1120 return (EFI_OUT_OF_RESOURCES
);
1122 StrCpyS(FileStringPath
, NewSize
/sizeof(CHAR16
), ShellInfoObject
.ShellInitSettings
.FileName
);
1123 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1124 StrnCatS(FileStringPath
, NewSize
/sizeof(CHAR16
), L
" ", NewSize
/sizeof(CHAR16
) - StrLen(FileStringPath
) -1);
1125 StrnCatS(FileStringPath
, NewSize
/sizeof(CHAR16
), ShellInfoObject
.ShellInitSettings
.FileOptions
, NewSize
/sizeof(CHAR16
) - StrLen(FileStringPath
) -1);
1127 Status
= RunShellCommand(FileStringPath
, &CalleeStatus
);
1128 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
== TRUE
) {
1129 ShellCommandRegisterExit(gEfiShellProtocol
->BatchIsActive(), (UINT64
)CalleeStatus
);
1131 FreePool(FileStringPath
);
1137 // for shell level 0 we do no scripts
1138 // Without the Startup bit overriding we allow for nostartup to prevent scripts
1140 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
1141 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
1143 return (EFI_SUCCESS
);
1146 gST
->ConOut
->EnableCursor(gST
->ConOut
, FALSE
);
1148 // print out our warning and see if they press a key
1150 for ( Status
= EFI_UNSUPPORTED
, Delay
= ShellInfoObject
.ShellInitSettings
.Delay
1151 ; Delay
!= 0 && EFI_ERROR(Status
)
1154 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
);
1155 gBS
->Stall (1000000);
1156 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
1157 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
1160 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
1161 gST
->ConOut
->EnableCursor(gST
->ConOut
, TRUE
);
1166 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
1167 return (EFI_SUCCESS
);
1171 // Try the first location (must be file system)
1173 MapName
= ShellInfoObject
.NewEfiShellProtocol
->GetMapFromDevicePath(&ImagePath
);
1174 if (MapName
!= NULL
) {
1175 FileStringPath
= NULL
;
1177 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, MapName
, 0);
1178 if (FileStringPath
== NULL
) {
1179 Status
= EFI_OUT_OF_RESOURCES
;
1181 TempSpot
= StrStr(FileStringPath
, L
";");
1182 if (TempSpot
!= NULL
) {
1183 *TempSpot
= CHAR_NULL
;
1185 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, ((FILEPATH_DEVICE_PATH
*)FilePath
)->PathName
, 0);
1186 PathRemoveLastItem(FileStringPath
);
1187 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, mStartupScript
, 0);
1188 Status
= ShellInfoObject
.NewEfiShellProtocol
->OpenFileByName(FileStringPath
, &FileHandle
, EFI_FILE_MODE_READ
);
1189 FreePool(FileStringPath
);
1192 if (EFI_ERROR(Status
)) {
1193 NamePath
= FileDevicePath (NULL
, mStartupScript
);
1194 NewPath
= AppendDevicePathNode (ImagePath
, NamePath
);
1200 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
1204 // If we got a file, run it
1206 if (!EFI_ERROR(Status
) && FileHandle
!= NULL
) {
1207 Status
= RunScriptFile (mStartupScript
, FileHandle
, L
"", ShellInfoObject
.NewShellParametersProtocol
);
1208 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
1210 FileStringPath
= ShellFindFilePath(mStartupScript
);
1211 if (FileStringPath
== NULL
) {
1213 // we return success since we don't need to have a startup script
1215 Status
= EFI_SUCCESS
;
1216 ASSERT(FileHandle
== NULL
);
1218 Status
= RunScriptFile(FileStringPath
, NULL
, L
"", ShellInfoObject
.NewShellParametersProtocol
);
1219 FreePool(FileStringPath
);
1228 Function to perform the shell prompt looping. It will do a single prompt,
1229 dispatch the result, and then return. It is expected that the caller will
1230 call this function in a loop many times.
1233 @retval RETURN_ABORTED
1244 CONST CHAR16
*CurDir
;
1247 LIST_ENTRY OldBufferList
;
1252 // Get screen setting to decide size of the command line buffer
1254 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
1255 BufferSize
= Column
* Row
* sizeof (CHAR16
);
1256 CmdLine
= AllocateZeroPool (BufferSize
);
1257 if (CmdLine
== NULL
) {
1258 return EFI_OUT_OF_RESOURCES
;
1261 SaveBufferList(&OldBufferList
);
1262 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1267 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
1269 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1270 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1272 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1276 // Read a line from the console
1278 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
1281 // Null terminate the string and parse it
1283 if (!EFI_ERROR (Status
)) {
1284 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
1285 Status
= RunCommand(CmdLine
);
1289 // Done with this command
1291 RestoreBufferList(&OldBufferList
);
1297 Add a buffer to the Buffer To Free List for safely returning buffers to other
1298 places without risking letting them modify internal shell information.
1300 @param Buffer Something to pass to FreePool when the shell is exiting.
1304 AddBufferToFreeList(
1308 BUFFER_LIST
*BufferListEntry
;
1310 if (Buffer
== NULL
) {
1314 BufferListEntry
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1315 ASSERT(BufferListEntry
!= NULL
);
1316 BufferListEntry
->Buffer
= Buffer
;
1317 InsertTailList(&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
1323 Create a new buffer list and stores the old one to OldBufferList
1325 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
1329 OUT LIST_ENTRY
*OldBufferList
1332 CopyMem (OldBufferList
, &ShellInfoObject
.BufferToFreeList
.Link
, sizeof (LIST_ENTRY
));
1333 InitializeListHead (&ShellInfoObject
.BufferToFreeList
.Link
);
1337 Restore previous nodes into BufferToFreeList .
1339 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
1343 IN OUT LIST_ENTRY
*OldBufferList
1346 FreeBufferList (&ShellInfoObject
.BufferToFreeList
);
1347 CopyMem (&ShellInfoObject
.BufferToFreeList
.Link
, OldBufferList
, sizeof (LIST_ENTRY
));
1352 Add a buffer to the Line History List
1354 @param Buffer The line buffer to add.
1358 AddLineToCommandHistory(
1359 IN CONST CHAR16
*Buffer
1363 BUFFER_LIST
*Walker
;
1364 UINT16 MaxHistoryCmdCount
;
1368 MaxHistoryCmdCount
= PcdGet16(PcdShellMaxHistoryCommandCount
);
1370 if (MaxHistoryCmdCount
== 0) {
1375 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1376 ASSERT(Node
!= NULL
);
1377 Node
->Buffer
= AllocateCopyPool(StrSize(Buffer
), Buffer
);
1378 ASSERT(Node
->Buffer
!= NULL
);
1380 for ( Walker
= (BUFFER_LIST
*)GetFirstNode(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)
1381 ; !IsNull(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Walker
->Link
)
1382 ; Walker
= (BUFFER_LIST
*)GetNextNode(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Walker
->Link
)
1386 if (Count
< MaxHistoryCmdCount
){
1387 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1389 Walker
= (BUFFER_LIST
*)GetFirstNode(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
1390 RemoveEntryList(&Walker
->Link
);
1391 if (Walker
->Buffer
!= NULL
) {
1392 FreePool(Walker
->Buffer
);
1395 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1400 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1401 with the correct command name.
1403 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1404 command name if it was an alias. If it was not
1405 an alias it will be unchanged. This function may
1406 change the buffer to fit the command name.
1408 @retval EFI_SUCCESS The name was changed.
1409 @retval EFI_SUCCESS The name was not an alias.
1410 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1415 IN OUT CHAR16
**CommandString
1418 CONST CHAR16
*NewString
;
1420 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
1421 if (NewString
== NULL
) {
1422 return (EFI_SUCCESS
);
1424 FreePool(*CommandString
);
1425 *CommandString
= AllocateCopyPool(StrSize(NewString
), NewString
);
1426 if (*CommandString
== NULL
) {
1427 return (EFI_OUT_OF_RESOURCES
);
1429 return (EFI_SUCCESS
);
1433 This function will eliminate unreplaced (and therefore non-found) environment variables.
1435 @param[in,out] CmdLine The command line to update.
1439 StripUnreplacedEnvironmentVariables(
1440 IN OUT CHAR16
*CmdLine
1443 CHAR16
*FirstPercent
;
1445 CHAR16
*SecondPercent
;
1446 CHAR16
*SecondQuote
;
1447 CHAR16
*CurrentLocator
;
1449 for (CurrentLocator
= CmdLine
; CurrentLocator
!= NULL
; ) {
1450 FirstQuote
= FindNextInstance(CurrentLocator
, L
"\"", TRUE
);
1451 FirstPercent
= FindNextInstance(CurrentLocator
, L
"%", TRUE
);
1452 SecondPercent
= FirstPercent
!=NULL
?FindNextInstance(FirstPercent
+1, L
"%", TRUE
):NULL
;
1453 if (FirstPercent
== NULL
|| SecondPercent
== NULL
) {
1455 // If we ever don't have 2 % we are done.
1460 if (FirstQuote
!= NULL
&& FirstQuote
< FirstPercent
) {
1461 SecondQuote
= FindNextInstance(FirstQuote
+1, L
"\"", TRUE
);
1463 // Quote is first found
1466 if (SecondQuote
< FirstPercent
) {
1468 // restart after the pair of "
1470 CurrentLocator
= SecondQuote
+ 1;
1471 } else /* FirstPercent < SecondQuote */{
1473 // Restart on the first percent
1475 CurrentLocator
= FirstPercent
;
1480 if (FirstQuote
== NULL
|| SecondPercent
< FirstQuote
) {
1481 if (IsValidEnvironmentVariableName(FirstPercent
, SecondPercent
)) {
1483 // We need to remove from FirstPercent to SecondPercent
1485 CopyMem(FirstPercent
, SecondPercent
+ 1, StrSize(SecondPercent
+ 1));
1487 // don't need to update the locator. both % characters are gone.
1490 CurrentLocator
= SecondPercent
+ 1;
1494 CurrentLocator
= FirstQuote
;
1496 return (EFI_SUCCESS
);
1500 Function allocates a new command line and replaces all instances of environment
1501 variable names that are correctly preset to their values.
1503 If the return value is not NULL the memory must be caller freed.
1505 @param[in] OriginalCommandLine The original command line
1507 @retval NULL An error occurred.
1508 @return The new command line with no environment variables present.
1512 ShellConvertVariables (
1513 IN CONST CHAR16
*OriginalCommandLine
1516 CONST CHAR16
*MasterEnvList
;
1518 CHAR16
*NewCommandLine1
;
1519 CHAR16
*NewCommandLine2
;
1523 SCRIPT_FILE
*CurrentScriptFile
;
1524 ALIAS_LIST
*AliasListNode
;
1526 ASSERT(OriginalCommandLine
!= NULL
);
1529 NewSize
= StrSize(OriginalCommandLine
);
1530 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
1533 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1536 // calculate the size required for the post-conversion string...
1538 if (CurrentScriptFile
!= NULL
) {
1539 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1540 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1541 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1543 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
1545 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
1548 // we need a preceding and if there is space no ^ preceding (if no space ignore)
1550 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
1551 NewSize
+= StrSize(AliasListNode
->CommandString
);
1557 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1558 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1559 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1561 if (StrSize(MasterEnvList
) > ItemSize
) {
1562 ItemSize
= StrSize(MasterEnvList
);
1564 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
1566 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
1569 // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)
1571 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
1572 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
1573 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
1579 // now do the replacements...
1581 NewCommandLine1
= AllocateCopyPool(NewSize
, OriginalCommandLine
);
1582 NewCommandLine2
= AllocateZeroPool(NewSize
);
1583 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
1584 if (NewCommandLine1
== NULL
|| NewCommandLine2
== NULL
|| ItemTemp
== NULL
) {
1585 SHELL_FREE_NON_NULL(NewCommandLine1
);
1586 SHELL_FREE_NON_NULL(NewCommandLine2
);
1587 SHELL_FREE_NON_NULL(ItemTemp
);
1590 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1591 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
1592 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1595 ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
)),
1599 ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
)),
1603 ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
)),
1606 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1607 StrCpyS(NewCommandLine1
, NewSize
/sizeof(CHAR16
), NewCommandLine2
);
1609 if (CurrentScriptFile
!= NULL
) {
1610 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1611 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1612 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1614 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1615 StrCpyS(NewCommandLine1
, NewSize
/sizeof(CHAR16
), NewCommandLine2
);
1620 // Remove non-existent environment variables
1622 StripUnreplacedEnvironmentVariables(NewCommandLine1
);
1625 // Now cleanup any straggler intentionally ignored "%" characters
1627 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, L
"^%", L
"%", TRUE
, FALSE
);
1628 StrCpyS(NewCommandLine1
, NewSize
/sizeof(CHAR16
), NewCommandLine2
);
1630 FreePool(NewCommandLine2
);
1633 return (NewCommandLine1
);
1637 Internal function to run a command line with pipe usage.
1639 @param[in] CmdLine The pointer to the command line.
1640 @param[in] StdIn The pointer to the Standard input.
1641 @param[in] StdOut The pointer to the Standard output.
1643 @retval EFI_SUCCESS The split command is executed successfully.
1644 @retval other Some error occurs when executing the split command.
1649 IN CONST CHAR16
*CmdLine
,
1650 IN SHELL_FILE_HANDLE
*StdIn
,
1651 IN SHELL_FILE_HANDLE
*StdOut
1655 CHAR16
*NextCommandLine
;
1656 CHAR16
*OurCommandLine
;
1660 SHELL_FILE_HANDLE
*TempFileHandle
;
1663 ASSERT(StdOut
== NULL
);
1665 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1667 Status
= EFI_SUCCESS
;
1668 NextCommandLine
= NULL
;
1669 OurCommandLine
= NULL
;
1673 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1674 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1676 if (NextCommandLine
== NULL
|| OurCommandLine
== NULL
) {
1677 SHELL_FREE_NON_NULL(OurCommandLine
);
1678 SHELL_FREE_NON_NULL(NextCommandLine
);
1679 return (EFI_OUT_OF_RESOURCES
);
1680 } else if (StrStr(OurCommandLine
, L
"|") != NULL
|| Size1
== 0 || Size2
== 0) {
1681 SHELL_FREE_NON_NULL(OurCommandLine
);
1682 SHELL_FREE_NON_NULL(NextCommandLine
);
1683 return (EFI_INVALID_PARAMETER
);
1684 } else if (NextCommandLine
[0] == L
'a' &&
1685 (NextCommandLine
[1] == L
' ' || NextCommandLine
[1] == CHAR_NULL
)
1687 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1688 while (NextCommandLine
[0] == L
' ') {
1689 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1691 if (NextCommandLine
[0] == CHAR_NULL
) {
1692 SHELL_FREE_NON_NULL(OurCommandLine
);
1693 SHELL_FREE_NON_NULL(NextCommandLine
);
1694 return (EFI_INVALID_PARAMETER
);
1703 // make a SPLIT_LIST item and add to list
1705 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1706 ASSERT(Split
!= NULL
);
1707 Split
->SplitStdIn
= StdIn
;
1708 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1709 ASSERT(Split
->SplitStdOut
!= NULL
);
1710 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1712 Status
= RunCommand(OurCommandLine
);
1715 // move the output from the first to the in to the second.
1717 TempFileHandle
= Split
->SplitStdOut
;
1718 if (Split
->SplitStdIn
== StdIn
) {
1719 Split
->SplitStdOut
= NULL
;
1721 Split
->SplitStdOut
= Split
->SplitStdIn
;
1723 Split
->SplitStdIn
= TempFileHandle
;
1724 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
), 0);
1726 if (!EFI_ERROR(Status
)) {
1727 Status
= RunCommand(NextCommandLine
);
1731 // remove the top level from the ScriptList
1733 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1734 RemoveEntryList(&Split
->Link
);
1737 // Note that the original StdIn is now the StdOut...
1739 if (Split
->SplitStdOut
!= NULL
&& Split
->SplitStdOut
!= StdIn
) {
1740 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdOut
));
1742 if (Split
->SplitStdIn
!= NULL
) {
1743 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
));
1747 FreePool(NextCommandLine
);
1748 FreePool(OurCommandLine
);
1754 Take the original command line, substitute any variables, free
1755 the original string, return the modified copy.
1757 @param[in] CmdLine pointer to the command line to update.
1759 @retval EFI_SUCCESS the function was successful.
1760 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1764 ShellSubstituteVariables(
1769 NewCmdLine
= ShellConvertVariables(*CmdLine
);
1770 SHELL_FREE_NON_NULL(*CmdLine
);
1771 if (NewCmdLine
== NULL
) {
1772 return (EFI_OUT_OF_RESOURCES
);
1774 *CmdLine
= NewCmdLine
;
1775 return (EFI_SUCCESS
);
1779 Take the original command line, substitute any alias in the first group of space delimited characters, free
1780 the original string, return the modified copy.
1782 @param[in] CmdLine pointer to the command line to update.
1784 @retval EFI_SUCCESS the function was successful.
1785 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1789 ShellSubstituteAliases(
1794 CHAR16
*CommandName
;
1796 UINTN PostAliasSize
;
1797 ASSERT(CmdLine
!= NULL
);
1798 ASSERT(*CmdLine
!= NULL
);
1802 if (StrStr((*CmdLine
), L
" ") == NULL
){
1803 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), 0);
1805 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), StrStr((*CmdLine
), L
" ") - (*CmdLine
));
1809 // This cannot happen 'inline' since the CmdLine can need extra space.
1812 if (!ShellCommandIsCommandOnList(CommandName
)) {
1814 // Convert via alias
1816 Status
= ShellConvertAlias(&CommandName
);
1817 if (EFI_ERROR(Status
)){
1821 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, CommandName
, 0);
1822 if (NewCmdLine
== NULL
) {
1823 SHELL_FREE_NON_NULL(CommandName
);
1824 SHELL_FREE_NON_NULL(*CmdLine
);
1825 return (EFI_OUT_OF_RESOURCES
);
1827 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, StrStr((*CmdLine
), L
" "), 0);
1828 if (NewCmdLine
== NULL
) {
1829 SHELL_FREE_NON_NULL(CommandName
);
1830 SHELL_FREE_NON_NULL(*CmdLine
);
1831 return (EFI_OUT_OF_RESOURCES
);
1834 NewCmdLine
= StrnCatGrow(&NewCmdLine
, NULL
, (*CmdLine
), 0);
1837 SHELL_FREE_NON_NULL(*CmdLine
);
1838 SHELL_FREE_NON_NULL(CommandName
);
1841 // re-assign the passed in double pointer to point to our newly allocated buffer
1843 *CmdLine
= NewCmdLine
;
1845 return (EFI_SUCCESS
);
1849 Takes the Argv[0] part of the command line and determine the meaning of it.
1851 @param[in] CmdName pointer to the command line to update.
1853 @retval Internal_Command The name is an internal command.
1854 @retval File_Sys_Change the name is a file system change.
1855 @retval Script_File_Name the name is a NSH script file.
1856 @retval Unknown_Invalid the name is unknown.
1857 @retval Efi_Application the name is an application (.EFI).
1859 SHELL_OPERATION_TYPES
1862 IN CONST CHAR16
*CmdName
1865 CHAR16
* FileWithPath
;
1866 CONST CHAR16
* TempLocation
;
1867 CONST CHAR16
* TempLocation2
;
1869 FileWithPath
= NULL
;
1871 // test for an internal command.
1873 if (ShellCommandIsCommandOnList(CmdName
)) {
1874 return (Internal_Command
);
1878 // Test for file system change request. anything ending with first : and cant have spaces.
1880 if (CmdName
[(StrLen(CmdName
)-1)] == L
':') {
1881 if ( StrStr(CmdName
, L
" ") != NULL
1882 || StrLen(StrStr(CmdName
, L
":")) > 1
1884 return (Unknown_Invalid
);
1886 return (File_Sys_Change
);
1892 if ((FileWithPath
= ShellFindFilePathEx(CmdName
, mExecutableExtensions
)) != NULL
) {
1894 // See if that file has a script file extension
1896 if (StrLen(FileWithPath
) > 4) {
1897 TempLocation
= FileWithPath
+StrLen(FileWithPath
)-4;
1898 TempLocation2
= mScriptExtension
;
1899 if (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0) {
1900 SHELL_FREE_NON_NULL(FileWithPath
);
1901 return (Script_File_Name
);
1906 // Was a file, but not a script. we treat this as an application.
1908 SHELL_FREE_NON_NULL(FileWithPath
);
1909 return (Efi_Application
);
1912 SHELL_FREE_NON_NULL(FileWithPath
);
1914 // No clue what this is... return invalid flag...
1916 return (Unknown_Invalid
);
1920 Determine if the first item in a command line is valid.
1922 @param[in] CmdLine The command line to parse.
1924 @retval EFI_SUCCESS The item is valid.
1925 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1926 @retval EFI_NOT_FOUND The operation type is unknown or invalid.
1931 IN CONST CHAR16
*CmdLine
1935 CHAR16
*FirstParameter
;
1941 Temp
= StrnCatGrow(&Temp
, NULL
, CmdLine
, 0);
1943 return (EFI_OUT_OF_RESOURCES
);
1946 FirstParameter
= StrStr(Temp
, L
"|");
1947 if (FirstParameter
!= NULL
) {
1948 *FirstParameter
= CHAR_NULL
;
1951 FirstParameter
= NULL
;
1954 // Process the command line
1956 Status
= ProcessCommandLineToFinal(&Temp
);
1958 if (!EFI_ERROR(Status
)) {
1959 FirstParameter
= AllocateZeroPool(StrSize(CmdLine
));
1960 if (FirstParameter
== NULL
) {
1961 SHELL_FREE_NON_NULL(Temp
);
1962 return (EFI_OUT_OF_RESOURCES
);
1964 TempWalker
= (CHAR16
*)Temp
;
1965 if (!EFI_ERROR(GetNextParameter(&TempWalker
, &FirstParameter
, StrSize(CmdLine
), TRUE
))) {
1966 if (GetOperationType(FirstParameter
) == Unknown_Invalid
) {
1967 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
1968 SetLastError(SHELL_NOT_FOUND
);
1969 Status
= EFI_NOT_FOUND
;
1974 SHELL_FREE_NON_NULL(Temp
);
1975 SHELL_FREE_NON_NULL(FirstParameter
);
1980 Determine if a command line contains with a split contains only valid commands.
1982 @param[in] CmdLine The command line to parse.
1984 @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.
1985 @retval EFI_ABORTED CmdLine has at least one invalid command or application.
1990 IN CONST CHAR16
*CmdLine
1993 CONST CHAR16
*TempSpot
;
1997 // If this was the only item, then get out
1999 if (!ContainsSplit(CmdLine
)) {
2000 return (EFI_SUCCESS
);
2004 // Verify up to the pipe or end character
2006 Status
= IsValidSplit(CmdLine
);
2007 if (EFI_ERROR(Status
)) {
2012 // recurse to verify the next item
2014 TempSpot
= FindFirstCharacter(CmdLine
, L
"|", L
'^') + 1;
2015 if (*TempSpot
== L
'a' &&
2016 (*(TempSpot
+ 1) == L
' ' || *(TempSpot
+ 1) == CHAR_NULL
)
2018 // If it's an ASCII pipe '|a'
2022 return (VerifySplit(TempSpot
));
2026 Process a split based operation.
2028 @param[in] CmdLine pointer to the command line to process
2030 @retval EFI_SUCCESS The operation was successful
2031 @return an error occurred.
2035 ProcessNewSplitCommandLine(
2036 IN CONST CHAR16
*CmdLine
2042 Status
= VerifySplit(CmdLine
);
2043 if (EFI_ERROR(Status
)) {
2050 // are we in an existing split???
2052 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
2053 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
2056 if (Split
== NULL
) {
2057 Status
= RunSplitCommand(CmdLine
, NULL
, NULL
);
2059 Status
= RunSplitCommand(CmdLine
, Split
->SplitStdIn
, Split
->SplitStdOut
);
2061 if (EFI_ERROR(Status
)) {
2062 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_SPLIT
), ShellInfoObject
.HiiHandle
, CmdLine
);
2068 Handle a request to change the current file system.
2070 @param[in] CmdLine The passed in command line.
2072 @retval EFI_SUCCESS The operation was successful.
2077 IN CONST CHAR16
*CmdLine
2081 Status
= EFI_SUCCESS
;
2084 // make sure we are the right operation
2086 ASSERT(CmdLine
[(StrLen(CmdLine
)-1)] == L
':' && StrStr(CmdLine
, L
" ") == NULL
);
2089 // Call the protocol API to do the work
2091 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, CmdLine
);
2094 // Report any errors
2096 if (EFI_ERROR(Status
)) {
2097 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, CmdLine
);
2104 Reprocess the command line to direct all -? to the help command.
2106 if found, will add "help" as argv[0], and move the rest later.
2108 @param[in,out] CmdLine pointer to the command line to update
2113 IN OUT CHAR16
**CmdLine
2116 CHAR16
*CurrentParameter
;
2118 CHAR16
*NewCommandLine
;
2120 UINTN NewCmdLineSize
;
2122 Status
= EFI_SUCCESS
;
2124 CurrentParameter
= AllocateZeroPool(StrSize(*CmdLine
));
2125 if (CurrentParameter
== NULL
) {
2126 return (EFI_OUT_OF_RESOURCES
);
2130 while(Walker
!= NULL
&& *Walker
!= CHAR_NULL
) {
2131 if (!EFI_ERROR(GetNextParameter(&Walker
, &CurrentParameter
, StrSize(*CmdLine
), TRUE
))) {
2132 if (StrStr(CurrentParameter
, L
"-?") == CurrentParameter
) {
2133 CurrentParameter
[0] = L
' ';
2134 CurrentParameter
[1] = L
' ';
2135 NewCmdLineSize
= StrSize(L
"help ") + StrSize(*CmdLine
);
2136 NewCommandLine
= AllocateZeroPool(NewCmdLineSize
);
2137 if (NewCommandLine
== NULL
) {
2138 Status
= EFI_OUT_OF_RESOURCES
;
2143 // We know the space is sufficient since we just calculated it.
2145 StrnCpyS(NewCommandLine
, NewCmdLineSize
/sizeof(CHAR16
), L
"help ", 5);
2146 StrnCatS(NewCommandLine
, NewCmdLineSize
/sizeof(CHAR16
), *CmdLine
, StrLen(*CmdLine
));
2147 SHELL_FREE_NON_NULL(*CmdLine
);
2148 *CmdLine
= NewCommandLine
;
2154 SHELL_FREE_NON_NULL(CurrentParameter
);
2160 Function to update the shell variable "lasterror".
2162 @param[in] ErrorCode the error code to put into lasterror.
2167 IN CONST SHELL_STATUS ErrorCode
2170 CHAR16 LeString
[19];
2171 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
2172 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ErrorCode
);
2174 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", ErrorCode
);
2176 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
2177 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
2179 return (EFI_SUCCESS
);
2183 Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.
2185 @param[in,out] CmdLine pointer to the command line to update
2187 @retval EFI_SUCCESS The operation was successful
2188 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
2189 @return some other error occurred
2193 ProcessCommandLineToFinal(
2194 IN OUT CHAR16
**CmdLine
2198 TrimSpaces(CmdLine
);
2200 Status
= ShellSubstituteAliases(CmdLine
);
2201 if (EFI_ERROR(Status
)) {
2205 TrimSpaces(CmdLine
);
2207 Status
= ShellSubstituteVariables(CmdLine
);
2208 if (EFI_ERROR(Status
)) {
2211 ASSERT (*CmdLine
!= NULL
);
2213 TrimSpaces(CmdLine
);
2216 // update for help parsing
2218 if (StrStr(*CmdLine
, L
"?") != NULL
) {
2220 // This may do nothing if the ? does not indicate help.
2221 // Save all the details for in the API below.
2223 Status
= DoHelpUpdate(CmdLine
);
2226 TrimSpaces(CmdLine
);
2228 return (EFI_SUCCESS
);
2232 Run an internal shell command.
2234 This API will update the shell's environment since these commands are libraries.
2236 @param[in] CmdLine the command line to run.
2237 @param[in] FirstParameter the first parameter on the command line
2238 @param[in] ParamProtocol the shell parameters protocol pointer
2239 @param[out] CommandStatus the status from the command line.
2241 @retval EFI_SUCCESS The command was completed.
2242 @retval EFI_ABORTED The command's operation was aborted.
2247 IN CONST CHAR16
*CmdLine
,
2248 IN CHAR16
*FirstParameter
,
2249 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2250 OUT EFI_STATUS
*CommandStatus
2256 SHELL_STATUS CommandReturnedStatus
;
2261 NewCmdLine
= AllocateCopyPool (StrSize (CmdLine
), CmdLine
);
2262 if (NewCmdLine
== NULL
) {
2263 return EFI_OUT_OF_RESOURCES
;
2266 for (Walker
= NewCmdLine
; Walker
!= NULL
&& *Walker
!= CHAR_NULL
; Walker
++) {
2267 if (*Walker
== L
'^' && *(Walker
+1) == L
'#') {
2268 CopyMem(Walker
, Walker
+1, StrSize(Walker
) - sizeof(Walker
[0]));
2273 // get the argc and argv updated for internal commands
2275 Status
= UpdateArgcArgv(ParamProtocol
, NewCmdLine
, Internal_Command
, &Argv
, &Argc
);
2276 if (!EFI_ERROR(Status
)) {
2278 // Run the internal command.
2280 Status
= ShellCommandRunCommandHandler(FirstParameter
, &CommandReturnedStatus
, &LastError
);
2282 if (!EFI_ERROR(Status
)) {
2283 if (CommandStatus
!= NULL
) {
2284 if (CommandReturnedStatus
!= SHELL_SUCCESS
) {
2285 *CommandStatus
= (EFI_STATUS
)(CommandReturnedStatus
| MAX_BIT
);
2287 *CommandStatus
= EFI_SUCCESS
;
2292 // Update last error status.
2293 // some commands do not update last error.
2296 SetLastError(CommandReturnedStatus
);
2300 // Pass thru the exitcode from the app.
2302 if (ShellCommandGetExit()) {
2304 // An Exit was requested ("exit" command), pass its value up.
2306 Status
= CommandReturnedStatus
;
2307 } else if (CommandReturnedStatus
!= SHELL_SUCCESS
&& IsScriptOnlyCommand(FirstParameter
)) {
2309 // Always abort when a script only command fails for any reason
2311 Status
= EFI_ABORTED
;
2312 } else if (ShellCommandGetCurrentScriptFile() != NULL
&& CommandReturnedStatus
== SHELL_ABORTED
) {
2314 // Abort when in a script and a command aborted
2316 Status
= EFI_ABORTED
;
2322 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
2323 // This is safe even if the update API failed. In this case, it may be a no-op.
2325 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);
2328 // If a script is running and the command is not a script only command, then
2329 // change return value to success so the script won't halt (unless aborted).
2331 // Script only commands have to be able halt the script since the script will
2332 // not operate if they are failing.
2334 if ( ShellCommandGetCurrentScriptFile() != NULL
2335 && !IsScriptOnlyCommand(FirstParameter
)
2336 && Status
!= EFI_ABORTED
2338 Status
= EFI_SUCCESS
;
2341 FreePool (NewCmdLine
);
2346 Function to run the command or file.
2348 @param[in] Type the type of operation being run.
2349 @param[in] CmdLine the command line to run.
2350 @param[in] FirstParameter the first parameter on the command line
2351 @param[in] ParamProtocol the shell parameters protocol pointer
2352 @param[out] CommandStatus the status from the command line.
2354 @retval EFI_SUCCESS The command was completed.
2355 @retval EFI_ABORTED The command's operation was aborted.
2360 IN SHELL_OPERATION_TYPES Type
,
2361 IN CONST CHAR16
*CmdLine
,
2362 IN CHAR16
*FirstParameter
,
2363 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2364 OUT EFI_STATUS
*CommandStatus
2368 EFI_STATUS StartStatus
;
2369 CHAR16
*CommandWithPath
;
2370 EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
2371 SHELL_STATUS CalleeExitStatus
;
2373 Status
= EFI_SUCCESS
;
2374 CommandWithPath
= NULL
;
2376 CalleeExitStatus
= SHELL_INVALID_PARAMETER
;
2379 case Internal_Command
:
2380 Status
= RunInternalCommand(CmdLine
, FirstParameter
, ParamProtocol
, CommandStatus
);
2382 case Script_File_Name
:
2383 case Efi_Application
:
2385 // Process a fully qualified path
2387 if (StrStr(FirstParameter
, L
":") != NULL
) {
2388 ASSERT (CommandWithPath
== NULL
);
2389 if (ShellIsFile(FirstParameter
) == EFI_SUCCESS
) {
2390 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, FirstParameter
, 0);
2395 // Process a relative path and also check in the path environment variable
2397 if (CommandWithPath
== NULL
) {
2398 CommandWithPath
= ShellFindFilePathEx(FirstParameter
, mExecutableExtensions
);
2402 // This should be impossible now.
2404 ASSERT(CommandWithPath
!= NULL
);
2407 // Make sure that path is not just a directory (or not found)
2409 if (!EFI_ERROR(ShellIsDirectory(CommandWithPath
))) {
2410 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2411 SetLastError(SHELL_NOT_FOUND
);
2414 case Script_File_Name
:
2415 Status
= RunScriptFile (CommandWithPath
, NULL
, CmdLine
, ParamProtocol
);
2417 case Efi_Application
:
2419 // Get the device path of the application image
2421 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
2422 if (DevPath
== NULL
){
2423 Status
= EFI_OUT_OF_RESOURCES
;
2428 // Execute the device path
2430 Status
= InternalShellExecuteDevicePath(
2438 SHELL_FREE_NON_NULL(DevPath
);
2440 if(EFI_ERROR (Status
)) {
2441 CalleeExitStatus
= (SHELL_STATUS
) (Status
& (~MAX_BIT
));
2443 CalleeExitStatus
= (SHELL_STATUS
) StartStatus
;
2446 if (CommandStatus
!= NULL
) {
2447 *CommandStatus
= CalleeExitStatus
;
2451 // Update last error status.
2453 // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
2454 SetLastError(CalleeExitStatus
);
2470 SHELL_FREE_NON_NULL(CommandWithPath
);
2476 Function to setup StdIn, StdErr, StdOut, and then run the command or file.
2478 @param[in] Type the type of operation being run.
2479 @param[in] CmdLine the command line to run.
2480 @param[in] FirstParameter the first parameter on the command line.
2481 @param[in] ParamProtocol the shell parameters protocol pointer
2482 @param[out] CommandStatus the status from the command line.
2484 @retval EFI_SUCCESS The command was completed.
2485 @retval EFI_ABORTED The command's operation was aborted.
2489 SetupAndRunCommandOrFile(
2490 IN SHELL_OPERATION_TYPES Type
,
2492 IN CHAR16
*FirstParameter
,
2493 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2494 OUT EFI_STATUS
*CommandStatus
2498 SHELL_FILE_HANDLE OriginalStdIn
;
2499 SHELL_FILE_HANDLE OriginalStdOut
;
2500 SHELL_FILE_HANDLE OriginalStdErr
;
2501 SYSTEM_TABLE_INFO OriginalSystemTableInfo
;
2502 CONST SCRIPT_FILE
*ConstScriptFile
;
2505 // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
2507 Status
= UpdateStdInStdOutStdErr(ParamProtocol
, CmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2510 // The StdIn, StdOut, and StdErr are set up.
2511 // Now run the command, script, or application
2513 if (!EFI_ERROR(Status
)) {
2514 TrimSpaces(&CmdLine
);
2515 Status
= RunCommandOrFile(Type
, CmdLine
, FirstParameter
, ParamProtocol
, CommandStatus
);
2521 if (EFI_ERROR(Status
)) {
2522 ConstScriptFile
= ShellCommandGetCurrentScriptFile();
2523 if (ConstScriptFile
== NULL
|| ConstScriptFile
->CurrentCommand
== NULL
) {
2524 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
));
2526 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR_SCRIPT
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
), ConstScriptFile
->CurrentCommand
->Line
);
2531 // put back the original StdIn, StdOut, and StdErr
2533 RestoreStdInStdOutStdErr(ParamProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2539 Function will process and run a command line.
2541 This will determine if the command line represents an internal shell
2542 command or dispatch an external application.
2544 @param[in] CmdLine The command line to parse.
2545 @param[out] CommandStatus The status from the command line.
2547 @retval EFI_SUCCESS The command was completed.
2548 @retval EFI_ABORTED The command's operation was aborted.
2553 IN CONST CHAR16
*CmdLine
,
2554 OUT EFI_STATUS
*CommandStatus
2558 CHAR16
*CleanOriginal
;
2559 CHAR16
*FirstParameter
;
2561 SHELL_OPERATION_TYPES Type
;
2563 ASSERT(CmdLine
!= NULL
);
2564 if (StrLen(CmdLine
) == 0) {
2565 return (EFI_SUCCESS
);
2568 Status
= EFI_SUCCESS
;
2569 CleanOriginal
= NULL
;
2571 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
2572 if (CleanOriginal
== NULL
) {
2573 return (EFI_OUT_OF_RESOURCES
);
2576 TrimSpaces(&CleanOriginal
);
2579 // NULL out comments (leveraged from RunScriptFileHandle() ).
2580 // The # character on a line is used to denote that all characters on the same line
2581 // and to the right of the # are to be ignored by the shell.
2582 // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.
2584 for (TempWalker
= CleanOriginal
; TempWalker
!= NULL
&& *TempWalker
!= CHAR_NULL
; TempWalker
++) {
2585 if (*TempWalker
== L
'^') {
2586 if (*(TempWalker
+ 1) == L
'#') {
2589 } else if (*TempWalker
== L
'#') {
2590 *TempWalker
= CHAR_NULL
;
2594 TrimSpaces(&CleanOriginal
);
2597 // Handle case that passed in command line is just 1 or more " " characters.
2599 if (StrLen (CleanOriginal
) == 0) {
2600 SHELL_FREE_NON_NULL(CleanOriginal
);
2601 return (EFI_SUCCESS
);
2604 Status
= ProcessCommandLineToFinal(&CleanOriginal
);
2605 if (EFI_ERROR(Status
)) {
2606 SHELL_FREE_NON_NULL(CleanOriginal
);
2611 // We don't do normal processing with a split command line (output from one command input to another)
2613 if (ContainsSplit(CleanOriginal
)) {
2614 Status
= ProcessNewSplitCommandLine(CleanOriginal
);
2615 SHELL_FREE_NON_NULL(CleanOriginal
);
2620 // We need the first parameter information so we can determine the operation type
2622 FirstParameter
= AllocateZeroPool(StrSize(CleanOriginal
));
2623 if (FirstParameter
== NULL
) {
2624 SHELL_FREE_NON_NULL(CleanOriginal
);
2625 return (EFI_OUT_OF_RESOURCES
);
2627 TempWalker
= CleanOriginal
;
2628 if (!EFI_ERROR(GetNextParameter(&TempWalker
, &FirstParameter
, StrSize(CleanOriginal
), TRUE
))) {
2630 // Depending on the first parameter we change the behavior
2632 switch (Type
= GetOperationType(FirstParameter
)) {
2633 case File_Sys_Change
:
2634 Status
= ChangeMappedDrive (FirstParameter
);
2636 case Internal_Command
:
2637 case Script_File_Name
:
2638 case Efi_Application
:
2639 Status
= SetupAndRunCommandOrFile(Type
, CleanOriginal
, FirstParameter
, ShellInfoObject
.NewShellParametersProtocol
, CommandStatus
);
2643 // Whatever was typed, it was invalid.
2645 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2646 SetLastError(SHELL_NOT_FOUND
);
2650 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2651 SetLastError(SHELL_NOT_FOUND
);
2654 SHELL_FREE_NON_NULL(CleanOriginal
);
2655 SHELL_FREE_NON_NULL(FirstParameter
);
2661 Function will process and run a command line.
2663 This will determine if the command line represents an internal shell
2664 command or dispatch an external application.
2666 @param[in] CmdLine The command line to parse.
2668 @retval EFI_SUCCESS The command was completed.
2669 @retval EFI_ABORTED The command's operation was aborted.
2674 IN CONST CHAR16
*CmdLine
2677 return (RunShellCommand(CmdLine
, NULL
));
2681 STATIC CONST UINT16 InvalidChars
[] = {L
'*', L
'?', L
'<', L
'>', L
'\\', L
'/', L
'\"', 0x0001, 0x0002};
2683 Function determines if the CommandName COULD be a valid command. It does not determine whether
2684 this is a valid command. It only checks for invalid characters.
2686 @param[in] CommandName The name to check
2688 @retval TRUE CommandName could be a command name
2689 @retval FALSE CommandName could not be a valid command name
2694 IN CONST CHAR16
*CommandName
2698 if (CommandName
== NULL
) {
2703 ; Count
< sizeof(InvalidChars
) / sizeof(InvalidChars
[0])
2706 if (ScanMem16(CommandName
, StrSize(CommandName
), InvalidChars
[Count
]) != NULL
) {
2714 Function to process a NSH script file via SHELL_FILE_HANDLE.
2716 @param[in] Handle The handle to the already opened file.
2717 @param[in] Name The name of the script file.
2719 @retval EFI_SUCCESS the script completed successfully
2723 RunScriptFileHandle (
2724 IN SHELL_FILE_HANDLE Handle
,
2725 IN CONST CHAR16
*Name
2729 SCRIPT_FILE
*NewScriptFile
;
2731 UINTN PrintBuffSize
;
2732 CHAR16
*CommandLine
;
2733 CHAR16
*CommandLine2
;
2734 CHAR16
*CommandLine3
;
2735 SCRIPT_COMMAND_LIST
*LastCommand
;
2737 BOOLEAN PreScriptEchoState
;
2738 BOOLEAN PreCommandEchoState
;
2739 CONST CHAR16
*CurDir
;
2741 CHAR16 LeString
[50];
2742 LIST_ENTRY OldBufferList
;
2744 ASSERT(!ShellCommandGetScriptExit());
2746 PreScriptEchoState
= ShellCommandGetEchoState();
2747 PrintBuffSize
= PcdGet16(PcdShellPrintBufferSize
);
2749 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
2750 if (NewScriptFile
== NULL
) {
2751 return (EFI_OUT_OF_RESOURCES
);
2757 ASSERT(NewScriptFile
->ScriptName
== NULL
);
2758 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
2759 if (NewScriptFile
->ScriptName
== NULL
) {
2760 DeleteScriptFileStruct(NewScriptFile
);
2761 return (EFI_OUT_OF_RESOURCES
);
2765 // Save the parameters (used to replace %0 to %9 later on)
2767 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
2768 if (NewScriptFile
->Argc
!= 0) {
2769 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
2770 if (NewScriptFile
->Argv
== NULL
) {
2771 DeleteScriptFileStruct(NewScriptFile
);
2772 return (EFI_OUT_OF_RESOURCES
);
2774 for (LoopVar
= 0 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
2775 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
2776 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
2777 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
2778 DeleteScriptFileStruct(NewScriptFile
);
2779 return (EFI_OUT_OF_RESOURCES
);
2783 NewScriptFile
->Argv
= NULL
;
2786 InitializeListHead(&NewScriptFile
->CommandList
);
2787 InitializeListHead(&NewScriptFile
->SubstList
);
2790 // Now build the list of all script commands.
2793 while(!ShellFileHandleEof(Handle
)) {
2794 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
2796 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0 || CommandLine
[0] == '#') {
2797 SHELL_FREE_NON_NULL(CommandLine
);
2800 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
2801 if (NewScriptFile
->CurrentCommand
== NULL
) {
2802 SHELL_FREE_NON_NULL(CommandLine
);
2803 DeleteScriptFileStruct(NewScriptFile
);
2804 return (EFI_OUT_OF_RESOURCES
);
2807 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
2808 NewScriptFile
->CurrentCommand
->Data
= NULL
;
2809 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
2811 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2815 // Add this as the topmost script file
2817 ShellCommandSetNewScript (NewScriptFile
);
2820 // Now enumerate through the commands and run each one.
2822 CommandLine
= AllocateZeroPool(PrintBuffSize
);
2823 if (CommandLine
== NULL
) {
2824 DeleteScriptFileStruct(NewScriptFile
);
2825 return (EFI_OUT_OF_RESOURCES
);
2827 CommandLine2
= AllocateZeroPool(PrintBuffSize
);
2828 if (CommandLine2
== NULL
) {
2829 FreePool(CommandLine
);
2830 DeleteScriptFileStruct(NewScriptFile
);
2831 return (EFI_OUT_OF_RESOURCES
);
2834 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
2835 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
2836 ; // conditional increment in the body of the loop
2838 ASSERT(CommandLine2
!= NULL
);
2839 StrnCpyS( CommandLine2
,
2840 PrintBuffSize
/sizeof(CHAR16
),
2841 NewScriptFile
->CurrentCommand
->Cl
,
2842 PrintBuffSize
/sizeof(CHAR16
) - 1
2845 SaveBufferList(&OldBufferList
);
2848 // NULL out comments
2850 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
2851 if (*CommandLine3
== L
'^') {
2852 if ( *(CommandLine3
+1) == L
':') {
2853 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
2854 } else if (*(CommandLine3
+1) == L
'#') {
2857 } else if (*CommandLine3
== L
'#') {
2858 *CommandLine3
= CHAR_NULL
;
2862 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
2864 // Due to variability in starting the find and replace action we need to have both buffers the same.
2866 StrnCpyS( CommandLine
,
2867 PrintBuffSize
/sizeof(CHAR16
),
2869 PrintBuffSize
/sizeof(CHAR16
) - 1
2873 // Remove the %0 to %9 from the command line (if we have some arguments)
2875 if (NewScriptFile
->Argv
!= NULL
) {
2876 switch (NewScriptFile
->Argc
) {
2878 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%9", NewScriptFile
->Argv
[9], FALSE
, FALSE
);
2879 ASSERT_EFI_ERROR(Status
);
2881 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%8", NewScriptFile
->Argv
[8], FALSE
, FALSE
);
2882 ASSERT_EFI_ERROR(Status
);
2884 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%7", NewScriptFile
->Argv
[7], FALSE
, FALSE
);
2885 ASSERT_EFI_ERROR(Status
);
2887 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%6", NewScriptFile
->Argv
[6], FALSE
, FALSE
);
2888 ASSERT_EFI_ERROR(Status
);
2890 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%5", NewScriptFile
->Argv
[5], FALSE
, FALSE
);
2891 ASSERT_EFI_ERROR(Status
);
2893 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%4", NewScriptFile
->Argv
[4], FALSE
, FALSE
);
2894 ASSERT_EFI_ERROR(Status
);
2896 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%3", NewScriptFile
->Argv
[3], FALSE
, FALSE
);
2897 ASSERT_EFI_ERROR(Status
);
2899 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%2", NewScriptFile
->Argv
[2], FALSE
, FALSE
);
2900 ASSERT_EFI_ERROR(Status
);
2902 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%1", NewScriptFile
->Argv
[1], FALSE
, FALSE
);
2903 ASSERT_EFI_ERROR(Status
);
2905 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%0", NewScriptFile
->Argv
[0], FALSE
, FALSE
);
2906 ASSERT_EFI_ERROR(Status
);
2912 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%1", L
"\"\"", FALSE
, FALSE
);
2913 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%2", L
"\"\"", FALSE
, FALSE
);
2914 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%3", L
"\"\"", FALSE
, FALSE
);
2915 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%4", L
"\"\"", FALSE
, FALSE
);
2916 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%5", L
"\"\"", FALSE
, FALSE
);
2917 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%6", L
"\"\"", FALSE
, FALSE
);
2918 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%7", L
"\"\"", FALSE
, FALSE
);
2919 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%8", L
"\"\"", FALSE
, FALSE
);
2920 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%9", L
"\"\"", FALSE
, FALSE
);
2922 StrnCpyS( CommandLine2
,
2923 PrintBuffSize
/sizeof(CHAR16
),
2925 PrintBuffSize
/sizeof(CHAR16
) - 1
2928 LastCommand
= NewScriptFile
->CurrentCommand
;
2930 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
2932 if (CommandLine3
!= NULL
&& CommandLine3
[0] == L
':' ) {
2934 // This line is a goto target / label
2937 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
2938 if (CommandLine3
[0] == L
'@') {
2940 // We need to save the current echo state
2941 // and disable echo for just this command.
2943 PreCommandEchoState
= ShellCommandGetEchoState();
2944 ShellCommandSetEchoState(FALSE
);
2945 Status
= RunCommand(CommandLine3
+1);
2948 // If command was "@echo -off" or "@echo -on" then don't restore echo state
2950 if (StrCmp (L
"@echo -off", CommandLine3
) != 0 &&
2951 StrCmp (L
"@echo -on", CommandLine3
) != 0) {
2953 // Now restore the pre-'@' echo state.
2955 ShellCommandSetEchoState(PreCommandEchoState
);
2958 if (ShellCommandGetEchoState()) {
2959 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
2960 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
2961 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
2963 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
2965 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
2967 Status
= RunCommand(CommandLine3
);
2971 if (ShellCommandGetScriptExit()) {
2973 // ShellCommandGetExitCode() always returns a UINT64
2975 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ShellCommandGetExitCode());
2976 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
2977 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
2979 ShellCommandRegisterExit(FALSE
, 0);
2980 Status
= EFI_SUCCESS
;
2981 RestoreBufferList(&OldBufferList
);
2984 if (ShellGetExecutionBreakFlag()) {
2985 RestoreBufferList(&OldBufferList
);
2988 if (EFI_ERROR(Status
)) {
2989 RestoreBufferList(&OldBufferList
);
2992 if (ShellCommandGetExit()) {
2993 RestoreBufferList(&OldBufferList
);
2998 // If that commend did not update the CurrentCommand then we need to advance it...
3000 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
3001 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
3002 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
3003 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
3007 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
3008 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
3009 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
3012 RestoreBufferList(&OldBufferList
);
3016 FreePool(CommandLine
);
3017 FreePool(CommandLine2
);
3018 ShellCommandSetNewScript (NULL
);
3021 // Only if this was the last script reset the state.
3023 if (ShellCommandGetCurrentScriptFile()==NULL
) {
3024 ShellCommandSetEchoState(PreScriptEchoState
);
3026 return (EFI_SUCCESS
);
3030 Function to process a NSH script file.
3032 @param[in] ScriptPath Pointer to the script file name (including file system path).
3033 @param[in] Handle the handle of the script file already opened.
3034 @param[in] CmdLine the command line to run.
3035 @param[in] ParamProtocol the shell parameters protocol pointer
3037 @retval EFI_SUCCESS the script completed successfully
3042 IN CONST CHAR16
*ScriptPath
,
3043 IN SHELL_FILE_HANDLE Handle OPTIONAL
,
3044 IN CONST CHAR16
*CmdLine
,
3045 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
3049 SHELL_FILE_HANDLE FileHandle
;
3053 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
3054 return (EFI_INVALID_PARAMETER
);
3058 // get the argc and argv updated for scripts
3060 Status
= UpdateArgcArgv(ParamProtocol
, CmdLine
, Script_File_Name
, &Argv
, &Argc
);
3061 if (!EFI_ERROR(Status
)) {
3063 if (Handle
== NULL
) {
3067 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
3068 if (!EFI_ERROR(Status
)) {
3072 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
);
3075 // now close the file
3077 ShellCloseFile(&FileHandle
);
3080 Status
= RunScriptFileHandle(Handle
, ScriptPath
);
3085 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
3086 // This is safe even if the update API failed. In this case, it may be a no-op.
3088 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);
3094 Return the pointer to the first occurrence of any character from a list of characters.
3096 @param[in] String the string to parse
3097 @param[in] CharacterList the list of character to look for
3098 @param[in] EscapeCharacter An escape character to skip
3100 @return the location of the first character in the string
3101 @retval CHAR_NULL no instance of any character in CharacterList was found in String
3106 IN CONST CHAR16
*String
,
3107 IN CONST CHAR16
*CharacterList
,
3108 IN CONST CHAR16 EscapeCharacter
3114 for (WalkStr
= 0; WalkStr
< StrLen(String
); WalkStr
++) {
3115 if (String
[WalkStr
] == EscapeCharacter
) {
3119 for (WalkChar
= 0; WalkChar
< StrLen(CharacterList
); WalkChar
++) {
3120 if (String
[WalkStr
] == CharacterList
[WalkChar
]) {
3121 return (&String
[WalkStr
]);
3125 return (String
+ StrLen(String
));