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 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20 // Initialize the global structure
22 SHELL_INFO ShellInfoObject
= {
58 {{NULL
, NULL
}, NULL
, NULL
},
59 {{NULL
, NULL
}, NULL
, NULL
},
71 STATIC CONST CHAR16 mScriptExtension
[] = L
".NSH";
72 STATIC CONST CHAR16 mExecutableExtensions
[] = L
".NSH;.EFI";
73 STATIC CONST CHAR16 mStartupScript
[] = L
"startup.nsh";
74 CONST CHAR16 mNoNestingEnvVarName
[] = L
"nonesting";
75 CONST CHAR16 mNoNestingTrue
[] = L
"True";
76 CONST CHAR16 mNoNestingFalse
[] = L
"False";
79 Cleans off leading and trailing spaces and tabs.
81 @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
117 IN CONST CHAR16
*SourceString
,
118 IN CONST CHAR16
*FindString
,
119 IN CONST BOOLEAN CheckForEscapeCharacter
123 if (SourceString
== NULL
) {
126 Temp
= StrStr(SourceString
, FindString
);
129 // If nothing found, or we don't care about escape characters
131 if (Temp
== NULL
|| !CheckForEscapeCharacter
) {
136 // If we found an escaped character, try again on the remainder of the string
138 if ((Temp
> (SourceString
)) && *(Temp
-1) == L
'^') {
139 return FindNextInstance(Temp
+1, FindString
, CheckForEscapeCharacter
);
143 // we found the right character
149 Check whether the string between a pair of % is a valid environment variable name.
151 @param[in] BeginPercent pointer to the first percent.
152 @param[in] EndPercent pointer to the last percent.
154 @retval TRUE is a valid environment variable name.
155 @retval FALSE is NOT a valid environment variable name.
158 IsValidEnvironmentVariableName(
159 IN CONST CHAR16
*BeginPercent
,
160 IN CONST CHAR16
*EndPercent
163 CONST CHAR16
*Walker
;
167 ASSERT (BeginPercent
!= NULL
);
168 ASSERT (EndPercent
!= NULL
);
169 ASSERT (BeginPercent
< EndPercent
);
171 if ((BeginPercent
+ 1) == EndPercent
) {
175 for (Walker
= BeginPercent
+ 1; Walker
< EndPercent
; Walker
++) {
177 (*Walker
>= L
'0' && *Walker
<= L
'9') ||
178 (*Walker
>= L
'A' && *Walker
<= L
'Z') ||
179 (*Walker
>= L
'a' && *Walker
<= L
'z') ||
182 if (Walker
== BeginPercent
+ 1 && (*Walker
>= L
'0' && *Walker
<= L
'9')) {
196 Determine if a command line contains a split operation
198 @param[in] CmdLine The command line to parse.
200 @retval TRUE CmdLine has a valid split.
201 @retval FALSE CmdLine does not have a valid split.
205 IN CONST CHAR16
*CmdLine
208 CONST CHAR16
*TempSpot
;
209 CONST CHAR16
*FirstQuote
;
210 CONST CHAR16
*SecondQuote
;
212 FirstQuote
= FindNextInstance (CmdLine
, L
"\"", TRUE
);
214 TempSpot
= FindFirstCharacter(CmdLine
, L
"|", L
'^');
216 if (FirstQuote
== NULL
||
218 TempSpot
== CHAR_NULL
||
219 FirstQuote
> TempSpot
221 return (BOOLEAN
) ((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
));
224 while ((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
)) {
225 if (FirstQuote
== NULL
|| FirstQuote
> TempSpot
) {
228 SecondQuote
= FindNextInstance (FirstQuote
+ 1, L
"\"", TRUE
);
229 if (SecondQuote
== NULL
) {
232 if (SecondQuote
< TempSpot
) {
233 FirstQuote
= FindNextInstance (SecondQuote
+ 1, L
"\"", TRUE
);
236 FirstQuote
= FindNextInstance (SecondQuote
+ 1, L
"\"", TRUE
);
237 TempSpot
= FindFirstCharacter(TempSpot
+ 1, L
"|", L
'^');
242 return (BOOLEAN
) ((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
));
246 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
247 feature's enabled state was not known when the shell initially launched.
249 @retval EFI_SUCCESS The feature is enabled.
250 @retval EFI_OUT_OF_RESOURCES There is not enough memory available.
253 InternalEfiShellStartCtrlSMonitor(
257 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
*SimpleEx
;
258 EFI_KEY_DATA KeyData
;
261 Status
= gBS
->OpenProtocol(
262 gST
->ConsoleInHandle
,
263 &gEfiSimpleTextInputExProtocolGuid
,
267 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
268 if (EFI_ERROR(Status
)) {
273 STRING_TOKEN (STR_SHELL_NO_IN_EX
),
274 ShellInfoObject
.HiiHandle
);
275 return (EFI_SUCCESS
);
278 KeyData
.KeyState
.KeyToggleState
= 0;
279 KeyData
.Key
.ScanCode
= 0;
280 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
281 KeyData
.Key
.UnicodeChar
= L
's';
283 Status
= SimpleEx
->RegisterKeyNotify(
286 NotificationFunction
,
287 &ShellInfoObject
.CtrlSNotifyHandle1
);
289 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
290 if (!EFI_ERROR(Status
)) {
291 Status
= SimpleEx
->RegisterKeyNotify(
294 NotificationFunction
,
295 &ShellInfoObject
.CtrlSNotifyHandle2
);
297 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
298 KeyData
.Key
.UnicodeChar
= 19;
300 if (!EFI_ERROR(Status
)) {
301 Status
= SimpleEx
->RegisterKeyNotify(
304 NotificationFunction
,
305 &ShellInfoObject
.CtrlSNotifyHandle3
);
307 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
308 if (!EFI_ERROR(Status
)) {
309 Status
= SimpleEx
->RegisterKeyNotify(
312 NotificationFunction
,
313 &ShellInfoObject
.CtrlSNotifyHandle4
);
321 The entry point for the application.
323 @param[in] ImageHandle The firmware allocated handle for the EFI image.
324 @param[in] SystemTable A pointer to the EFI System Table.
326 @retval EFI_SUCCESS The entry point is executed successfully.
327 @retval other Some error occurs when executing this entry point.
333 IN EFI_HANDLE ImageHandle
,
334 IN EFI_SYSTEM_TABLE
*SystemTable
340 EFI_HANDLE ConInHandle
;
341 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*OldConIn
;
344 if (PcdGet8(PcdShellSupportLevel
) > 3) {
345 return (EFI_UNSUPPORTED
);
351 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
352 if (EFI_ERROR(Status
)) {
357 // Populate the global structure from PCDs
359 ShellInfoObject
.ImageDevPath
= NULL
;
360 ShellInfoObject
.FileDevPath
= NULL
;
361 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
362 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
363 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
366 // verify we dont allow for spec violation
368 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
371 // Initialize the LIST ENTRY objects...
373 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
374 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
375 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
378 // Check PCDs for optional features that are not implemented yet.
380 if ( PcdGetBool(PcdShellSupportOldProtocols
)
381 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
382 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
384 return (EFI_UNSUPPORTED
);
388 // turn off the watchdog timer
390 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
393 // install our console logger. This will keep a log of the output for back-browsing
395 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
396 if (!EFI_ERROR(Status
)) {
398 // Enable the cursor to be visible
400 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
403 // If supporting EFI 1.1 we need to install HII protocol
404 // only do this if PcdShellRequireHiiPlatform == FALSE
406 // remove EFI_UNSUPPORTED check above when complete.
407 ///@todo add support for Framework HII
410 // install our (solitary) HII package
412 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
413 if (ShellInfoObject
.HiiHandle
== NULL
) {
414 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
415 ///@todo Add our package into Framework HII
417 if (ShellInfoObject
.HiiHandle
== NULL
) {
418 Status
= EFI_NOT_STARTED
;
424 // create and install the EfiShellParametersProtocol
426 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
427 ASSERT_EFI_ERROR(Status
);
428 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
431 // create and install the EfiShellProtocol
433 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
434 ASSERT_EFI_ERROR(Status
);
435 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
438 // Now initialize the shell library (it requires Shell Parameters protocol)
440 Status
= ShellInitialize();
441 ASSERT_EFI_ERROR(Status
);
443 Status
= CommandInit();
444 ASSERT_EFI_ERROR(Status
);
446 Status
= ShellInitEnvVarList ();
449 // Check the command line
451 Status
= ProcessCommandLine ();
452 if (EFI_ERROR (Status
)) {
457 // If shell support level is >= 1 create the mappings and paths
459 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
460 Status
= ShellCommandCreateInitialMappingsAndPaths();
464 // Set the environment variable for nesting support
468 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoNest
) {
470 // No change. require nesting in Shell Protocol Execute()
472 StrnCatGrow(&TempString
,
477 StrnCatGrow(&TempString
,
482 Status
= InternalEfiShellSetEnv(mNoNestingEnvVarName
, TempString
, TRUE
);
483 SHELL_FREE_NON_NULL(TempString
);
487 // save the device path for the loaded image and the device path for the filepath (under loaded image)
488 // These are where to look for the startup.nsh file
490 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
491 ASSERT_EFI_ERROR(Status
);
494 // Display the version
496 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
499 gST
->ConOut
->Mode
->CursorRow
,
501 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL
),
502 ShellInfoObject
.HiiHandle
,
503 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
504 gEfiShellProtocol
->MajorVersion
,
505 gEfiShellProtocol
->MinorVersion
512 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER
),
513 ShellInfoObject
.HiiHandle
,
514 (CHAR16
*) PcdGetPtr (PcdShellSupplier
)
521 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI
),
522 ShellInfoObject
.HiiHandle
,
523 (gST
->Hdr
.Revision
&0xffff0000)>>16,
524 (gST
->Hdr
.Revision
&0x0000ffff),
526 gST
->FirmwareRevision
531 // Display the mapping
533 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
534 Status
= RunCommand(L
"map");
535 ASSERT_EFI_ERROR(Status
);
539 // init all the built in alias'
541 Status
= SetBuiltInAlias();
542 ASSERT_EFI_ERROR(Status
);
545 // Initialize environment variables
547 if (ShellCommandGetProfileList() != NULL
) {
548 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
549 ASSERT_EFI_ERROR(Status
);
553 TempString
= AllocateZeroPool(Size
);
555 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
556 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
557 ASSERT_EFI_ERROR(Status
);
559 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
560 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
561 ASSERT_EFI_ERROR(Status
);
563 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
564 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
565 ASSERT_EFI_ERROR(Status
);
567 FreePool(TempString
);
569 if (!EFI_ERROR(Status
)) {
570 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
572 // Set up the event for CTRL-C monitoring...
574 Status
= InernalEfiShellStartMonitor();
577 if (!EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
579 // Set up the event for CTRL-S monitoring...
581 Status
= InternalEfiShellStartCtrlSMonitor();
584 if (!EFI_ERROR(Status
) && ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
586 // close off the gST->ConIn
588 OldConIn
= gST
->ConIn
;
589 ConInHandle
= gST
->ConsoleInHandle
;
590 gST
->ConIn
= CreateSimpleTextInOnFile((SHELL_FILE_HANDLE
)&FileInterfaceNulFile
, &gST
->ConsoleInHandle
);
596 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
598 // process the startup script or launch the called app.
600 Status
= DoStartupScript(ShellInfoObject
.ImageDevPath
, ShellInfoObject
.FileDevPath
);
603 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
&& !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
605 // begin the UI waiting loop
609 // clean out all the memory allocated for CONST <something> * return values
610 // between each shell prompt presentation
612 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
613 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
617 // Reset page break back to default.
619 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
620 ASSERT (ShellInfoObject
.ConsoleInfo
!= NULL
);
621 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
622 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
627 Status
= DoShellPrompt();
628 } while (!ShellCommandGetExit());
630 if (OldConIn
!= NULL
&& ConInHandle
!= NULL
) {
631 CloseSimpleTextInOnFile (gST
->ConIn
);
632 gST
->ConIn
= OldConIn
;
633 gST
->ConsoleInHandle
= ConInHandle
;
640 // uninstall protocols / free memory / etc...
642 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
643 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
644 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
646 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
647 FreePool(ShellInfoObject
.ImageDevPath
);
648 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
650 if (ShellInfoObject
.FileDevPath
!= NULL
) {
651 FreePool(ShellInfoObject
.FileDevPath
);
652 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
654 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
655 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
656 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
658 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
659 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
660 InternalEfiShellSetEnv(L
"cwd", NULL
, TRUE
);
662 CleanUpShellEnvironment (ShellInfoObject
.NewEfiShellProtocol
);
663 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
666 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
667 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
670 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
671 ASSERT(FALSE
); ///@todo finish this de-allocation (free SplitStdIn/Out when needed).
673 for ( Split
= (SPLIT_LIST
*)GetFirstNode (&ShellInfoObject
.SplitList
.Link
)
674 ; !IsNull (&ShellInfoObject
.SplitList
.Link
, &Split
->Link
)
675 ; Split
= (SPLIT_LIST
*)GetNextNode (&ShellInfoObject
.SplitList
.Link
, &Split
->Link
)
677 RemoveEntryList (&Split
->Link
);
681 DEBUG_CODE (InitializeListHead (&ShellInfoObject
.SplitList
.Link
););
684 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
685 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
686 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
689 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
690 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
691 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
694 if (ShellInfoObject
.HiiHandle
!= NULL
) {
695 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
696 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
699 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
700 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
703 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
704 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
705 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
706 FreePool(ShellInfoObject
.ConsoleInfo
);
707 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
710 ShellFreeEnvVarList ();
712 if (ShellCommandGetExit()) {
713 return ((EFI_STATUS
)ShellCommandGetExitCode());
719 Sets all the alias' that were registered with the ShellCommandLib library.
721 @retval EFI_SUCCESS all init commands were run successfully.
729 CONST ALIAS_LIST
*List
;
733 // Get all the commands we want to run
735 List
= ShellCommandGetInitAliasList();
738 // for each command in the List
740 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
741 ; !IsNull (&List
->Link
, &Node
->Link
)
742 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
745 // install the alias'
747 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
748 ASSERT_EFI_ERROR(Status
);
750 return (EFI_SUCCESS
);
754 Internal function to determine if 2 command names are really the same.
756 @param[in] Command1 The pointer to the first command name.
757 @param[in] Command2 The pointer to the second command name.
759 @retval TRUE The 2 command names are the same.
760 @retval FALSE The 2 command names are not the same.
764 IN CONST CHAR16
*Command1
,
765 IN CONST CHAR16
*Command2
768 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
775 Internal function to determine if a command is a script only command.
777 @param[in] CommandName The pointer to the command name.
779 @retval TRUE The command is a script only command.
780 @retval FALSE The command is not a script only command.
784 IN CONST CHAR16
*CommandName
787 if (IsCommand(CommandName
, L
"for")
788 ||IsCommand(CommandName
, L
"endfor")
789 ||IsCommand(CommandName
, L
"if")
790 ||IsCommand(CommandName
, L
"else")
791 ||IsCommand(CommandName
, L
"endif")
792 ||IsCommand(CommandName
, L
"goto")) {
799 This function will populate the 2 device path protocol parameters based on the
800 global gImageHandle. The DevPath will point to the device path for the handle that has
801 loaded image protocol installed on it. The FilePath will point to the device path
802 for the file that was loaded.
804 @param[in, out] DevPath On a successful return the device path to the loaded image.
805 @param[in, out] FilePath On a successful return the device path to the file.
807 @retval EFI_SUCCESS The 2 device paths were successfully returned.
808 @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.
895 CHAR16
*DelayValueStr
;
898 EFI_UNICODE_COLLATION_PROTOCOL
*UnicodeCollation
;
900 // `file-name-options` will contain arguments to `file-name` that we don't
901 // know about. This would cause ShellCommandLineParse to error, so we parse
902 // arguments manually, ignoring those after the first thing that doesn't look
903 // like a shell option (which is assumed to be `file-name`).
905 Status
= gBS
->LocateProtocol (
906 &gEfiUnicodeCollation2ProtocolGuid
,
908 (VOID
**) &UnicodeCollation
910 if (EFI_ERROR (Status
)) {
911 Status
= gBS
->LocateProtocol (
912 &gEfiUnicodeCollationProtocolGuid
,
914 (VOID
**) &UnicodeCollation
916 if (EFI_ERROR (Status
)) {
921 // Set default options
922 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= FALSE
;
923 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= FALSE
;
924 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= FALSE
;
925 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= FALSE
;
926 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= FALSE
;
927 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= FALSE
;
928 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= FALSE
;
929 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= FALSE
;
930 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= FALSE
;
931 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoNest
= FALSE
;
932 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
935 // Start LoopVar at 0 to parse only optional arguments at Argv[0]
936 // and parse other parameters from Argv[1]. This is for use case that
937 // UEFI Shell boot option is created, and OptionalData is provided
938 // that starts with shell command-line options.
940 for (LoopVar
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
941 CurrentArg
= gEfiShellParametersProtocol
->Argv
[LoopVar
];
942 if (UnicodeCollation
->StriColl (
947 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= TRUE
;
949 else if (UnicodeCollation
->StriColl (
954 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= TRUE
;
956 else if (UnicodeCollation
->StriColl (
961 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= TRUE
;
963 else if (UnicodeCollation
->StriColl (
968 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= TRUE
;
970 else if (UnicodeCollation
->StriColl (
975 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= TRUE
;
977 else if (UnicodeCollation
->StriColl (
982 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= TRUE
;
984 else if (UnicodeCollation
->StriColl (
989 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= TRUE
;
991 else if (UnicodeCollation
->StriColl (
996 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoNest
= TRUE
;
998 else if (UnicodeCollation
->StriColl (
1003 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= TRUE
;
1004 // Check for optional delay value following "-delay"
1005 if ((LoopVar
+ 1) >= gEfiShellParametersProtocol
->Argc
) {
1006 DelayValueStr
= NULL
;
1008 DelayValueStr
= gEfiShellParametersProtocol
->Argv
[LoopVar
+ 1];
1010 if (DelayValueStr
!= NULL
){
1011 if (*DelayValueStr
== L
':') {
1014 if (!EFI_ERROR(ShellConvertStringToUint64 (
1020 ShellInfoObject
.ShellInitSettings
.Delay
= (UINTN
)DelayValue
;
1024 } else if (UnicodeCollation
->StriColl (
1029 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= TRUE
;
1030 } else if (StrnCmp (L
"-", CurrentArg
, 1) == 0) {
1031 // Unrecognized option
1032 ShellPrintHiiEx(-1, -1, NULL
,
1033 STRING_TOKEN (STR_GEN_PROBLEM
),
1034 ShellInfoObject
.HiiHandle
,
1037 return EFI_INVALID_PARAMETER
;
1040 // First argument should be Shell.efi image name
1046 ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;
1049 // If first argument contains a space, then add double quotes before the argument
1051 if (StrStr (CurrentArg
, L
" ") != NULL
) {
1052 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileName
, &Size
, L
"\"", 0);
1053 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
1054 return (EFI_OUT_OF_RESOURCES
);
1057 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileName
, &Size
, CurrentArg
, 0);
1058 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
1059 return (EFI_OUT_OF_RESOURCES
);
1062 // If first argument contains a space, then add double quotes after the argument
1064 if (StrStr (CurrentArg
, L
" ") != NULL
) {
1065 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileName
, &Size
, L
"\"", 0);
1066 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
1067 return (EFI_OUT_OF_RESOURCES
);
1071 // We found `file-name`.
1073 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
1076 // Add `file-name-options`
1077 for (Size
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
1078 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
1080 // Add a space between arguments
1082 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1083 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
, &Size
, L
" ", 0);
1084 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1085 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
1086 return (EFI_OUT_OF_RESOURCES
);
1090 // If an argumnent contains a space, then add double quotes before the argument
1092 if (StrStr (gEfiShellParametersProtocol
->Argv
[LoopVar
], L
" ") != NULL
) {
1093 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
1097 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1098 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
1099 return (EFI_OUT_OF_RESOURCES
);
1102 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
1104 gEfiShellParametersProtocol
->Argv
[LoopVar
],
1106 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1107 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
1108 return (EFI_OUT_OF_RESOURCES
);
1111 // If an argumnent contains a space, then add double quotes after the argument
1113 if (StrStr (gEfiShellParametersProtocol
->Argv
[LoopVar
], L
" ") != NULL
) {
1114 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
1118 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1119 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
1120 return (EFI_OUT_OF_RESOURCES
);
1127 // "-nointerrupt" overrides "-delay"
1128 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
1129 ShellInfoObject
.ShellInitSettings
.Delay
= 0;
1136 Function try to find location of the Startup.nsh file.
1138 The buffer is callee allocated and should be freed by the caller.
1140 @param ImageDevicePath The path to the image for shell. first place to look for the startup script
1141 @param FileDevicePath The path to the file for shell. second place to look for the startup script.
1143 @retval NULL No Startup.nsh file was found.
1144 @return !=NULL Pointer to NULL-terminated path.
1147 LocateStartupScript (
1148 IN EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
,
1149 IN EFI_DEVICE_PATH_PROTOCOL
*FileDevicePath
1152 CHAR16
*StartupScriptPath
;
1154 CONST CHAR16
*MapName
;
1157 StartupScriptPath
= NULL
;
1161 // Try to find 'Startup.nsh' in the directory where the shell itself was launched.
1163 MapName
= ShellInfoObject
.NewEfiShellProtocol
->GetMapFromDevicePath (&ImageDevicePath
);
1164 if (MapName
!= NULL
) {
1165 StartupScriptPath
= StrnCatGrow (&StartupScriptPath
, &Size
, MapName
, 0);
1166 if (StartupScriptPath
== NULL
) {
1168 // Do not locate the startup script in sys path when out of resource.
1172 TempSpot
= StrStr (StartupScriptPath
, L
";");
1173 if (TempSpot
!= NULL
) {
1174 *TempSpot
= CHAR_NULL
;
1177 InternalEfiShellSetEnv(L
"homefilesystem", StartupScriptPath
, TRUE
);
1179 StartupScriptPath
= StrnCatGrow (&StartupScriptPath
, &Size
, ((FILEPATH_DEVICE_PATH
*)FileDevicePath
)->PathName
, 0);
1180 PathRemoveLastItem (StartupScriptPath
);
1181 StartupScriptPath
= StrnCatGrow (&StartupScriptPath
, &Size
, mStartupScript
, 0);
1185 // Try to find 'Startup.nsh' in the execution path defined by the envrionment variable PATH.
1187 if ((StartupScriptPath
== NULL
) || EFI_ERROR (ShellIsFile (StartupScriptPath
))) {
1188 SHELL_FREE_NON_NULL (StartupScriptPath
);
1189 StartupScriptPath
= ShellFindFilePath (mStartupScript
);
1192 return StartupScriptPath
;
1196 Handles all interaction with the default startup script.
1198 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
1200 @param ImagePath the path to the image for shell. first place to look for the startup script
1201 @param FilePath the path to the file for shell. second place to look for the startup script.
1203 @retval EFI_SUCCESS the variable is initialized.
1207 IN EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
1208 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
1212 EFI_STATUS CalleeStatus
;
1215 CHAR16
*FileStringPath
;
1216 CHAR16
*FullFileStringPath
;
1219 Key
.UnicodeChar
= CHAR_NULL
;
1222 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
1224 // launch something else instead
1226 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
1227 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1228 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
1230 FileStringPath
= AllocateZeroPool(NewSize
);
1231 if (FileStringPath
== NULL
) {
1232 return (EFI_OUT_OF_RESOURCES
);
1234 StrCpyS(FileStringPath
, NewSize
/sizeof(CHAR16
), ShellInfoObject
.ShellInitSettings
.FileName
);
1235 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1236 StrnCatS(FileStringPath
, NewSize
/sizeof(CHAR16
), L
" ", NewSize
/sizeof(CHAR16
) - StrLen(FileStringPath
) -1);
1237 StrnCatS(FileStringPath
, NewSize
/sizeof(CHAR16
), ShellInfoObject
.ShellInitSettings
.FileOptions
, NewSize
/sizeof(CHAR16
) - StrLen(FileStringPath
) -1);
1239 Status
= RunShellCommand(FileStringPath
, &CalleeStatus
);
1240 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
== TRUE
) {
1241 ShellCommandRegisterExit(gEfiShellProtocol
->BatchIsActive(), (UINT64
)CalleeStatus
);
1243 FreePool(FileStringPath
);
1249 // for shell level 0 we do no scripts
1250 // Without the Startup bit overriding we allow for nostartup to prevent scripts
1252 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
1253 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
1255 return (EFI_SUCCESS
);
1258 gST
->ConOut
->EnableCursor(gST
->ConOut
, FALSE
);
1260 // print out our warning and see if they press a key
1262 for ( Status
= EFI_UNSUPPORTED
, Delay
= ShellInfoObject
.ShellInitSettings
.Delay
1263 ; Delay
!= 0 && EFI_ERROR(Status
)
1266 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
);
1267 gBS
->Stall (1000000);
1268 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
1269 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
1272 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
1273 gST
->ConOut
->EnableCursor(gST
->ConOut
, TRUE
);
1278 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
1279 return (EFI_SUCCESS
);
1282 FileStringPath
= LocateStartupScript (ImagePath
, FilePath
);
1283 if (FileStringPath
!= NULL
) {
1284 FullFileStringPath
= FullyQualifyPath(FileStringPath
);
1285 if (FullFileStringPath
== NULL
) {
1286 Status
= RunScriptFile (FileStringPath
, NULL
, FileStringPath
, ShellInfoObject
.NewShellParametersProtocol
);
1288 Status
= RunScriptFile (FullFileStringPath
, NULL
, FullFileStringPath
, ShellInfoObject
.NewShellParametersProtocol
);
1289 FreePool(FullFileStringPath
);
1291 FreePool (FileStringPath
);
1294 // we return success since startup script is not mandatory.
1296 Status
= EFI_SUCCESS
;
1303 Function to perform the shell prompt looping. It will do a single prompt,
1304 dispatch the result, and then return. It is expected that the caller will
1305 call this function in a loop many times.
1308 @retval RETURN_ABORTED
1318 CONST CHAR16
*CurDir
;
1321 LIST_ENTRY OldBufferList
;
1326 // Get screen setting to decide size of the command line buffer
1328 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
1329 BufferSize
= Column
* Row
* sizeof (CHAR16
);
1330 CmdLine
= AllocateZeroPool (BufferSize
);
1331 if (CmdLine
== NULL
) {
1332 return EFI_OUT_OF_RESOURCES
;
1335 SaveBufferList(&OldBufferList
);
1336 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1341 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
1343 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1344 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1346 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1350 // Read a line from the console
1352 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
1355 // Null terminate the string and parse it
1357 if (!EFI_ERROR (Status
)) {
1359 // Reset the CTRL-C event just before running the command (yes we ignore the return values)
1361 Status
= gBS
->CheckEvent (ShellInfoObject
.NewEfiShellProtocol
->ExecutionBreak
);
1363 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
1364 Status
= RunCommand(CmdLine
);
1368 // Done with this command
1370 RestoreBufferList(&OldBufferList
);
1376 Add a buffer to the Buffer To Free List for safely returning buffers to other
1377 places without risking letting them modify internal shell information.
1379 @param Buffer Something to pass to FreePool when the shell is exiting.
1382 AddBufferToFreeList (
1386 BUFFER_LIST
*BufferListEntry
;
1388 if (Buffer
== NULL
) {
1392 BufferListEntry
= AllocateZeroPool (sizeof (BUFFER_LIST
));
1393 if (BufferListEntry
== NULL
) {
1397 BufferListEntry
->Buffer
= Buffer
;
1398 InsertTailList (&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
1404 Create a new buffer list and stores the old one to OldBufferList
1406 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
1410 OUT LIST_ENTRY
*OldBufferList
1413 CopyMem (OldBufferList
, &ShellInfoObject
.BufferToFreeList
.Link
, sizeof (LIST_ENTRY
));
1414 InitializeListHead (&ShellInfoObject
.BufferToFreeList
.Link
);
1418 Restore previous nodes into BufferToFreeList .
1420 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
1424 IN OUT LIST_ENTRY
*OldBufferList
1427 FreeBufferList (&ShellInfoObject
.BufferToFreeList
);
1428 CopyMem (&ShellInfoObject
.BufferToFreeList
.Link
, OldBufferList
, sizeof (LIST_ENTRY
));
1433 Add a buffer to the Line History List
1435 @param Buffer The line buffer to add.
1438 AddLineToCommandHistory(
1439 IN CONST CHAR16
*Buffer
1443 BUFFER_LIST
*Walker
;
1444 UINT16 MaxHistoryCmdCount
;
1448 MaxHistoryCmdCount
= PcdGet16(PcdShellMaxHistoryCommandCount
);
1450 if (MaxHistoryCmdCount
== 0) {
1455 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1460 Node
->Buffer
= AllocateCopyPool (StrSize (Buffer
), Buffer
);
1461 if (Node
->Buffer
== NULL
) {
1466 for ( Walker
= (BUFFER_LIST
*)GetFirstNode(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)
1467 ; !IsNull(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Walker
->Link
)
1468 ; Walker
= (BUFFER_LIST
*)GetNextNode(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Walker
->Link
)
1472 if (Count
< MaxHistoryCmdCount
){
1473 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1475 Walker
= (BUFFER_LIST
*)GetFirstNode(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
1476 RemoveEntryList(&Walker
->Link
);
1477 if (Walker
->Buffer
!= NULL
) {
1478 FreePool(Walker
->Buffer
);
1481 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1486 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1487 with the correct command name.
1489 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1490 command name if it was an alias. If it was not
1491 an alias it will be unchanged. This function may
1492 change the buffer to fit the command name.
1494 @retval EFI_SUCCESS The name was changed.
1495 @retval EFI_SUCCESS The name was not an alias.
1496 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1500 IN OUT CHAR16
**CommandString
1503 CONST CHAR16
*NewString
;
1505 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
1506 if (NewString
== NULL
) {
1507 return (EFI_SUCCESS
);
1509 FreePool(*CommandString
);
1510 *CommandString
= AllocateCopyPool(StrSize(NewString
), NewString
);
1511 if (*CommandString
== NULL
) {
1512 return (EFI_OUT_OF_RESOURCES
);
1514 return (EFI_SUCCESS
);
1518 This function will eliminate unreplaced (and therefore non-found) environment variables.
1520 @param[in,out] CmdLine The command line to update.
1523 StripUnreplacedEnvironmentVariables(
1524 IN OUT CHAR16
*CmdLine
1527 CHAR16
*FirstPercent
;
1529 CHAR16
*SecondPercent
;
1530 CHAR16
*SecondQuote
;
1531 CHAR16
*CurrentLocator
;
1533 for (CurrentLocator
= CmdLine
; CurrentLocator
!= NULL
; ) {
1534 FirstQuote
= FindNextInstance(CurrentLocator
, L
"\"", TRUE
);
1535 FirstPercent
= FindNextInstance(CurrentLocator
, L
"%", TRUE
);
1536 SecondPercent
= FirstPercent
!=NULL
?FindNextInstance(FirstPercent
+1, L
"%", TRUE
):NULL
;
1537 if (FirstPercent
== NULL
|| SecondPercent
== NULL
) {
1539 // If we ever don't have 2 % we are done.
1544 if (FirstQuote
!= NULL
&& FirstQuote
< FirstPercent
) {
1545 SecondQuote
= FindNextInstance(FirstQuote
+1, L
"\"", TRUE
);
1547 // Quote is first found
1550 if (SecondQuote
< FirstPercent
) {
1552 // restart after the pair of "
1554 CurrentLocator
= SecondQuote
+ 1;
1555 } else /* FirstPercent < SecondQuote */{
1557 // Restart on the first percent
1559 CurrentLocator
= FirstPercent
;
1564 if (FirstQuote
== NULL
|| SecondPercent
< FirstQuote
) {
1565 if (IsValidEnvironmentVariableName(FirstPercent
, SecondPercent
)) {
1567 // We need to remove from FirstPercent to SecondPercent
1569 CopyMem(FirstPercent
, SecondPercent
+ 1, StrSize(SecondPercent
+ 1));
1571 // don't need to update the locator. both % characters are gone.
1574 CurrentLocator
= SecondPercent
+ 1;
1578 CurrentLocator
= FirstQuote
;
1580 return (EFI_SUCCESS
);
1584 Function allocates a new command line and replaces all instances of environment
1585 variable names that are correctly preset to their values.
1587 If the return value is not NULL the memory must be caller freed.
1589 @param[in] OriginalCommandLine The original command line
1591 @retval NULL An error occurred.
1592 @return The new command line with no environment variables present.
1595 ShellConvertVariables (
1596 IN CONST CHAR16
*OriginalCommandLine
1599 CONST CHAR16
*MasterEnvList
;
1601 CHAR16
*NewCommandLine1
;
1602 CHAR16
*NewCommandLine2
;
1606 SCRIPT_FILE
*CurrentScriptFile
;
1607 ALIAS_LIST
*AliasListNode
;
1609 ASSERT(OriginalCommandLine
!= NULL
);
1612 NewSize
= StrSize(OriginalCommandLine
);
1613 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
1616 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1619 // calculate the size required for the post-conversion string...
1621 if (CurrentScriptFile
!= NULL
) {
1622 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1623 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1624 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1626 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
1628 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
1631 // we need a preceding and if there is space no ^ preceding (if no space ignore)
1633 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
1634 NewSize
+= StrSize(AliasListNode
->CommandString
);
1640 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1641 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1642 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1644 if (StrSize(MasterEnvList
) > ItemSize
) {
1645 ItemSize
= StrSize(MasterEnvList
);
1647 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
1649 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
1652 // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)
1654 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
1655 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
1656 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
1662 // now do the replacements...
1664 NewCommandLine1
= AllocateZeroPool (NewSize
);
1665 NewCommandLine2
= AllocateZeroPool(NewSize
);
1666 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
1667 if (NewCommandLine1
== NULL
|| NewCommandLine2
== NULL
|| ItemTemp
== NULL
) {
1668 SHELL_FREE_NON_NULL(NewCommandLine1
);
1669 SHELL_FREE_NON_NULL(NewCommandLine2
);
1670 SHELL_FREE_NON_NULL(ItemTemp
);
1673 CopyMem (NewCommandLine1
, OriginalCommandLine
, StrSize (OriginalCommandLine
));
1675 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1676 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
1677 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1680 ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
)),
1684 ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
)),
1688 ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
)),
1691 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1692 StrCpyS(NewCommandLine1
, NewSize
/sizeof(CHAR16
), NewCommandLine2
);
1694 if (CurrentScriptFile
!= NULL
) {
1695 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1696 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1697 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1699 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1700 StrCpyS(NewCommandLine1
, NewSize
/sizeof(CHAR16
), NewCommandLine2
);
1705 // Remove non-existent environment variables
1707 StripUnreplacedEnvironmentVariables(NewCommandLine1
);
1710 // Now cleanup any straggler intentionally ignored "%" characters
1712 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, L
"^%", L
"%", TRUE
, FALSE
);
1713 StrCpyS(NewCommandLine1
, NewSize
/sizeof(CHAR16
), NewCommandLine2
);
1715 FreePool(NewCommandLine2
);
1718 return (NewCommandLine1
);
1722 Internal function to run a command line with pipe usage.
1724 @param[in] CmdLine The pointer to the command line.
1725 @param[in] StdIn The pointer to the Standard input.
1726 @param[in] StdOut The pointer to the Standard output.
1728 @retval EFI_SUCCESS The split command is executed successfully.
1729 @retval other Some error occurs when executing the split command.
1733 IN CONST CHAR16
*CmdLine
,
1734 IN SHELL_FILE_HANDLE StdIn
,
1735 IN SHELL_FILE_HANDLE StdOut
1739 CHAR16
*NextCommandLine
;
1740 CHAR16
*OurCommandLine
;
1744 SHELL_FILE_HANDLE TempFileHandle
;
1747 ASSERT(StdOut
== NULL
);
1749 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1751 Status
= EFI_SUCCESS
;
1752 NextCommandLine
= NULL
;
1753 OurCommandLine
= NULL
;
1757 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1758 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1760 if (NextCommandLine
== NULL
|| OurCommandLine
== NULL
) {
1761 SHELL_FREE_NON_NULL(OurCommandLine
);
1762 SHELL_FREE_NON_NULL(NextCommandLine
);
1763 return (EFI_OUT_OF_RESOURCES
);
1764 } else if (StrStr(OurCommandLine
, L
"|") != NULL
|| Size1
== 0 || Size2
== 0) {
1765 SHELL_FREE_NON_NULL(OurCommandLine
);
1766 SHELL_FREE_NON_NULL(NextCommandLine
);
1767 return (EFI_INVALID_PARAMETER
);
1768 } else if (NextCommandLine
[0] == L
'a' &&
1769 (NextCommandLine
[1] == L
' ' || NextCommandLine
[1] == CHAR_NULL
)
1771 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1772 while (NextCommandLine
[0] == L
' ') {
1773 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1775 if (NextCommandLine
[0] == CHAR_NULL
) {
1776 SHELL_FREE_NON_NULL(OurCommandLine
);
1777 SHELL_FREE_NON_NULL(NextCommandLine
);
1778 return (EFI_INVALID_PARAMETER
);
1787 // make a SPLIT_LIST item and add to list
1789 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1790 if (Split
== NULL
) {
1791 return EFI_OUT_OF_RESOURCES
;
1793 Split
->SplitStdIn
= StdIn
;
1794 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1795 ASSERT(Split
->SplitStdOut
!= NULL
);
1796 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1798 Status
= RunCommand(OurCommandLine
);
1801 // move the output from the first to the in to the second.
1803 TempFileHandle
= Split
->SplitStdOut
;
1804 if (Split
->SplitStdIn
== StdIn
) {
1805 Split
->SplitStdOut
= NULL
;
1807 Split
->SplitStdOut
= Split
->SplitStdIn
;
1809 Split
->SplitStdIn
= TempFileHandle
;
1810 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition (Split
->SplitStdIn
, 0);
1812 if (!EFI_ERROR(Status
)) {
1813 Status
= RunCommand(NextCommandLine
);
1817 // remove the top level from the ScriptList
1819 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1820 RemoveEntryList(&Split
->Link
);
1823 // Note that the original StdIn is now the StdOut...
1825 if (Split
->SplitStdOut
!= NULL
) {
1826 ShellInfoObject
.NewEfiShellProtocol
->CloseFile (Split
->SplitStdOut
);
1828 if (Split
->SplitStdIn
!= NULL
) {
1829 ShellInfoObject
.NewEfiShellProtocol
->CloseFile (Split
->SplitStdIn
);
1833 FreePool(NextCommandLine
);
1834 FreePool(OurCommandLine
);
1840 Take the original command line, substitute any variables, free
1841 the original string, return the modified copy.
1843 @param[in] CmdLine pointer to the command line to update.
1845 @retval EFI_SUCCESS the function was successful.
1846 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1849 ShellSubstituteVariables(
1854 NewCmdLine
= ShellConvertVariables(*CmdLine
);
1855 SHELL_FREE_NON_NULL(*CmdLine
);
1856 if (NewCmdLine
== NULL
) {
1857 return (EFI_OUT_OF_RESOURCES
);
1859 *CmdLine
= NewCmdLine
;
1860 return (EFI_SUCCESS
);
1864 Take the original command line, substitute any alias in the first group of space delimited characters, free
1865 the original string, return the modified copy.
1867 @param[in] CmdLine pointer to the command line to update.
1869 @retval EFI_SUCCESS the function was successful.
1870 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1873 ShellSubstituteAliases(
1878 CHAR16
*CommandName
;
1880 UINTN PostAliasSize
;
1881 ASSERT(CmdLine
!= NULL
);
1882 ASSERT(*CmdLine
!= NULL
);
1886 if (StrStr((*CmdLine
), L
" ") == NULL
){
1887 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), 0);
1889 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), StrStr((*CmdLine
), L
" ") - (*CmdLine
));
1893 // This cannot happen 'inline' since the CmdLine can need extra space.
1896 if (!ShellCommandIsCommandOnList(CommandName
)) {
1898 // Convert via alias
1900 Status
= ShellConvertAlias(&CommandName
);
1901 if (EFI_ERROR(Status
)){
1905 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, CommandName
, 0);
1906 if (NewCmdLine
== NULL
) {
1907 SHELL_FREE_NON_NULL(CommandName
);
1908 SHELL_FREE_NON_NULL(*CmdLine
);
1909 return (EFI_OUT_OF_RESOURCES
);
1911 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, StrStr((*CmdLine
), L
" "), 0);
1912 if (NewCmdLine
== NULL
) {
1913 SHELL_FREE_NON_NULL(CommandName
);
1914 SHELL_FREE_NON_NULL(*CmdLine
);
1915 return (EFI_OUT_OF_RESOURCES
);
1918 NewCmdLine
= StrnCatGrow(&NewCmdLine
, NULL
, (*CmdLine
), 0);
1921 SHELL_FREE_NON_NULL(*CmdLine
);
1922 SHELL_FREE_NON_NULL(CommandName
);
1925 // re-assign the passed in double pointer to point to our newly allocated buffer
1927 *CmdLine
= NewCmdLine
;
1929 return (EFI_SUCCESS
);
1933 Takes the Argv[0] part of the command line and determine the meaning of it.
1935 @param[in] CmdName pointer to the command line to update.
1937 @retval Internal_Command The name is an internal command.
1938 @retval File_Sys_Change the name is a file system change.
1939 @retval Script_File_Name the name is a NSH script file.
1940 @retval Unknown_Invalid the name is unknown.
1941 @retval Efi_Application the name is an application (.EFI).
1943 SHELL_OPERATION_TYPES
1945 IN CONST CHAR16
*CmdName
1948 CHAR16
* FileWithPath
;
1949 CONST CHAR16
* TempLocation
;
1950 CONST CHAR16
* TempLocation2
;
1952 FileWithPath
= NULL
;
1954 // test for an internal command.
1956 if (ShellCommandIsCommandOnList(CmdName
)) {
1957 return (Internal_Command
);
1961 // Test for file system change request. anything ending with first : and cant have spaces.
1963 if (CmdName
[(StrLen(CmdName
)-1)] == L
':') {
1964 if ( StrStr(CmdName
, L
" ") != NULL
1965 || StrLen(StrStr(CmdName
, L
":")) > 1
1967 return (Unknown_Invalid
);
1969 return (File_Sys_Change
);
1975 if ((FileWithPath
= ShellFindFilePathEx(CmdName
, mExecutableExtensions
)) != NULL
) {
1977 // See if that file has a script file extension
1979 if (StrLen(FileWithPath
) > 4) {
1980 TempLocation
= FileWithPath
+StrLen(FileWithPath
)-4;
1981 TempLocation2
= mScriptExtension
;
1982 if (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0) {
1983 SHELL_FREE_NON_NULL(FileWithPath
);
1984 return (Script_File_Name
);
1989 // Was a file, but not a script. we treat this as an application.
1991 SHELL_FREE_NON_NULL(FileWithPath
);
1992 return (Efi_Application
);
1995 SHELL_FREE_NON_NULL(FileWithPath
);
1997 // No clue what this is... return invalid flag...
1999 return (Unknown_Invalid
);
2003 Determine if the first item in a command line is valid.
2005 @param[in] CmdLine The command line to parse.
2007 @retval EFI_SUCCESS The item is valid.
2008 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
2009 @retval EFI_NOT_FOUND The operation type is unknown or invalid.
2013 IN CONST CHAR16
*CmdLine
2017 CHAR16
*FirstParameter
;
2023 Temp
= StrnCatGrow(&Temp
, NULL
, CmdLine
, 0);
2025 return (EFI_OUT_OF_RESOURCES
);
2028 FirstParameter
= StrStr(Temp
, L
"|");
2029 if (FirstParameter
!= NULL
) {
2030 *FirstParameter
= CHAR_NULL
;
2033 FirstParameter
= NULL
;
2036 // Process the command line
2038 Status
= ProcessCommandLineToFinal(&Temp
);
2040 if (!EFI_ERROR(Status
)) {
2041 FirstParameter
= AllocateZeroPool(StrSize(CmdLine
));
2042 if (FirstParameter
== NULL
) {
2043 SHELL_FREE_NON_NULL(Temp
);
2044 return (EFI_OUT_OF_RESOURCES
);
2046 TempWalker
= (CHAR16
*)Temp
;
2047 if (!EFI_ERROR(GetNextParameter(&TempWalker
, &FirstParameter
, StrSize(CmdLine
), TRUE
))) {
2048 if (GetOperationType(FirstParameter
) == Unknown_Invalid
) {
2049 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2050 SetLastError(SHELL_NOT_FOUND
);
2051 Status
= EFI_NOT_FOUND
;
2056 SHELL_FREE_NON_NULL(Temp
);
2057 SHELL_FREE_NON_NULL(FirstParameter
);
2062 Determine if a command line contains with a split contains only valid commands.
2064 @param[in] CmdLine The command line to parse.
2066 @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.
2067 @retval EFI_ABORTED CmdLine has at least one invalid command or application.
2071 IN CONST CHAR16
*CmdLine
2074 CONST CHAR16
*TempSpot
;
2078 // If this was the only item, then get out
2080 if (!ContainsSplit(CmdLine
)) {
2081 return (EFI_SUCCESS
);
2085 // Verify up to the pipe or end character
2087 Status
= IsValidSplit(CmdLine
);
2088 if (EFI_ERROR(Status
)) {
2093 // recurse to verify the next item
2095 TempSpot
= FindFirstCharacter(CmdLine
, L
"|", L
'^') + 1;
2096 if (*TempSpot
== L
'a' &&
2097 (*(TempSpot
+ 1) == L
' ' || *(TempSpot
+ 1) == CHAR_NULL
)
2099 // If it's an ASCII pipe '|a'
2103 return (VerifySplit(TempSpot
));
2107 Process a split based operation.
2109 @param[in] CmdLine pointer to the command line to process
2111 @retval EFI_SUCCESS The operation was successful
2112 @return an error occurred.
2115 ProcessNewSplitCommandLine(
2116 IN CONST CHAR16
*CmdLine
2122 Status
= VerifySplit(CmdLine
);
2123 if (EFI_ERROR(Status
)) {
2130 // are we in an existing split???
2132 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
2133 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
2136 if (Split
== NULL
) {
2137 Status
= RunSplitCommand(CmdLine
, NULL
, NULL
);
2139 Status
= RunSplitCommand(CmdLine
, Split
->SplitStdIn
, Split
->SplitStdOut
);
2141 if (EFI_ERROR(Status
)) {
2142 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_SPLIT
), ShellInfoObject
.HiiHandle
, CmdLine
);
2148 Handle a request to change the current file system.
2150 @param[in] CmdLine The passed in command line.
2152 @retval EFI_SUCCESS The operation was successful.
2156 IN CONST CHAR16
*CmdLine
2160 Status
= EFI_SUCCESS
;
2163 // make sure we are the right operation
2165 ASSERT(CmdLine
[(StrLen(CmdLine
)-1)] == L
':' && StrStr(CmdLine
, L
" ") == NULL
);
2168 // Call the protocol API to do the work
2170 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, CmdLine
);
2173 // Report any errors
2175 if (EFI_ERROR(Status
)) {
2176 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, CmdLine
);
2183 Reprocess the command line to direct all -? to the help command.
2185 if found, will add "help" as argv[0], and move the rest later.
2187 @param[in,out] CmdLine pointer to the command line to update
2191 IN OUT CHAR16
**CmdLine
2194 CHAR16
*CurrentParameter
;
2196 CHAR16
*NewCommandLine
;
2198 UINTN NewCmdLineSize
;
2200 Status
= EFI_SUCCESS
;
2202 CurrentParameter
= AllocateZeroPool(StrSize(*CmdLine
));
2203 if (CurrentParameter
== NULL
) {
2204 return (EFI_OUT_OF_RESOURCES
);
2208 while(Walker
!= NULL
&& *Walker
!= CHAR_NULL
) {
2209 if (!EFI_ERROR(GetNextParameter(&Walker
, &CurrentParameter
, StrSize(*CmdLine
), TRUE
))) {
2210 if (StrStr(CurrentParameter
, L
"-?") == CurrentParameter
) {
2211 CurrentParameter
[0] = L
' ';
2212 CurrentParameter
[1] = L
' ';
2213 NewCmdLineSize
= StrSize(L
"help ") + StrSize(*CmdLine
);
2214 NewCommandLine
= AllocateZeroPool(NewCmdLineSize
);
2215 if (NewCommandLine
== NULL
) {
2216 Status
= EFI_OUT_OF_RESOURCES
;
2221 // We know the space is sufficient since we just calculated it.
2223 StrnCpyS(NewCommandLine
, NewCmdLineSize
/sizeof(CHAR16
), L
"help ", 5);
2224 StrnCatS(NewCommandLine
, NewCmdLineSize
/sizeof(CHAR16
), *CmdLine
, StrLen(*CmdLine
));
2225 SHELL_FREE_NON_NULL(*CmdLine
);
2226 *CmdLine
= NewCommandLine
;
2232 SHELL_FREE_NON_NULL(CurrentParameter
);
2238 Function to update the shell variable "lasterror".
2240 @param[in] ErrorCode the error code to put into lasterror.
2244 IN CONST SHELL_STATUS ErrorCode
2247 CHAR16 LeString
[19];
2248 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
2249 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ErrorCode
);
2251 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", ErrorCode
);
2253 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
2254 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
2256 return (EFI_SUCCESS
);
2260 Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.
2262 @param[in,out] CmdLine pointer to the command line to update
2264 @retval EFI_SUCCESS The operation was successful
2265 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
2266 @return some other error occurred
2269 ProcessCommandLineToFinal(
2270 IN OUT CHAR16
**CmdLine
2274 TrimSpaces(CmdLine
);
2276 Status
= ShellSubstituteAliases(CmdLine
);
2277 if (EFI_ERROR(Status
)) {
2281 TrimSpaces(CmdLine
);
2283 Status
= ShellSubstituteVariables(CmdLine
);
2284 if (EFI_ERROR(Status
)) {
2287 ASSERT (*CmdLine
!= NULL
);
2289 TrimSpaces(CmdLine
);
2292 // update for help parsing
2294 if (StrStr(*CmdLine
, L
"?") != NULL
) {
2296 // This may do nothing if the ? does not indicate help.
2297 // Save all the details for in the API below.
2299 Status
= DoHelpUpdate(CmdLine
);
2302 TrimSpaces(CmdLine
);
2304 return (EFI_SUCCESS
);
2308 Run an internal shell command.
2310 This API will update the shell's environment since these commands are libraries.
2312 @param[in] CmdLine the command line to run.
2313 @param[in] FirstParameter the first parameter on the command line
2314 @param[in] ParamProtocol the shell parameters protocol pointer
2315 @param[out] CommandStatus the status from the command line.
2317 @retval EFI_SUCCESS The command was completed.
2318 @retval EFI_ABORTED The command's operation was aborted.
2322 IN CONST CHAR16
*CmdLine
,
2323 IN CHAR16
*FirstParameter
,
2324 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2325 OUT EFI_STATUS
*CommandStatus
2331 SHELL_STATUS CommandReturnedStatus
;
2336 NewCmdLine
= AllocateCopyPool (StrSize (CmdLine
), CmdLine
);
2337 if (NewCmdLine
== NULL
) {
2338 return EFI_OUT_OF_RESOURCES
;
2341 for (Walker
= NewCmdLine
; Walker
!= NULL
&& *Walker
!= CHAR_NULL
; Walker
++) {
2342 if (*Walker
== L
'^' && *(Walker
+1) == L
'#') {
2343 CopyMem(Walker
, Walker
+1, StrSize(Walker
) - sizeof(Walker
[0]));
2348 // get the argc and argv updated for internal commands
2350 Status
= UpdateArgcArgv(ParamProtocol
, NewCmdLine
, Internal_Command
, &Argv
, &Argc
);
2351 if (!EFI_ERROR(Status
)) {
2353 // Run the internal command.
2355 Status
= ShellCommandRunCommandHandler(FirstParameter
, &CommandReturnedStatus
, &LastError
);
2357 if (!EFI_ERROR(Status
)) {
2358 if (CommandStatus
!= NULL
) {
2359 if (CommandReturnedStatus
!= SHELL_SUCCESS
) {
2360 *CommandStatus
= (EFI_STATUS
)(CommandReturnedStatus
| MAX_BIT
);
2362 *CommandStatus
= EFI_SUCCESS
;
2367 // Update last error status.
2368 // some commands do not update last error.
2371 SetLastError(CommandReturnedStatus
);
2375 // Pass thru the exitcode from the app.
2377 if (ShellCommandGetExit()) {
2379 // An Exit was requested ("exit" command), pass its value up.
2381 Status
= CommandReturnedStatus
;
2382 } else if (CommandReturnedStatus
!= SHELL_SUCCESS
&& IsScriptOnlyCommand(FirstParameter
)) {
2384 // Always abort when a script only command fails for any reason
2386 Status
= EFI_ABORTED
;
2387 } else if (ShellCommandGetCurrentScriptFile() != NULL
&& CommandReturnedStatus
== SHELL_ABORTED
) {
2389 // Abort when in a script and a command aborted
2391 Status
= EFI_ABORTED
;
2397 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
2398 // This is safe even if the update API failed. In this case, it may be a no-op.
2400 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);
2403 // If a script is running and the command is not a script only command, then
2404 // change return value to success so the script won't halt (unless aborted).
2406 // Script only commands have to be able halt the script since the script will
2407 // not operate if they are failing.
2409 if ( ShellCommandGetCurrentScriptFile() != NULL
2410 && !IsScriptOnlyCommand(FirstParameter
)
2411 && Status
!= EFI_ABORTED
2413 Status
= EFI_SUCCESS
;
2416 FreePool (NewCmdLine
);
2421 Function to run the command or file.
2423 @param[in] Type the type of operation being run.
2424 @param[in] CmdLine the command line to run.
2425 @param[in] FirstParameter the first parameter on the command line
2426 @param[in] ParamProtocol the shell parameters protocol pointer
2427 @param[out] CommandStatus the status from the command line.
2429 @retval EFI_SUCCESS The command was completed.
2430 @retval EFI_ABORTED The command's operation was aborted.
2434 IN SHELL_OPERATION_TYPES Type
,
2435 IN CONST CHAR16
*CmdLine
,
2436 IN CHAR16
*FirstParameter
,
2437 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2438 OUT EFI_STATUS
*CommandStatus
2442 EFI_STATUS StartStatus
;
2443 CHAR16
*CommandWithPath
;
2444 CHAR16
*FullCommandWithPath
;
2445 EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
2446 SHELL_STATUS CalleeExitStatus
;
2448 Status
= EFI_SUCCESS
;
2449 CommandWithPath
= NULL
;
2451 CalleeExitStatus
= SHELL_INVALID_PARAMETER
;
2454 case Internal_Command
:
2455 Status
= RunInternalCommand(CmdLine
, FirstParameter
, ParamProtocol
, CommandStatus
);
2457 case Script_File_Name
:
2458 case Efi_Application
:
2460 // Process a fully qualified path
2462 if (StrStr(FirstParameter
, L
":") != NULL
) {
2463 ASSERT (CommandWithPath
== NULL
);
2464 if (ShellIsFile(FirstParameter
) == EFI_SUCCESS
) {
2465 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, FirstParameter
, 0);
2470 // Process a relative path and also check in the path environment variable
2472 if (CommandWithPath
== NULL
) {
2473 CommandWithPath
= ShellFindFilePathEx(FirstParameter
, mExecutableExtensions
);
2477 // This should be impossible now.
2479 ASSERT(CommandWithPath
!= NULL
);
2482 // Make sure that path is not just a directory (or not found)
2484 if (!EFI_ERROR(ShellIsDirectory(CommandWithPath
))) {
2485 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2486 SetLastError(SHELL_NOT_FOUND
);
2489 case Script_File_Name
:
2490 FullCommandWithPath
= FullyQualifyPath(CommandWithPath
);
2491 if (FullCommandWithPath
== NULL
) {
2492 Status
= RunScriptFile (CommandWithPath
, NULL
, CmdLine
, ParamProtocol
);
2494 Status
= RunScriptFile (FullCommandWithPath
, NULL
, CmdLine
, ParamProtocol
);
2495 FreePool(FullCommandWithPath
);
2498 case Efi_Application
:
2500 // Get the device path of the application image
2502 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
2503 if (DevPath
== NULL
){
2504 Status
= EFI_OUT_OF_RESOURCES
;
2509 // Execute the device path
2511 Status
= InternalShellExecuteDevicePath(
2519 SHELL_FREE_NON_NULL(DevPath
);
2521 if(EFI_ERROR (Status
)) {
2522 CalleeExitStatus
= (SHELL_STATUS
) (Status
& (~MAX_BIT
));
2524 CalleeExitStatus
= (SHELL_STATUS
) StartStatus
;
2527 if (CommandStatus
!= NULL
) {
2528 *CommandStatus
= CalleeExitStatus
;
2532 // Update last error status.
2534 // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
2535 SetLastError(CalleeExitStatus
);
2551 SHELL_FREE_NON_NULL(CommandWithPath
);
2557 Function to setup StdIn, StdErr, StdOut, and then run the command or file.
2559 @param[in] Type the type of operation being run.
2560 @param[in] CmdLine the command line to run.
2561 @param[in] FirstParameter the first parameter on the command line.
2562 @param[in] ParamProtocol the shell parameters protocol pointer
2563 @param[out] CommandStatus the status from the command line.
2565 @retval EFI_SUCCESS The command was completed.
2566 @retval EFI_ABORTED The command's operation was aborted.
2569 SetupAndRunCommandOrFile(
2570 IN SHELL_OPERATION_TYPES Type
,
2572 IN CHAR16
*FirstParameter
,
2573 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2574 OUT EFI_STATUS
*CommandStatus
2578 SHELL_FILE_HANDLE OriginalStdIn
;
2579 SHELL_FILE_HANDLE OriginalStdOut
;
2580 SHELL_FILE_HANDLE OriginalStdErr
;
2581 SYSTEM_TABLE_INFO OriginalSystemTableInfo
;
2582 CONST SCRIPT_FILE
*ConstScriptFile
;
2585 // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
2587 Status
= UpdateStdInStdOutStdErr(ParamProtocol
, CmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2590 // The StdIn, StdOut, and StdErr are set up.
2591 // Now run the command, script, or application
2593 if (!EFI_ERROR(Status
)) {
2594 TrimSpaces(&CmdLine
);
2595 Status
= RunCommandOrFile(Type
, CmdLine
, FirstParameter
, ParamProtocol
, CommandStatus
);
2601 if (EFI_ERROR(Status
)) {
2602 ConstScriptFile
= ShellCommandGetCurrentScriptFile();
2603 if (ConstScriptFile
== NULL
|| ConstScriptFile
->CurrentCommand
== NULL
) {
2604 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
));
2606 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR_SCRIPT
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
), ConstScriptFile
->CurrentCommand
->Line
);
2611 // put back the original StdIn, StdOut, and StdErr
2613 RestoreStdInStdOutStdErr(ParamProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2619 Function will process and run a command line.
2621 This will determine if the command line represents an internal shell
2622 command or dispatch an external application.
2624 @param[in] CmdLine The command line to parse.
2625 @param[out] CommandStatus The status from the command line.
2627 @retval EFI_SUCCESS The command was completed.
2628 @retval EFI_ABORTED The command's operation was aborted.
2632 IN CONST CHAR16
*CmdLine
,
2633 OUT EFI_STATUS
*CommandStatus
2637 CHAR16
*CleanOriginal
;
2638 CHAR16
*FirstParameter
;
2640 SHELL_OPERATION_TYPES Type
;
2641 CONST CHAR16
*CurDir
;
2643 ASSERT(CmdLine
!= NULL
);
2644 if (StrLen(CmdLine
) == 0) {
2645 return (EFI_SUCCESS
);
2648 Status
= EFI_SUCCESS
;
2649 CleanOriginal
= NULL
;
2651 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
2652 if (CleanOriginal
== NULL
) {
2653 return (EFI_OUT_OF_RESOURCES
);
2656 TrimSpaces(&CleanOriginal
);
2659 // NULL out comments (leveraged from RunScriptFileHandle() ).
2660 // The # character on a line is used to denote that all characters on the same line
2661 // and to the right of the # are to be ignored by the shell.
2662 // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.
2664 for (TempWalker
= CleanOriginal
; TempWalker
!= NULL
&& *TempWalker
!= CHAR_NULL
; TempWalker
++) {
2665 if (*TempWalker
== L
'^') {
2666 if (*(TempWalker
+ 1) == L
'#') {
2669 } else if (*TempWalker
== L
'#') {
2670 *TempWalker
= CHAR_NULL
;
2674 TrimSpaces(&CleanOriginal
);
2677 // Handle case that passed in command line is just 1 or more " " characters.
2679 if (StrLen (CleanOriginal
) == 0) {
2680 SHELL_FREE_NON_NULL(CleanOriginal
);
2681 return (EFI_SUCCESS
);
2684 Status
= ProcessCommandLineToFinal(&CleanOriginal
);
2685 if (EFI_ERROR(Status
)) {
2686 SHELL_FREE_NON_NULL(CleanOriginal
);
2691 // We don't do normal processing with a split command line (output from one command input to another)
2693 if (ContainsSplit(CleanOriginal
)) {
2694 Status
= ProcessNewSplitCommandLine(CleanOriginal
);
2695 SHELL_FREE_NON_NULL(CleanOriginal
);
2700 // We need the first parameter information so we can determine the operation type
2702 FirstParameter
= AllocateZeroPool(StrSize(CleanOriginal
));
2703 if (FirstParameter
== NULL
) {
2704 SHELL_FREE_NON_NULL(CleanOriginal
);
2705 return (EFI_OUT_OF_RESOURCES
);
2707 TempWalker
= CleanOriginal
;
2708 if (!EFI_ERROR(GetNextParameter(&TempWalker
, &FirstParameter
, StrSize(CleanOriginal
), TRUE
))) {
2710 // Depending on the first parameter we change the behavior
2712 switch (Type
= GetOperationType(FirstParameter
)) {
2713 case File_Sys_Change
:
2714 Status
= ChangeMappedDrive (FirstParameter
);
2716 case Internal_Command
:
2717 case Script_File_Name
:
2718 case Efi_Application
:
2719 Status
= SetupAndRunCommandOrFile(Type
, CleanOriginal
, FirstParameter
, ShellInfoObject
.NewShellParametersProtocol
, CommandStatus
);
2723 // Whatever was typed, it was invalid.
2725 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2726 SetLastError(SHELL_NOT_FOUND
);
2730 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2731 SetLastError(SHELL_NOT_FOUND
);
2734 // Check whether the current file system still exists. If not exist, we need update "cwd" and gShellCurMapping.
2736 CurDir
= EfiShellGetCurDir (NULL
);
2737 if (CurDir
!= NULL
) {
2738 if (EFI_ERROR(ShellFileExists (CurDir
))) {
2740 // EfiShellSetCurDir() cannot set current directory to NULL.
2741 // EfiShellSetEnv() is not allowed to set the "cwd" variable.
2742 // Only InternalEfiShellSetEnv () is allowed setting the "cwd" variable.
2744 InternalEfiShellSetEnv (L
"cwd", NULL
, TRUE
);
2745 gShellCurMapping
= NULL
;
2749 SHELL_FREE_NON_NULL(CleanOriginal
);
2750 SHELL_FREE_NON_NULL(FirstParameter
);
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.
2763 @retval EFI_SUCCESS The command was completed.
2764 @retval EFI_ABORTED The command's operation was aborted.
2768 IN CONST CHAR16
*CmdLine
2771 return (RunShellCommand(CmdLine
, NULL
));
2775 Function to process a NSH script file via SHELL_FILE_HANDLE.
2777 @param[in] Handle The handle to the already opened file.
2778 @param[in] Name The name of the script file.
2780 @retval EFI_SUCCESS the script completed successfully
2783 RunScriptFileHandle (
2784 IN SHELL_FILE_HANDLE Handle
,
2785 IN CONST CHAR16
*Name
2789 SCRIPT_FILE
*NewScriptFile
;
2791 UINTN PrintBuffSize
;
2792 CHAR16
*CommandLine
;
2793 CHAR16
*CommandLine2
;
2794 CHAR16
*CommandLine3
;
2795 SCRIPT_COMMAND_LIST
*LastCommand
;
2797 BOOLEAN PreScriptEchoState
;
2798 BOOLEAN PreCommandEchoState
;
2799 CONST CHAR16
*CurDir
;
2801 CHAR16 LeString
[50];
2802 LIST_ENTRY OldBufferList
;
2804 ASSERT(!ShellCommandGetScriptExit());
2806 PreScriptEchoState
= ShellCommandGetEchoState();
2807 PrintBuffSize
= PcdGet16(PcdShellPrintBufferSize
);
2809 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
2810 if (NewScriptFile
== NULL
) {
2811 return (EFI_OUT_OF_RESOURCES
);
2817 ASSERT(NewScriptFile
->ScriptName
== NULL
);
2818 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
2819 if (NewScriptFile
->ScriptName
== NULL
) {
2820 DeleteScriptFileStruct(NewScriptFile
);
2821 return (EFI_OUT_OF_RESOURCES
);
2825 // Save the parameters (used to replace %0 to %9 later on)
2827 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
2828 if (NewScriptFile
->Argc
!= 0) {
2829 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
2830 if (NewScriptFile
->Argv
== NULL
) {
2831 DeleteScriptFileStruct(NewScriptFile
);
2832 return (EFI_OUT_OF_RESOURCES
);
2835 // Put the full path of the script file into Argv[0] as required by section
2836 // 3.6.2 of version 2.2 of the shell specification.
2838 NewScriptFile
->Argv
[0] = StrnCatGrow(&NewScriptFile
->Argv
[0], NULL
, NewScriptFile
->ScriptName
, 0);
2839 for (LoopVar
= 1 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
2840 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
2841 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
2842 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
2843 DeleteScriptFileStruct(NewScriptFile
);
2844 return (EFI_OUT_OF_RESOURCES
);
2848 NewScriptFile
->Argv
= NULL
;
2851 InitializeListHead(&NewScriptFile
->CommandList
);
2852 InitializeListHead(&NewScriptFile
->SubstList
);
2855 // Now build the list of all script commands.
2858 while(!ShellFileHandleEof(Handle
)) {
2859 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
2861 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0 || CommandLine
[0] == '#') {
2862 SHELL_FREE_NON_NULL(CommandLine
);
2865 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
2866 if (NewScriptFile
->CurrentCommand
== NULL
) {
2867 SHELL_FREE_NON_NULL(CommandLine
);
2868 DeleteScriptFileStruct(NewScriptFile
);
2869 return (EFI_OUT_OF_RESOURCES
);
2872 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
2873 NewScriptFile
->CurrentCommand
->Data
= NULL
;
2874 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
2876 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2880 // Add this as the topmost script file
2882 ShellCommandSetNewScript (NewScriptFile
);
2885 // Now enumerate through the commands and run each one.
2887 CommandLine
= AllocateZeroPool(PrintBuffSize
);
2888 if (CommandLine
== NULL
) {
2889 DeleteScriptFileStruct(NewScriptFile
);
2890 return (EFI_OUT_OF_RESOURCES
);
2892 CommandLine2
= AllocateZeroPool(PrintBuffSize
);
2893 if (CommandLine2
== NULL
) {
2894 FreePool(CommandLine
);
2895 DeleteScriptFileStruct(NewScriptFile
);
2896 return (EFI_OUT_OF_RESOURCES
);
2899 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
2900 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
2901 ; // conditional increment in the body of the loop
2903 ASSERT(CommandLine2
!= NULL
);
2904 StrnCpyS( CommandLine2
,
2905 PrintBuffSize
/sizeof(CHAR16
),
2906 NewScriptFile
->CurrentCommand
->Cl
,
2907 PrintBuffSize
/sizeof(CHAR16
) - 1
2910 SaveBufferList(&OldBufferList
);
2913 // NULL out comments
2915 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
2916 if (*CommandLine3
== L
'^') {
2917 if ( *(CommandLine3
+1) == L
':') {
2918 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
2919 } else if (*(CommandLine3
+1) == L
'#') {
2922 } else if (*CommandLine3
== L
'#') {
2923 *CommandLine3
= CHAR_NULL
;
2927 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
2929 // Due to variability in starting the find and replace action we need to have both buffers the same.
2931 StrnCpyS( CommandLine
,
2932 PrintBuffSize
/sizeof(CHAR16
),
2934 PrintBuffSize
/sizeof(CHAR16
) - 1
2938 // Remove the %0 to %9 from the command line (if we have some arguments)
2940 if (NewScriptFile
->Argv
!= NULL
) {
2941 switch (NewScriptFile
->Argc
) {
2943 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%9", NewScriptFile
->Argv
[9], FALSE
, FALSE
);
2944 ASSERT_EFI_ERROR(Status
);
2946 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%8", NewScriptFile
->Argv
[8], FALSE
, FALSE
);
2947 ASSERT_EFI_ERROR(Status
);
2949 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%7", NewScriptFile
->Argv
[7], FALSE
, FALSE
);
2950 ASSERT_EFI_ERROR(Status
);
2952 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%6", NewScriptFile
->Argv
[6], FALSE
, FALSE
);
2953 ASSERT_EFI_ERROR(Status
);
2955 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%5", NewScriptFile
->Argv
[5], FALSE
, FALSE
);
2956 ASSERT_EFI_ERROR(Status
);
2958 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%4", NewScriptFile
->Argv
[4], FALSE
, FALSE
);
2959 ASSERT_EFI_ERROR(Status
);
2961 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%3", NewScriptFile
->Argv
[3], FALSE
, FALSE
);
2962 ASSERT_EFI_ERROR(Status
);
2964 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%2", NewScriptFile
->Argv
[2], FALSE
, FALSE
);
2965 ASSERT_EFI_ERROR(Status
);
2967 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%1", NewScriptFile
->Argv
[1], FALSE
, FALSE
);
2968 ASSERT_EFI_ERROR(Status
);
2970 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%0", NewScriptFile
->Argv
[0], FALSE
, FALSE
);
2971 ASSERT_EFI_ERROR(Status
);
2977 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%1", L
"\"\"", FALSE
, FALSE
);
2978 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%2", L
"\"\"", FALSE
, FALSE
);
2979 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%3", L
"\"\"", FALSE
, FALSE
);
2980 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%4", L
"\"\"", FALSE
, FALSE
);
2981 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%5", L
"\"\"", FALSE
, FALSE
);
2982 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%6", L
"\"\"", FALSE
, FALSE
);
2983 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%7", L
"\"\"", FALSE
, FALSE
);
2984 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%8", L
"\"\"", FALSE
, FALSE
);
2985 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%9", L
"\"\"", FALSE
, FALSE
);
2987 StrnCpyS( CommandLine2
,
2988 PrintBuffSize
/sizeof(CHAR16
),
2990 PrintBuffSize
/sizeof(CHAR16
) - 1
2993 LastCommand
= NewScriptFile
->CurrentCommand
;
2995 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
2997 if (CommandLine3
!= NULL
&& CommandLine3
[0] == L
':' ) {
2999 // This line is a goto target / label
3002 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
3003 if (CommandLine3
[0] == L
'@') {
3005 // We need to save the current echo state
3006 // and disable echo for just this command.
3008 PreCommandEchoState
= ShellCommandGetEchoState();
3009 ShellCommandSetEchoState(FALSE
);
3010 Status
= RunCommand(CommandLine3
+1);
3013 // If command was "@echo -off" or "@echo -on" then don't restore echo state
3015 if (StrCmp (L
"@echo -off", CommandLine3
) != 0 &&
3016 StrCmp (L
"@echo -on", CommandLine3
) != 0) {
3018 // Now restore the pre-'@' echo state.
3020 ShellCommandSetEchoState(PreCommandEchoState
);
3023 if (ShellCommandGetEchoState()) {
3024 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
3025 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
3026 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
3028 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
3030 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
3032 Status
= RunCommand(CommandLine3
);
3036 if (ShellCommandGetScriptExit()) {
3038 // ShellCommandGetExitCode() always returns a UINT64
3040 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ShellCommandGetExitCode());
3041 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
3042 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
3044 ShellCommandRegisterExit(FALSE
, 0);
3045 Status
= EFI_SUCCESS
;
3046 RestoreBufferList(&OldBufferList
);
3049 if (ShellGetExecutionBreakFlag()) {
3050 RestoreBufferList(&OldBufferList
);
3053 if (EFI_ERROR(Status
)) {
3054 RestoreBufferList(&OldBufferList
);
3057 if (ShellCommandGetExit()) {
3058 RestoreBufferList(&OldBufferList
);
3063 // If that commend did not update the CurrentCommand then we need to advance it...
3065 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
3066 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
3067 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
3068 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
3072 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
3073 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
3074 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
3077 RestoreBufferList(&OldBufferList
);
3081 FreePool(CommandLine
);
3082 FreePool(CommandLine2
);
3083 ShellCommandSetNewScript (NULL
);
3086 // Only if this was the last script reset the state.
3088 if (ShellCommandGetCurrentScriptFile()==NULL
) {
3089 ShellCommandSetEchoState(PreScriptEchoState
);
3091 return (EFI_SUCCESS
);
3095 Function to process a NSH script file.
3097 @param[in] ScriptPath Pointer to the script file name (including file system path).
3098 @param[in] Handle the handle of the script file already opened.
3099 @param[in] CmdLine the command line to run.
3100 @param[in] ParamProtocol the shell parameters protocol pointer
3102 @retval EFI_SUCCESS the script completed successfully
3106 IN CONST CHAR16
*ScriptPath
,
3107 IN SHELL_FILE_HANDLE Handle OPTIONAL
,
3108 IN CONST CHAR16
*CmdLine
,
3109 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
3113 SHELL_FILE_HANDLE FileHandle
;
3117 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
3118 return (EFI_INVALID_PARAMETER
);
3122 // get the argc and argv updated for scripts
3124 Status
= UpdateArgcArgv(ParamProtocol
, CmdLine
, Script_File_Name
, &Argv
, &Argc
);
3125 if (!EFI_ERROR(Status
)) {
3127 if (Handle
== NULL
) {
3131 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
3132 if (!EFI_ERROR(Status
)) {
3136 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
);
3139 // now close the file
3141 ShellCloseFile(&FileHandle
);
3144 Status
= RunScriptFileHandle(Handle
, ScriptPath
);
3149 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
3150 // This is safe even if the update API failed. In this case, it may be a no-op.
3152 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);
3158 Return the pointer to the first occurrence of any character from a list of characters.
3160 @param[in] String the string to parse
3161 @param[in] CharacterList the list of character to look for
3162 @param[in] EscapeCharacter An escape character to skip
3164 @return the location of the first character in the string
3165 @retval CHAR_NULL no instance of any character in CharacterList was found in String
3169 IN CONST CHAR16
*String
,
3170 IN CONST CHAR16
*CharacterList
,
3171 IN CONST CHAR16 EscapeCharacter
3177 for (WalkStr
= 0; WalkStr
< StrLen(String
); WalkStr
++) {
3178 if (String
[WalkStr
] == EscapeCharacter
) {
3182 for (WalkChar
= 0; WalkChar
< StrLen(CharacterList
); WalkChar
++) {
3183 if (String
[WalkStr
] == CharacterList
[WalkChar
]) {
3184 return (&String
[WalkStr
]);
3188 return (String
+ StrLen(String
));