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
;
347 if (PcdGet8(PcdShellSupportLevel
) > 3) {
348 return (EFI_UNSUPPORTED
);
354 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
355 if (EFI_ERROR(Status
)) {
360 // Populate the global structure from PCDs
362 ShellInfoObject
.ImageDevPath
= NULL
;
363 ShellInfoObject
.FileDevPath
= NULL
;
364 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
365 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
366 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
369 // verify we dont allow for spec violation
371 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
374 // Initialize the LIST ENTRY objects...
376 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
377 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
378 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
381 // Check PCDs for optional features that are not implemented yet.
383 if ( PcdGetBool(PcdShellSupportOldProtocols
)
384 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
385 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
387 return (EFI_UNSUPPORTED
);
391 // turn off the watchdog timer
393 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
396 // install our console logger. This will keep a log of the output for back-browsing
398 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
399 if (!EFI_ERROR(Status
)) {
401 // Enable the cursor to be visible
403 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
406 // If supporting EFI 1.1 we need to install HII protocol
407 // only do this if PcdShellRequireHiiPlatform == FALSE
409 // remove EFI_UNSUPPORTED check above when complete.
410 ///@todo add support for Framework HII
413 // install our (solitary) HII package
415 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
416 if (ShellInfoObject
.HiiHandle
== NULL
) {
417 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
418 ///@todo Add our package into Framework HII
420 if (ShellInfoObject
.HiiHandle
== NULL
) {
421 Status
= EFI_NOT_STARTED
;
427 // create and install the EfiShellParametersProtocol
429 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
430 ASSERT_EFI_ERROR(Status
);
431 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
434 // create and install the EfiShellProtocol
436 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
437 ASSERT_EFI_ERROR(Status
);
438 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
441 // Now initialize the shell library (it requires Shell Parameters protocol)
443 Status
= ShellInitialize();
444 ASSERT_EFI_ERROR(Status
);
446 Status
= CommandInit();
447 ASSERT_EFI_ERROR(Status
);
449 Status
= ShellInitEnvVarList ();
452 // Check the command line
454 Status
= ProcessCommandLine ();
455 if (EFI_ERROR (Status
)) {
460 // If shell support level is >= 1 create the mappings and paths
462 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
463 Status
= ShellCommandCreateInitialMappingsAndPaths();
467 // Set the environment variable for nesting support
471 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoNest
) {
473 // No change. require nesting in Shell Protocol Execute()
475 StrnCatGrow(&TempString
,
480 StrnCatGrow(&TempString
,
485 Status
= InternalEfiShellSetEnv(mNoNestingEnvVarName
, TempString
, TRUE
);
486 SHELL_FREE_NON_NULL(TempString
);
490 // save the device path for the loaded image and the device path for the filepath (under loaded image)
491 // These are where to look for the startup.nsh file
493 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
494 ASSERT_EFI_ERROR(Status
);
497 // Display the version
499 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
502 gST
->ConOut
->Mode
->CursorRow
,
504 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL
),
505 ShellInfoObject
.HiiHandle
,
506 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
507 gEfiShellProtocol
->MajorVersion
,
508 gEfiShellProtocol
->MinorVersion
515 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER
),
516 ShellInfoObject
.HiiHandle
,
517 (CHAR16
*) PcdGetPtr (PcdShellSupplier
)
524 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI
),
525 ShellInfoObject
.HiiHandle
,
526 (gST
->Hdr
.Revision
&0xffff0000)>>16,
527 (gST
->Hdr
.Revision
&0x0000ffff),
529 gST
->FirmwareRevision
534 // Display the mapping
536 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
537 Status
= RunCommand(L
"map");
538 ASSERT_EFI_ERROR(Status
);
542 // init all the built in alias'
544 Status
= SetBuiltInAlias();
545 ASSERT_EFI_ERROR(Status
);
548 // Initialize environment variables
550 if (ShellCommandGetProfileList() != NULL
) {
551 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
552 ASSERT_EFI_ERROR(Status
);
556 TempString
= AllocateZeroPool(Size
);
558 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
559 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
560 ASSERT_EFI_ERROR(Status
);
562 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
563 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
564 ASSERT_EFI_ERROR(Status
);
566 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
567 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
568 ASSERT_EFI_ERROR(Status
);
570 FreePool(TempString
);
572 if (!EFI_ERROR(Status
)) {
573 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
575 // Set up the event for CTRL-C monitoring...
577 Status
= InernalEfiShellStartMonitor();
580 if (!EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
582 // Set up the event for CTRL-S monitoring...
584 Status
= InternalEfiShellStartCtrlSMonitor();
587 if (!EFI_ERROR(Status
) && ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
589 // close off the gST->ConIn
591 OldConIn
= gST
->ConIn
;
592 ConInHandle
= gST
->ConsoleInHandle
;
593 gST
->ConIn
= CreateSimpleTextInOnFile((SHELL_FILE_HANDLE
)&FileInterfaceNulFile
, &gST
->ConsoleInHandle
);
599 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
601 // process the startup script or launch the called app.
603 Status
= DoStartupScript(ShellInfoObject
.ImageDevPath
, ShellInfoObject
.FileDevPath
);
606 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
&& !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
608 // begin the UI waiting loop
612 // clean out all the memory allocated for CONST <something> * return values
613 // between each shell prompt presentation
615 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
616 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
620 // Reset page break back to default.
622 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
623 ASSERT (ShellInfoObject
.ConsoleInfo
!= NULL
);
624 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
625 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
628 // Reset the CTRL-C event (yes we ignore the return values)
630 Status
= gBS
->CheckEvent (ShellInfoObject
.NewEfiShellProtocol
->ExecutionBreak
);
635 Status
= DoShellPrompt();
636 } while (!ShellCommandGetExit());
638 if (OldConIn
!= NULL
&& ConInHandle
!= NULL
) {
639 CloseSimpleTextInOnFile (gST
->ConIn
);
640 gST
->ConIn
= OldConIn
;
641 gST
->ConsoleInHandle
= ConInHandle
;
648 // uninstall protocols / free memory / etc...
650 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
651 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
652 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
654 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
655 FreePool(ShellInfoObject
.ImageDevPath
);
656 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
658 if (ShellInfoObject
.FileDevPath
!= NULL
) {
659 FreePool(ShellInfoObject
.FileDevPath
);
660 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
662 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
663 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
664 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
666 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
667 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
668 InternalEfiShellSetEnv(L
"cwd", NULL
, TRUE
);
670 CleanUpShellEnvironment (ShellInfoObject
.NewEfiShellProtocol
);
671 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
674 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
675 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
678 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
679 ASSERT(FALSE
); ///@todo finish this de-allocation (free SplitStdIn/Out when needed).
681 for ( Split
= (SPLIT_LIST
*)GetFirstNode (&ShellInfoObject
.SplitList
.Link
)
682 ; !IsNull (&ShellInfoObject
.SplitList
.Link
, &Split
->Link
)
683 ; Split
= (SPLIT_LIST
*)GetNextNode (&ShellInfoObject
.SplitList
.Link
, &Split
->Link
)
685 RemoveEntryList (&Split
->Link
);
689 DEBUG_CODE (InitializeListHead (&ShellInfoObject
.SplitList
.Link
););
692 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
693 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
694 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
697 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
698 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
699 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
702 if (ShellInfoObject
.HiiHandle
!= NULL
) {
703 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
704 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
707 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
708 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
711 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
712 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
713 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
714 FreePool(ShellInfoObject
.ConsoleInfo
);
715 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
718 ShellFreeEnvVarList ();
720 if (ShellCommandGetExit()) {
721 return ((EFI_STATUS
)ShellCommandGetExitCode());
727 Sets all the alias' that were registered with the ShellCommandLib library.
729 @retval EFI_SUCCESS all init commands were run successfully.
737 CONST ALIAS_LIST
*List
;
741 // Get all the commands we want to run
743 List
= ShellCommandGetInitAliasList();
746 // for each command in the List
748 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
749 ; !IsNull (&List
->Link
, &Node
->Link
)
750 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
753 // install the alias'
755 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
756 ASSERT_EFI_ERROR(Status
);
758 return (EFI_SUCCESS
);
762 Internal function to determine if 2 command names are really the same.
764 @param[in] Command1 The pointer to the first command name.
765 @param[in] Command2 The pointer to the second command name.
767 @retval TRUE The 2 command names are the same.
768 @retval FALSE The 2 command names are not the same.
773 IN CONST CHAR16
*Command1
,
774 IN CONST CHAR16
*Command2
777 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
784 Internal function to determine if a command is a script only command.
786 @param[in] CommandName The pointer to the command name.
788 @retval TRUE The command is a script only command.
789 @retval FALSE The command is not a script only command.
794 IN CONST CHAR16
*CommandName
797 if (IsCommand(CommandName
, L
"for")
798 ||IsCommand(CommandName
, L
"endfor")
799 ||IsCommand(CommandName
, L
"if")
800 ||IsCommand(CommandName
, L
"else")
801 ||IsCommand(CommandName
, L
"endif")
802 ||IsCommand(CommandName
, L
"goto")) {
809 This function will populate the 2 device path protocol parameters based on the
810 global gImageHandle. The DevPath will point to the device path for the handle that has
811 loaded image protocol installed on it. The FilePath will point to the device path
812 for the file that was loaded.
814 @param[in, out] DevPath On a successful return the device path to the loaded image.
815 @param[in, out] FilePath On a successful return the device path to the file.
817 @retval EFI_SUCCESS The 2 device paths were successfully returned.
818 @retval other A error from gBS->HandleProtocol.
824 GetDevicePathsForImageAndFile (
825 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
826 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
830 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
831 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
833 ASSERT(DevPath
!= NULL
);
834 ASSERT(FilePath
!= NULL
);
836 Status
= gBS
->OpenProtocol (
838 &gEfiLoadedImageProtocolGuid
,
839 (VOID
**)&LoadedImage
,
842 EFI_OPEN_PROTOCOL_GET_PROTOCOL
844 if (!EFI_ERROR (Status
)) {
845 Status
= gBS
->OpenProtocol (
846 LoadedImage
->DeviceHandle
,
847 &gEfiDevicePathProtocolGuid
,
848 (VOID
**)&ImageDevicePath
,
851 EFI_OPEN_PROTOCOL_GET_PROTOCOL
853 if (!EFI_ERROR (Status
)) {
854 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
855 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
857 LoadedImage
->DeviceHandle
,
858 &gEfiDevicePathProtocolGuid
,
864 &gEfiLoadedImageProtocolGuid
,
872 Process all Uefi Shell 2.0 command line options.
874 see Uefi Shell 2.0 section 3.2 for full details.
876 the command line must resemble the following:
878 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
880 ShellOpt-options Options which control the initialization behavior of the shell.
881 These options are read from the EFI global variable "ShellOpt"
882 and are processed before options or file-name.
884 options Options which control the initialization behavior of the shell.
886 file-name The name of a UEFI shell application or script to be executed
887 after initialization is complete. By default, if file-name is
888 specified, then -nostartup is implied. Scripts are not supported
891 file-name-options The command-line options that are passed to file-name when it
894 This will initialize the ShellInfoObject.ShellInitSettings global variable.
896 @retval EFI_SUCCESS The variable is initialized.
907 CHAR16
*DelayValueStr
;
910 EFI_UNICODE_COLLATION_PROTOCOL
*UnicodeCollation
;
912 // `file-name-options` will contain arguments to `file-name` that we don't
913 // know about. This would cause ShellCommandLineParse to error, so we parse
914 // arguments manually, ignoring those after the first thing that doesn't look
915 // like a shell option (which is assumed to be `file-name`).
917 Status
= gBS
->LocateProtocol (
918 &gEfiUnicodeCollation2ProtocolGuid
,
920 (VOID
**) &UnicodeCollation
922 if (EFI_ERROR (Status
)) {
923 Status
= gBS
->LocateProtocol (
924 &gEfiUnicodeCollationProtocolGuid
,
926 (VOID
**) &UnicodeCollation
928 if (EFI_ERROR (Status
)) {
933 // Set default options
934 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= FALSE
;
935 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= FALSE
;
936 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= FALSE
;
937 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= FALSE
;
938 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= FALSE
;
939 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= FALSE
;
940 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= FALSE
;
941 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= FALSE
;
942 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= FALSE
;
943 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoNest
= FALSE
;
944 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
947 // Start LoopVar at 0 to parse only optional arguments at Argv[0]
948 // and parse other parameters from Argv[1]. This is for use case that
949 // UEFI Shell boot option is created, and OptionalData is provided
950 // that starts with shell command-line options.
952 for (LoopVar
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
953 CurrentArg
= gEfiShellParametersProtocol
->Argv
[LoopVar
];
954 if (UnicodeCollation
->StriColl (
959 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= TRUE
;
961 else if (UnicodeCollation
->StriColl (
966 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= TRUE
;
968 else if (UnicodeCollation
->StriColl (
973 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= TRUE
;
975 else if (UnicodeCollation
->StriColl (
980 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= TRUE
;
982 else if (UnicodeCollation
->StriColl (
987 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= TRUE
;
989 else if (UnicodeCollation
->StriColl (
994 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= TRUE
;
996 else if (UnicodeCollation
->StriColl (
1001 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= TRUE
;
1003 else if (UnicodeCollation
->StriColl (
1008 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoNest
= TRUE
;
1010 else if (UnicodeCollation
->StriColl (
1015 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= TRUE
;
1016 // Check for optional delay value following "-delay"
1017 DelayValueStr
= gEfiShellParametersProtocol
->Argv
[LoopVar
+ 1];
1018 if (DelayValueStr
!= NULL
){
1019 if (*DelayValueStr
== L
':') {
1022 if (!EFI_ERROR(ShellConvertStringToUint64 (
1028 ShellInfoObject
.ShellInitSettings
.Delay
= (UINTN
)DelayValue
;
1032 } else if (UnicodeCollation
->StriColl (
1037 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= TRUE
;
1038 } else if (StrnCmp (L
"-", CurrentArg
, 1) == 0) {
1039 // Unrecognized option
1040 ShellPrintHiiEx(-1, -1, NULL
,
1041 STRING_TOKEN (STR_GEN_PROBLEM
),
1042 ShellInfoObject
.HiiHandle
,
1045 return EFI_INVALID_PARAMETER
;
1048 // First argument should be Shell.efi image name
1054 ShellInfoObject
.ShellInitSettings
.FileName
= AllocateCopyPool(StrSize(CurrentArg
), CurrentArg
);
1055 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
1056 return (EFI_OUT_OF_RESOURCES
);
1059 // We found `file-name`.
1061 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
1064 // Add `file-name-options`
1065 for (Size
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
1066 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
1067 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
1071 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1072 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
1073 return (EFI_OUT_OF_RESOURCES
);
1075 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
1077 gEfiShellParametersProtocol
->Argv
[LoopVar
],
1079 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
1080 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
1081 return (EFI_OUT_OF_RESOURCES
);
1087 // "-nointerrupt" overrides "-delay"
1088 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
1089 ShellInfoObject
.ShellInitSettings
.Delay
= 0;
1096 Handles all interaction with the default startup script.
1098 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
1100 @param ImagePath the path to the image for shell. first place to look for the startup script
1101 @param FilePath the path to the file for shell. second place to look for the startup script.
1103 @retval EFI_SUCCESS the variable is initialized.
1108 IN EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
1109 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
1113 EFI_STATUS CalleeStatus
;
1116 SHELL_FILE_HANDLE FileHandle
;
1117 EFI_DEVICE_PATH_PROTOCOL
*NewPath
;
1118 EFI_DEVICE_PATH_PROTOCOL
*NamePath
;
1119 CHAR16
*FileStringPath
;
1122 CONST CHAR16
*MapName
;
1124 Key
.UnicodeChar
= CHAR_NULL
;
1128 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
1130 // launch something else instead
1132 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
1133 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1134 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
1136 FileStringPath
= AllocateZeroPool(NewSize
);
1137 if (FileStringPath
== NULL
) {
1138 return (EFI_OUT_OF_RESOURCES
);
1140 StrCpyS(FileStringPath
, NewSize
/sizeof(CHAR16
), ShellInfoObject
.ShellInitSettings
.FileName
);
1141 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1142 StrnCatS(FileStringPath
, NewSize
/sizeof(CHAR16
), L
" ", NewSize
/sizeof(CHAR16
) - StrLen(FileStringPath
) -1);
1143 StrnCatS(FileStringPath
, NewSize
/sizeof(CHAR16
), ShellInfoObject
.ShellInitSettings
.FileOptions
, NewSize
/sizeof(CHAR16
) - StrLen(FileStringPath
) -1);
1145 Status
= RunShellCommand(FileStringPath
, &CalleeStatus
);
1146 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
== TRUE
) {
1147 ShellCommandRegisterExit(gEfiShellProtocol
->BatchIsActive(), (UINT64
)CalleeStatus
);
1149 FreePool(FileStringPath
);
1155 // for shell level 0 we do no scripts
1156 // Without the Startup bit overriding we allow for nostartup to prevent scripts
1158 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
1159 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
1161 return (EFI_SUCCESS
);
1164 gST
->ConOut
->EnableCursor(gST
->ConOut
, FALSE
);
1166 // print out our warning and see if they press a key
1168 for ( Status
= EFI_UNSUPPORTED
, Delay
= ShellInfoObject
.ShellInitSettings
.Delay
1169 ; Delay
!= 0 && EFI_ERROR(Status
)
1172 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
);
1173 gBS
->Stall (1000000);
1174 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
1175 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
1178 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
1179 gST
->ConOut
->EnableCursor(gST
->ConOut
, TRUE
);
1184 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
1185 return (EFI_SUCCESS
);
1189 // Try the first location (must be file system)
1191 MapName
= ShellInfoObject
.NewEfiShellProtocol
->GetMapFromDevicePath(&ImagePath
);
1192 if (MapName
!= NULL
) {
1193 FileStringPath
= NULL
;
1195 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, MapName
, 0);
1196 if (FileStringPath
== NULL
) {
1197 Status
= EFI_OUT_OF_RESOURCES
;
1199 TempSpot
= StrStr(FileStringPath
, L
";");
1200 if (TempSpot
!= NULL
) {
1201 *TempSpot
= CHAR_NULL
;
1203 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, ((FILEPATH_DEVICE_PATH
*)FilePath
)->PathName
, 0);
1204 PathRemoveLastItem(FileStringPath
);
1205 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, mStartupScript
, 0);
1206 Status
= ShellInfoObject
.NewEfiShellProtocol
->OpenFileByName(FileStringPath
, &FileHandle
, EFI_FILE_MODE_READ
);
1207 FreePool(FileStringPath
);
1210 if (EFI_ERROR(Status
)) {
1211 NamePath
= FileDevicePath (NULL
, mStartupScript
);
1212 NewPath
= AppendDevicePathNode (ImagePath
, NamePath
);
1218 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
1222 // If we got a file, run it
1224 if (!EFI_ERROR(Status
) && FileHandle
!= NULL
) {
1225 Status
= RunScriptFile (mStartupScript
, FileHandle
, L
"", ShellInfoObject
.NewShellParametersProtocol
);
1226 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
1228 FileStringPath
= ShellFindFilePath(mStartupScript
);
1229 if (FileStringPath
== NULL
) {
1231 // we return success since we don't need to have a startup script
1233 Status
= EFI_SUCCESS
;
1234 ASSERT(FileHandle
== NULL
);
1236 Status
= RunScriptFile(FileStringPath
, NULL
, L
"", ShellInfoObject
.NewShellParametersProtocol
);
1237 FreePool(FileStringPath
);
1246 Function to perform the shell prompt looping. It will do a single prompt,
1247 dispatch the result, and then return. It is expected that the caller will
1248 call this function in a loop many times.
1251 @retval RETURN_ABORTED
1262 CONST CHAR16
*CurDir
;
1265 LIST_ENTRY OldBufferList
;
1270 // Get screen setting to decide size of the command line buffer
1272 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
1273 BufferSize
= Column
* Row
* sizeof (CHAR16
);
1274 CmdLine
= AllocateZeroPool (BufferSize
);
1275 if (CmdLine
== NULL
) {
1276 return EFI_OUT_OF_RESOURCES
;
1279 SaveBufferList(&OldBufferList
);
1280 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1285 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
1287 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1288 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1290 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1294 // Read a line from the console
1296 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
1299 // Null terminate the string and parse it
1301 if (!EFI_ERROR (Status
)) {
1302 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
1303 Status
= RunCommand(CmdLine
);
1307 // Done with this command
1309 RestoreBufferList(&OldBufferList
);
1315 Add a buffer to the Buffer To Free List for safely returning buffers to other
1316 places without risking letting them modify internal shell information.
1318 @param Buffer Something to pass to FreePool when the shell is exiting.
1322 AddBufferToFreeList (
1326 BUFFER_LIST
*BufferListEntry
;
1328 if (Buffer
== NULL
) {
1332 BufferListEntry
= AllocateZeroPool (sizeof (BUFFER_LIST
));
1333 if (BufferListEntry
== NULL
) {
1337 BufferListEntry
->Buffer
= Buffer
;
1338 InsertTailList (&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
1344 Create a new buffer list and stores the old one to OldBufferList
1346 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
1350 OUT LIST_ENTRY
*OldBufferList
1353 CopyMem (OldBufferList
, &ShellInfoObject
.BufferToFreeList
.Link
, sizeof (LIST_ENTRY
));
1354 InitializeListHead (&ShellInfoObject
.BufferToFreeList
.Link
);
1358 Restore previous nodes into BufferToFreeList .
1360 @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
1364 IN OUT LIST_ENTRY
*OldBufferList
1367 FreeBufferList (&ShellInfoObject
.BufferToFreeList
);
1368 CopyMem (&ShellInfoObject
.BufferToFreeList
.Link
, OldBufferList
, sizeof (LIST_ENTRY
));
1373 Add a buffer to the Line History List
1375 @param Buffer The line buffer to add.
1379 AddLineToCommandHistory(
1380 IN CONST CHAR16
*Buffer
1384 BUFFER_LIST
*Walker
;
1385 UINT16 MaxHistoryCmdCount
;
1389 MaxHistoryCmdCount
= PcdGet16(PcdShellMaxHistoryCommandCount
);
1391 if (MaxHistoryCmdCount
== 0) {
1396 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1401 Node
->Buffer
= AllocateCopyPool (StrSize (Buffer
), Buffer
);
1402 if (Node
->Buffer
== NULL
) {
1407 for ( Walker
= (BUFFER_LIST
*)GetFirstNode(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)
1408 ; !IsNull(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Walker
->Link
)
1409 ; Walker
= (BUFFER_LIST
*)GetNextNode(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Walker
->Link
)
1413 if (Count
< MaxHistoryCmdCount
){
1414 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1416 Walker
= (BUFFER_LIST
*)GetFirstNode(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
1417 RemoveEntryList(&Walker
->Link
);
1418 if (Walker
->Buffer
!= NULL
) {
1419 FreePool(Walker
->Buffer
);
1422 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1427 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1428 with the correct command name.
1430 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1431 command name if it was an alias. If it was not
1432 an alias it will be unchanged. This function may
1433 change the buffer to fit the command name.
1435 @retval EFI_SUCCESS The name was changed.
1436 @retval EFI_SUCCESS The name was not an alias.
1437 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1442 IN OUT CHAR16
**CommandString
1445 CONST CHAR16
*NewString
;
1447 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
1448 if (NewString
== NULL
) {
1449 return (EFI_SUCCESS
);
1451 FreePool(*CommandString
);
1452 *CommandString
= AllocateCopyPool(StrSize(NewString
), NewString
);
1453 if (*CommandString
== NULL
) {
1454 return (EFI_OUT_OF_RESOURCES
);
1456 return (EFI_SUCCESS
);
1460 This function will eliminate unreplaced (and therefore non-found) environment variables.
1462 @param[in,out] CmdLine The command line to update.
1466 StripUnreplacedEnvironmentVariables(
1467 IN OUT CHAR16
*CmdLine
1470 CHAR16
*FirstPercent
;
1472 CHAR16
*SecondPercent
;
1473 CHAR16
*SecondQuote
;
1474 CHAR16
*CurrentLocator
;
1476 for (CurrentLocator
= CmdLine
; CurrentLocator
!= NULL
; ) {
1477 FirstQuote
= FindNextInstance(CurrentLocator
, L
"\"", TRUE
);
1478 FirstPercent
= FindNextInstance(CurrentLocator
, L
"%", TRUE
);
1479 SecondPercent
= FirstPercent
!=NULL
?FindNextInstance(FirstPercent
+1, L
"%", TRUE
):NULL
;
1480 if (FirstPercent
== NULL
|| SecondPercent
== NULL
) {
1482 // If we ever don't have 2 % we are done.
1487 if (FirstQuote
!= NULL
&& FirstQuote
< FirstPercent
) {
1488 SecondQuote
= FindNextInstance(FirstQuote
+1, L
"\"", TRUE
);
1490 // Quote is first found
1493 if (SecondQuote
< FirstPercent
) {
1495 // restart after the pair of "
1497 CurrentLocator
= SecondQuote
+ 1;
1498 } else /* FirstPercent < SecondQuote */{
1500 // Restart on the first percent
1502 CurrentLocator
= FirstPercent
;
1507 if (FirstQuote
== NULL
|| SecondPercent
< FirstQuote
) {
1508 if (IsValidEnvironmentVariableName(FirstPercent
, SecondPercent
)) {
1510 // We need to remove from FirstPercent to SecondPercent
1512 CopyMem(FirstPercent
, SecondPercent
+ 1, StrSize(SecondPercent
+ 1));
1514 // don't need to update the locator. both % characters are gone.
1517 CurrentLocator
= SecondPercent
+ 1;
1521 CurrentLocator
= FirstQuote
;
1523 return (EFI_SUCCESS
);
1527 Function allocates a new command line and replaces all instances of environment
1528 variable names that are correctly preset to their values.
1530 If the return value is not NULL the memory must be caller freed.
1532 @param[in] OriginalCommandLine The original command line
1534 @retval NULL An error occurred.
1535 @return The new command line with no environment variables present.
1539 ShellConvertVariables (
1540 IN CONST CHAR16
*OriginalCommandLine
1543 CONST CHAR16
*MasterEnvList
;
1545 CHAR16
*NewCommandLine1
;
1546 CHAR16
*NewCommandLine2
;
1550 SCRIPT_FILE
*CurrentScriptFile
;
1551 ALIAS_LIST
*AliasListNode
;
1553 ASSERT(OriginalCommandLine
!= NULL
);
1556 NewSize
= StrSize(OriginalCommandLine
);
1557 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
1560 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1563 // calculate the size required for the post-conversion string...
1565 if (CurrentScriptFile
!= NULL
) {
1566 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1567 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1568 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1570 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
1572 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
1575 // we need a preceding and if there is space no ^ preceding (if no space ignore)
1577 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
1578 NewSize
+= StrSize(AliasListNode
->CommandString
);
1584 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1585 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1586 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1588 if (StrSize(MasterEnvList
) > ItemSize
) {
1589 ItemSize
= StrSize(MasterEnvList
);
1591 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
1593 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
1596 // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)
1598 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
1599 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
1600 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
1606 // now do the replacements...
1608 NewCommandLine1
= AllocateCopyPool(NewSize
, OriginalCommandLine
);
1609 NewCommandLine2
= AllocateZeroPool(NewSize
);
1610 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
1611 if (NewCommandLine1
== NULL
|| NewCommandLine2
== NULL
|| ItemTemp
== NULL
) {
1612 SHELL_FREE_NON_NULL(NewCommandLine1
);
1613 SHELL_FREE_NON_NULL(NewCommandLine2
);
1614 SHELL_FREE_NON_NULL(ItemTemp
);
1617 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1618 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
1619 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1622 ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
)),
1626 ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
)),
1630 ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
)),
1633 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1634 StrCpyS(NewCommandLine1
, NewSize
/sizeof(CHAR16
), NewCommandLine2
);
1636 if (CurrentScriptFile
!= NULL
) {
1637 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1638 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1639 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1641 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1642 StrCpyS(NewCommandLine1
, NewSize
/sizeof(CHAR16
), NewCommandLine2
);
1647 // Remove non-existent environment variables
1649 StripUnreplacedEnvironmentVariables(NewCommandLine1
);
1652 // Now cleanup any straggler intentionally ignored "%" characters
1654 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, L
"^%", L
"%", TRUE
, FALSE
);
1655 StrCpyS(NewCommandLine1
, NewSize
/sizeof(CHAR16
), NewCommandLine2
);
1657 FreePool(NewCommandLine2
);
1660 return (NewCommandLine1
);
1664 Internal function to run a command line with pipe usage.
1666 @param[in] CmdLine The pointer to the command line.
1667 @param[in] StdIn The pointer to the Standard input.
1668 @param[in] StdOut The pointer to the Standard output.
1670 @retval EFI_SUCCESS The split command is executed successfully.
1671 @retval other Some error occurs when executing the split command.
1676 IN CONST CHAR16
*CmdLine
,
1677 IN SHELL_FILE_HANDLE
*StdIn
,
1678 IN SHELL_FILE_HANDLE
*StdOut
1682 CHAR16
*NextCommandLine
;
1683 CHAR16
*OurCommandLine
;
1687 SHELL_FILE_HANDLE
*TempFileHandle
;
1690 ASSERT(StdOut
== NULL
);
1692 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1694 Status
= EFI_SUCCESS
;
1695 NextCommandLine
= NULL
;
1696 OurCommandLine
= NULL
;
1700 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1701 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1703 if (NextCommandLine
== NULL
|| OurCommandLine
== NULL
) {
1704 SHELL_FREE_NON_NULL(OurCommandLine
);
1705 SHELL_FREE_NON_NULL(NextCommandLine
);
1706 return (EFI_OUT_OF_RESOURCES
);
1707 } else if (StrStr(OurCommandLine
, L
"|") != NULL
|| Size1
== 0 || Size2
== 0) {
1708 SHELL_FREE_NON_NULL(OurCommandLine
);
1709 SHELL_FREE_NON_NULL(NextCommandLine
);
1710 return (EFI_INVALID_PARAMETER
);
1711 } else if (NextCommandLine
[0] == L
'a' &&
1712 (NextCommandLine
[1] == L
' ' || NextCommandLine
[1] == CHAR_NULL
)
1714 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1715 while (NextCommandLine
[0] == L
' ') {
1716 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1718 if (NextCommandLine
[0] == CHAR_NULL
) {
1719 SHELL_FREE_NON_NULL(OurCommandLine
);
1720 SHELL_FREE_NON_NULL(NextCommandLine
);
1721 return (EFI_INVALID_PARAMETER
);
1730 // make a SPLIT_LIST item and add to list
1732 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1733 if (Split
== NULL
) {
1734 return EFI_OUT_OF_RESOURCES
;
1736 Split
->SplitStdIn
= StdIn
;
1737 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1738 ASSERT(Split
->SplitStdOut
!= NULL
);
1739 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1741 Status
= RunCommand(OurCommandLine
);
1744 // move the output from the first to the in to the second.
1746 TempFileHandle
= Split
->SplitStdOut
;
1747 if (Split
->SplitStdIn
== StdIn
) {
1748 Split
->SplitStdOut
= NULL
;
1750 Split
->SplitStdOut
= Split
->SplitStdIn
;
1752 Split
->SplitStdIn
= TempFileHandle
;
1753 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
), 0);
1755 if (!EFI_ERROR(Status
)) {
1756 Status
= RunCommand(NextCommandLine
);
1760 // remove the top level from the ScriptList
1762 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1763 RemoveEntryList(&Split
->Link
);
1766 // Note that the original StdIn is now the StdOut...
1768 if (Split
->SplitStdOut
!= NULL
) {
1769 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdOut
));
1771 if (Split
->SplitStdIn
!= NULL
) {
1772 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
));
1773 FreePool (Split
->SplitStdIn
);
1777 FreePool(NextCommandLine
);
1778 FreePool(OurCommandLine
);
1784 Take the original command line, substitute any variables, free
1785 the original string, return the modified copy.
1787 @param[in] CmdLine pointer to the command line to update.
1789 @retval EFI_SUCCESS the function was successful.
1790 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1794 ShellSubstituteVariables(
1799 NewCmdLine
= ShellConvertVariables(*CmdLine
);
1800 SHELL_FREE_NON_NULL(*CmdLine
);
1801 if (NewCmdLine
== NULL
) {
1802 return (EFI_OUT_OF_RESOURCES
);
1804 *CmdLine
= NewCmdLine
;
1805 return (EFI_SUCCESS
);
1809 Take the original command line, substitute any alias in the first group of space delimited characters, free
1810 the original string, return the modified copy.
1812 @param[in] CmdLine pointer to the command line to update.
1814 @retval EFI_SUCCESS the function was successful.
1815 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1819 ShellSubstituteAliases(
1824 CHAR16
*CommandName
;
1826 UINTN PostAliasSize
;
1827 ASSERT(CmdLine
!= NULL
);
1828 ASSERT(*CmdLine
!= NULL
);
1832 if (StrStr((*CmdLine
), L
" ") == NULL
){
1833 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), 0);
1835 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), StrStr((*CmdLine
), L
" ") - (*CmdLine
));
1839 // This cannot happen 'inline' since the CmdLine can need extra space.
1842 if (!ShellCommandIsCommandOnList(CommandName
)) {
1844 // Convert via alias
1846 Status
= ShellConvertAlias(&CommandName
);
1847 if (EFI_ERROR(Status
)){
1851 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, CommandName
, 0);
1852 if (NewCmdLine
== NULL
) {
1853 SHELL_FREE_NON_NULL(CommandName
);
1854 SHELL_FREE_NON_NULL(*CmdLine
);
1855 return (EFI_OUT_OF_RESOURCES
);
1857 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, StrStr((*CmdLine
), L
" "), 0);
1858 if (NewCmdLine
== NULL
) {
1859 SHELL_FREE_NON_NULL(CommandName
);
1860 SHELL_FREE_NON_NULL(*CmdLine
);
1861 return (EFI_OUT_OF_RESOURCES
);
1864 NewCmdLine
= StrnCatGrow(&NewCmdLine
, NULL
, (*CmdLine
), 0);
1867 SHELL_FREE_NON_NULL(*CmdLine
);
1868 SHELL_FREE_NON_NULL(CommandName
);
1871 // re-assign the passed in double pointer to point to our newly allocated buffer
1873 *CmdLine
= NewCmdLine
;
1875 return (EFI_SUCCESS
);
1879 Takes the Argv[0] part of the command line and determine the meaning of it.
1881 @param[in] CmdName pointer to the command line to update.
1883 @retval Internal_Command The name is an internal command.
1884 @retval File_Sys_Change the name is a file system change.
1885 @retval Script_File_Name the name is a NSH script file.
1886 @retval Unknown_Invalid the name is unknown.
1887 @retval Efi_Application the name is an application (.EFI).
1889 SHELL_OPERATION_TYPES
1892 IN CONST CHAR16
*CmdName
1895 CHAR16
* FileWithPath
;
1896 CONST CHAR16
* TempLocation
;
1897 CONST CHAR16
* TempLocation2
;
1899 FileWithPath
= NULL
;
1901 // test for an internal command.
1903 if (ShellCommandIsCommandOnList(CmdName
)) {
1904 return (Internal_Command
);
1908 // Test for file system change request. anything ending with first : and cant have spaces.
1910 if (CmdName
[(StrLen(CmdName
)-1)] == L
':') {
1911 if ( StrStr(CmdName
, L
" ") != NULL
1912 || StrLen(StrStr(CmdName
, L
":")) > 1
1914 return (Unknown_Invalid
);
1916 return (File_Sys_Change
);
1922 if ((FileWithPath
= ShellFindFilePathEx(CmdName
, mExecutableExtensions
)) != NULL
) {
1924 // See if that file has a script file extension
1926 if (StrLen(FileWithPath
) > 4) {
1927 TempLocation
= FileWithPath
+StrLen(FileWithPath
)-4;
1928 TempLocation2
= mScriptExtension
;
1929 if (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0) {
1930 SHELL_FREE_NON_NULL(FileWithPath
);
1931 return (Script_File_Name
);
1936 // Was a file, but not a script. we treat this as an application.
1938 SHELL_FREE_NON_NULL(FileWithPath
);
1939 return (Efi_Application
);
1942 SHELL_FREE_NON_NULL(FileWithPath
);
1944 // No clue what this is... return invalid flag...
1946 return (Unknown_Invalid
);
1950 Determine if the first item in a command line is valid.
1952 @param[in] CmdLine The command line to parse.
1954 @retval EFI_SUCCESS The item is valid.
1955 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1956 @retval EFI_NOT_FOUND The operation type is unknown or invalid.
1961 IN CONST CHAR16
*CmdLine
1965 CHAR16
*FirstParameter
;
1971 Temp
= StrnCatGrow(&Temp
, NULL
, CmdLine
, 0);
1973 return (EFI_OUT_OF_RESOURCES
);
1976 FirstParameter
= StrStr(Temp
, L
"|");
1977 if (FirstParameter
!= NULL
) {
1978 *FirstParameter
= CHAR_NULL
;
1981 FirstParameter
= NULL
;
1984 // Process the command line
1986 Status
= ProcessCommandLineToFinal(&Temp
);
1988 if (!EFI_ERROR(Status
)) {
1989 FirstParameter
= AllocateZeroPool(StrSize(CmdLine
));
1990 if (FirstParameter
== NULL
) {
1991 SHELL_FREE_NON_NULL(Temp
);
1992 return (EFI_OUT_OF_RESOURCES
);
1994 TempWalker
= (CHAR16
*)Temp
;
1995 if (!EFI_ERROR(GetNextParameter(&TempWalker
, &FirstParameter
, StrSize(CmdLine
), TRUE
))) {
1996 if (GetOperationType(FirstParameter
) == Unknown_Invalid
) {
1997 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
1998 SetLastError(SHELL_NOT_FOUND
);
1999 Status
= EFI_NOT_FOUND
;
2004 SHELL_FREE_NON_NULL(Temp
);
2005 SHELL_FREE_NON_NULL(FirstParameter
);
2010 Determine if a command line contains with a split contains only valid commands.
2012 @param[in] CmdLine The command line to parse.
2014 @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.
2015 @retval EFI_ABORTED CmdLine has at least one invalid command or application.
2020 IN CONST CHAR16
*CmdLine
2023 CONST CHAR16
*TempSpot
;
2027 // If this was the only item, then get out
2029 if (!ContainsSplit(CmdLine
)) {
2030 return (EFI_SUCCESS
);
2034 // Verify up to the pipe or end character
2036 Status
= IsValidSplit(CmdLine
);
2037 if (EFI_ERROR(Status
)) {
2042 // recurse to verify the next item
2044 TempSpot
= FindFirstCharacter(CmdLine
, L
"|", L
'^') + 1;
2045 if (*TempSpot
== L
'a' &&
2046 (*(TempSpot
+ 1) == L
' ' || *(TempSpot
+ 1) == CHAR_NULL
)
2048 // If it's an ASCII pipe '|a'
2052 return (VerifySplit(TempSpot
));
2056 Process a split based operation.
2058 @param[in] CmdLine pointer to the command line to process
2060 @retval EFI_SUCCESS The operation was successful
2061 @return an error occurred.
2065 ProcessNewSplitCommandLine(
2066 IN CONST CHAR16
*CmdLine
2072 Status
= VerifySplit(CmdLine
);
2073 if (EFI_ERROR(Status
)) {
2080 // are we in an existing split???
2082 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
2083 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
2086 if (Split
== NULL
) {
2087 Status
= RunSplitCommand(CmdLine
, NULL
, NULL
);
2089 Status
= RunSplitCommand(CmdLine
, Split
->SplitStdIn
, Split
->SplitStdOut
);
2091 if (EFI_ERROR(Status
)) {
2092 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_SPLIT
), ShellInfoObject
.HiiHandle
, CmdLine
);
2098 Handle a request to change the current file system.
2100 @param[in] CmdLine The passed in command line.
2102 @retval EFI_SUCCESS The operation was successful.
2107 IN CONST CHAR16
*CmdLine
2111 Status
= EFI_SUCCESS
;
2114 // make sure we are the right operation
2116 ASSERT(CmdLine
[(StrLen(CmdLine
)-1)] == L
':' && StrStr(CmdLine
, L
" ") == NULL
);
2119 // Call the protocol API to do the work
2121 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, CmdLine
);
2124 // Report any errors
2126 if (EFI_ERROR(Status
)) {
2127 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, CmdLine
);
2134 Reprocess the command line to direct all -? to the help command.
2136 if found, will add "help" as argv[0], and move the rest later.
2138 @param[in,out] CmdLine pointer to the command line to update
2143 IN OUT CHAR16
**CmdLine
2146 CHAR16
*CurrentParameter
;
2148 CHAR16
*NewCommandLine
;
2150 UINTN NewCmdLineSize
;
2152 Status
= EFI_SUCCESS
;
2154 CurrentParameter
= AllocateZeroPool(StrSize(*CmdLine
));
2155 if (CurrentParameter
== NULL
) {
2156 return (EFI_OUT_OF_RESOURCES
);
2160 while(Walker
!= NULL
&& *Walker
!= CHAR_NULL
) {
2161 if (!EFI_ERROR(GetNextParameter(&Walker
, &CurrentParameter
, StrSize(*CmdLine
), TRUE
))) {
2162 if (StrStr(CurrentParameter
, L
"-?") == CurrentParameter
) {
2163 CurrentParameter
[0] = L
' ';
2164 CurrentParameter
[1] = L
' ';
2165 NewCmdLineSize
= StrSize(L
"help ") + StrSize(*CmdLine
);
2166 NewCommandLine
= AllocateZeroPool(NewCmdLineSize
);
2167 if (NewCommandLine
== NULL
) {
2168 Status
= EFI_OUT_OF_RESOURCES
;
2173 // We know the space is sufficient since we just calculated it.
2175 StrnCpyS(NewCommandLine
, NewCmdLineSize
/sizeof(CHAR16
), L
"help ", 5);
2176 StrnCatS(NewCommandLine
, NewCmdLineSize
/sizeof(CHAR16
), *CmdLine
, StrLen(*CmdLine
));
2177 SHELL_FREE_NON_NULL(*CmdLine
);
2178 *CmdLine
= NewCommandLine
;
2184 SHELL_FREE_NON_NULL(CurrentParameter
);
2190 Function to update the shell variable "lasterror".
2192 @param[in] ErrorCode the error code to put into lasterror.
2197 IN CONST SHELL_STATUS ErrorCode
2200 CHAR16 LeString
[19];
2201 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
2202 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ErrorCode
);
2204 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", ErrorCode
);
2206 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
2207 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
2209 return (EFI_SUCCESS
);
2213 Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.
2215 @param[in,out] CmdLine pointer to the command line to update
2217 @retval EFI_SUCCESS The operation was successful
2218 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
2219 @return some other error occurred
2223 ProcessCommandLineToFinal(
2224 IN OUT CHAR16
**CmdLine
2228 TrimSpaces(CmdLine
);
2230 Status
= ShellSubstituteAliases(CmdLine
);
2231 if (EFI_ERROR(Status
)) {
2235 TrimSpaces(CmdLine
);
2237 Status
= ShellSubstituteVariables(CmdLine
);
2238 if (EFI_ERROR(Status
)) {
2241 ASSERT (*CmdLine
!= NULL
);
2243 TrimSpaces(CmdLine
);
2246 // update for help parsing
2248 if (StrStr(*CmdLine
, L
"?") != NULL
) {
2250 // This may do nothing if the ? does not indicate help.
2251 // Save all the details for in the API below.
2253 Status
= DoHelpUpdate(CmdLine
);
2256 TrimSpaces(CmdLine
);
2258 return (EFI_SUCCESS
);
2262 Run an internal shell command.
2264 This API will update the shell's environment since these commands are libraries.
2266 @param[in] CmdLine the command line to run.
2267 @param[in] FirstParameter the first parameter on the command line
2268 @param[in] ParamProtocol the shell parameters protocol pointer
2269 @param[out] CommandStatus the status from the command line.
2271 @retval EFI_SUCCESS The command was completed.
2272 @retval EFI_ABORTED The command's operation was aborted.
2277 IN CONST CHAR16
*CmdLine
,
2278 IN CHAR16
*FirstParameter
,
2279 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2280 OUT EFI_STATUS
*CommandStatus
2286 SHELL_STATUS CommandReturnedStatus
;
2291 NewCmdLine
= AllocateCopyPool (StrSize (CmdLine
), CmdLine
);
2292 if (NewCmdLine
== NULL
) {
2293 return EFI_OUT_OF_RESOURCES
;
2296 for (Walker
= NewCmdLine
; Walker
!= NULL
&& *Walker
!= CHAR_NULL
; Walker
++) {
2297 if (*Walker
== L
'^' && *(Walker
+1) == L
'#') {
2298 CopyMem(Walker
, Walker
+1, StrSize(Walker
) - sizeof(Walker
[0]));
2303 // get the argc and argv updated for internal commands
2305 Status
= UpdateArgcArgv(ParamProtocol
, NewCmdLine
, Internal_Command
, &Argv
, &Argc
);
2306 if (!EFI_ERROR(Status
)) {
2308 // Run the internal command.
2310 Status
= ShellCommandRunCommandHandler(FirstParameter
, &CommandReturnedStatus
, &LastError
);
2312 if (!EFI_ERROR(Status
)) {
2313 if (CommandStatus
!= NULL
) {
2314 if (CommandReturnedStatus
!= SHELL_SUCCESS
) {
2315 *CommandStatus
= (EFI_STATUS
)(CommandReturnedStatus
| MAX_BIT
);
2317 *CommandStatus
= EFI_SUCCESS
;
2322 // Update last error status.
2323 // some commands do not update last error.
2326 SetLastError(CommandReturnedStatus
);
2330 // Pass thru the exitcode from the app.
2332 if (ShellCommandGetExit()) {
2334 // An Exit was requested ("exit" command), pass its value up.
2336 Status
= CommandReturnedStatus
;
2337 } else if (CommandReturnedStatus
!= SHELL_SUCCESS
&& IsScriptOnlyCommand(FirstParameter
)) {
2339 // Always abort when a script only command fails for any reason
2341 Status
= EFI_ABORTED
;
2342 } else if (ShellCommandGetCurrentScriptFile() != NULL
&& CommandReturnedStatus
== SHELL_ABORTED
) {
2344 // Abort when in a script and a command aborted
2346 Status
= EFI_ABORTED
;
2352 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
2353 // This is safe even if the update API failed. In this case, it may be a no-op.
2355 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);
2358 // If a script is running and the command is not a script only command, then
2359 // change return value to success so the script won't halt (unless aborted).
2361 // Script only commands have to be able halt the script since the script will
2362 // not operate if they are failing.
2364 if ( ShellCommandGetCurrentScriptFile() != NULL
2365 && !IsScriptOnlyCommand(FirstParameter
)
2366 && Status
!= EFI_ABORTED
2368 Status
= EFI_SUCCESS
;
2371 FreePool (NewCmdLine
);
2376 Function to run the command or file.
2378 @param[in] Type the type of operation being run.
2379 @param[in] CmdLine the command line to run.
2380 @param[in] FirstParameter the first parameter on the command line
2381 @param[in] ParamProtocol the shell parameters protocol pointer
2382 @param[out] CommandStatus the status from the command line.
2384 @retval EFI_SUCCESS The command was completed.
2385 @retval EFI_ABORTED The command's operation was aborted.
2390 IN SHELL_OPERATION_TYPES Type
,
2391 IN CONST CHAR16
*CmdLine
,
2392 IN CHAR16
*FirstParameter
,
2393 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2394 OUT EFI_STATUS
*CommandStatus
2398 EFI_STATUS StartStatus
;
2399 CHAR16
*CommandWithPath
;
2400 EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
2401 SHELL_STATUS CalleeExitStatus
;
2403 Status
= EFI_SUCCESS
;
2404 CommandWithPath
= NULL
;
2406 CalleeExitStatus
= SHELL_INVALID_PARAMETER
;
2409 case Internal_Command
:
2410 Status
= RunInternalCommand(CmdLine
, FirstParameter
, ParamProtocol
, CommandStatus
);
2412 case Script_File_Name
:
2413 case Efi_Application
:
2415 // Process a fully qualified path
2417 if (StrStr(FirstParameter
, L
":") != NULL
) {
2418 ASSERT (CommandWithPath
== NULL
);
2419 if (ShellIsFile(FirstParameter
) == EFI_SUCCESS
) {
2420 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, FirstParameter
, 0);
2425 // Process a relative path and also check in the path environment variable
2427 if (CommandWithPath
== NULL
) {
2428 CommandWithPath
= ShellFindFilePathEx(FirstParameter
, mExecutableExtensions
);
2432 // This should be impossible now.
2434 ASSERT(CommandWithPath
!= NULL
);
2437 // Make sure that path is not just a directory (or not found)
2439 if (!EFI_ERROR(ShellIsDirectory(CommandWithPath
))) {
2440 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2441 SetLastError(SHELL_NOT_FOUND
);
2444 case Script_File_Name
:
2445 Status
= RunScriptFile (CommandWithPath
, NULL
, CmdLine
, ParamProtocol
);
2447 case Efi_Application
:
2449 // Get the device path of the application image
2451 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
2452 if (DevPath
== NULL
){
2453 Status
= EFI_OUT_OF_RESOURCES
;
2458 // Execute the device path
2460 Status
= InternalShellExecuteDevicePath(
2468 SHELL_FREE_NON_NULL(DevPath
);
2470 if(EFI_ERROR (Status
)) {
2471 CalleeExitStatus
= (SHELL_STATUS
) (Status
& (~MAX_BIT
));
2473 CalleeExitStatus
= (SHELL_STATUS
) StartStatus
;
2476 if (CommandStatus
!= NULL
) {
2477 *CommandStatus
= CalleeExitStatus
;
2481 // Update last error status.
2483 // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
2484 SetLastError(CalleeExitStatus
);
2500 SHELL_FREE_NON_NULL(CommandWithPath
);
2506 Function to setup StdIn, StdErr, StdOut, and then run the command or file.
2508 @param[in] Type the type of operation being run.
2509 @param[in] CmdLine the command line to run.
2510 @param[in] FirstParameter the first parameter on the command line.
2511 @param[in] ParamProtocol the shell parameters protocol pointer
2512 @param[out] CommandStatus the status from the command line.
2514 @retval EFI_SUCCESS The command was completed.
2515 @retval EFI_ABORTED The command's operation was aborted.
2519 SetupAndRunCommandOrFile(
2520 IN SHELL_OPERATION_TYPES Type
,
2522 IN CHAR16
*FirstParameter
,
2523 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2524 OUT EFI_STATUS
*CommandStatus
2528 SHELL_FILE_HANDLE OriginalStdIn
;
2529 SHELL_FILE_HANDLE OriginalStdOut
;
2530 SHELL_FILE_HANDLE OriginalStdErr
;
2531 SYSTEM_TABLE_INFO OriginalSystemTableInfo
;
2532 CONST SCRIPT_FILE
*ConstScriptFile
;
2535 // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
2537 Status
= UpdateStdInStdOutStdErr(ParamProtocol
, CmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2540 // The StdIn, StdOut, and StdErr are set up.
2541 // Now run the command, script, or application
2543 if (!EFI_ERROR(Status
)) {
2544 TrimSpaces(&CmdLine
);
2545 Status
= RunCommandOrFile(Type
, CmdLine
, FirstParameter
, ParamProtocol
, CommandStatus
);
2551 if (EFI_ERROR(Status
)) {
2552 ConstScriptFile
= ShellCommandGetCurrentScriptFile();
2553 if (ConstScriptFile
== NULL
|| ConstScriptFile
->CurrentCommand
== NULL
) {
2554 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
));
2556 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR_SCRIPT
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
), ConstScriptFile
->CurrentCommand
->Line
);
2561 // put back the original StdIn, StdOut, and StdErr
2563 RestoreStdInStdOutStdErr(ParamProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2569 Function will process and run a command line.
2571 This will determine if the command line represents an internal shell
2572 command or dispatch an external application.
2574 @param[in] CmdLine The command line to parse.
2575 @param[out] CommandStatus The status from the command line.
2577 @retval EFI_SUCCESS The command was completed.
2578 @retval EFI_ABORTED The command's operation was aborted.
2583 IN CONST CHAR16
*CmdLine
,
2584 OUT EFI_STATUS
*CommandStatus
2588 CHAR16
*CleanOriginal
;
2589 CHAR16
*FirstParameter
;
2591 SHELL_OPERATION_TYPES Type
;
2593 ASSERT(CmdLine
!= NULL
);
2594 if (StrLen(CmdLine
) == 0) {
2595 return (EFI_SUCCESS
);
2598 Status
= EFI_SUCCESS
;
2599 CleanOriginal
= NULL
;
2601 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
2602 if (CleanOriginal
== NULL
) {
2603 return (EFI_OUT_OF_RESOURCES
);
2606 TrimSpaces(&CleanOriginal
);
2609 // NULL out comments (leveraged from RunScriptFileHandle() ).
2610 // The # character on a line is used to denote that all characters on the same line
2611 // and to the right of the # are to be ignored by the shell.
2612 // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.
2614 for (TempWalker
= CleanOriginal
; TempWalker
!= NULL
&& *TempWalker
!= CHAR_NULL
; TempWalker
++) {
2615 if (*TempWalker
== L
'^') {
2616 if (*(TempWalker
+ 1) == L
'#') {
2619 } else if (*TempWalker
== L
'#') {
2620 *TempWalker
= CHAR_NULL
;
2624 TrimSpaces(&CleanOriginal
);
2627 // Handle case that passed in command line is just 1 or more " " characters.
2629 if (StrLen (CleanOriginal
) == 0) {
2630 SHELL_FREE_NON_NULL(CleanOriginal
);
2631 return (EFI_SUCCESS
);
2634 Status
= ProcessCommandLineToFinal(&CleanOriginal
);
2635 if (EFI_ERROR(Status
)) {
2636 SHELL_FREE_NON_NULL(CleanOriginal
);
2641 // We don't do normal processing with a split command line (output from one command input to another)
2643 if (ContainsSplit(CleanOriginal
)) {
2644 Status
= ProcessNewSplitCommandLine(CleanOriginal
);
2645 SHELL_FREE_NON_NULL(CleanOriginal
);
2650 // We need the first parameter information so we can determine the operation type
2652 FirstParameter
= AllocateZeroPool(StrSize(CleanOriginal
));
2653 if (FirstParameter
== NULL
) {
2654 SHELL_FREE_NON_NULL(CleanOriginal
);
2655 return (EFI_OUT_OF_RESOURCES
);
2657 TempWalker
= CleanOriginal
;
2658 if (!EFI_ERROR(GetNextParameter(&TempWalker
, &FirstParameter
, StrSize(CleanOriginal
), TRUE
))) {
2660 // Depending on the first parameter we change the behavior
2662 switch (Type
= GetOperationType(FirstParameter
)) {
2663 case File_Sys_Change
:
2664 Status
= ChangeMappedDrive (FirstParameter
);
2666 case Internal_Command
:
2667 case Script_File_Name
:
2668 case Efi_Application
:
2669 Status
= SetupAndRunCommandOrFile(Type
, CleanOriginal
, FirstParameter
, ShellInfoObject
.NewShellParametersProtocol
, CommandStatus
);
2673 // Whatever was typed, it was invalid.
2675 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2676 SetLastError(SHELL_NOT_FOUND
);
2680 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2681 SetLastError(SHELL_NOT_FOUND
);
2684 SHELL_FREE_NON_NULL(CleanOriginal
);
2685 SHELL_FREE_NON_NULL(FirstParameter
);
2691 Function will process and run a command line.
2693 This will determine if the command line represents an internal shell
2694 command or dispatch an external application.
2696 @param[in] CmdLine The command line to parse.
2698 @retval EFI_SUCCESS The command was completed.
2699 @retval EFI_ABORTED The command's operation was aborted.
2704 IN CONST CHAR16
*CmdLine
2707 return (RunShellCommand(CmdLine
, NULL
));
2711 STATIC CONST UINT16 InvalidChars
[] = {L
'*', L
'?', L
'<', L
'>', L
'\\', L
'/', L
'\"', 0x0001, 0x0002};
2713 Function determines if the CommandName COULD be a valid command. It does not determine whether
2714 this is a valid command. It only checks for invalid characters.
2716 @param[in] CommandName The name to check
2718 @retval TRUE CommandName could be a command name
2719 @retval FALSE CommandName could not be a valid command name
2724 IN CONST CHAR16
*CommandName
2728 if (CommandName
== NULL
) {
2733 ; Count
< sizeof(InvalidChars
) / sizeof(InvalidChars
[0])
2736 if (ScanMem16(CommandName
, StrSize(CommandName
), InvalidChars
[Count
]) != NULL
) {
2744 Function to process a NSH script file via SHELL_FILE_HANDLE.
2746 @param[in] Handle The handle to the already opened file.
2747 @param[in] Name The name of the script file.
2749 @retval EFI_SUCCESS the script completed successfully
2753 RunScriptFileHandle (
2754 IN SHELL_FILE_HANDLE Handle
,
2755 IN CONST CHAR16
*Name
2759 SCRIPT_FILE
*NewScriptFile
;
2761 UINTN PrintBuffSize
;
2762 CHAR16
*CommandLine
;
2763 CHAR16
*CommandLine2
;
2764 CHAR16
*CommandLine3
;
2765 SCRIPT_COMMAND_LIST
*LastCommand
;
2767 BOOLEAN PreScriptEchoState
;
2768 BOOLEAN PreCommandEchoState
;
2769 CONST CHAR16
*CurDir
;
2771 CHAR16 LeString
[50];
2772 LIST_ENTRY OldBufferList
;
2774 ASSERT(!ShellCommandGetScriptExit());
2776 PreScriptEchoState
= ShellCommandGetEchoState();
2777 PrintBuffSize
= PcdGet16(PcdShellPrintBufferSize
);
2779 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
2780 if (NewScriptFile
== NULL
) {
2781 return (EFI_OUT_OF_RESOURCES
);
2787 ASSERT(NewScriptFile
->ScriptName
== NULL
);
2788 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
2789 if (NewScriptFile
->ScriptName
== NULL
) {
2790 DeleteScriptFileStruct(NewScriptFile
);
2791 return (EFI_OUT_OF_RESOURCES
);
2795 // Save the parameters (used to replace %0 to %9 later on)
2797 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
2798 if (NewScriptFile
->Argc
!= 0) {
2799 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
2800 if (NewScriptFile
->Argv
== NULL
) {
2801 DeleteScriptFileStruct(NewScriptFile
);
2802 return (EFI_OUT_OF_RESOURCES
);
2804 for (LoopVar
= 0 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
2805 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
2806 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
2807 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
2808 DeleteScriptFileStruct(NewScriptFile
);
2809 return (EFI_OUT_OF_RESOURCES
);
2813 NewScriptFile
->Argv
= NULL
;
2816 InitializeListHead(&NewScriptFile
->CommandList
);
2817 InitializeListHead(&NewScriptFile
->SubstList
);
2820 // Now build the list of all script commands.
2823 while(!ShellFileHandleEof(Handle
)) {
2824 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
2826 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0 || CommandLine
[0] == '#') {
2827 SHELL_FREE_NON_NULL(CommandLine
);
2830 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
2831 if (NewScriptFile
->CurrentCommand
== NULL
) {
2832 SHELL_FREE_NON_NULL(CommandLine
);
2833 DeleteScriptFileStruct(NewScriptFile
);
2834 return (EFI_OUT_OF_RESOURCES
);
2837 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
2838 NewScriptFile
->CurrentCommand
->Data
= NULL
;
2839 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
2841 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2845 // Add this as the topmost script file
2847 ShellCommandSetNewScript (NewScriptFile
);
2850 // Now enumerate through the commands and run each one.
2852 CommandLine
= AllocateZeroPool(PrintBuffSize
);
2853 if (CommandLine
== NULL
) {
2854 DeleteScriptFileStruct(NewScriptFile
);
2855 return (EFI_OUT_OF_RESOURCES
);
2857 CommandLine2
= AllocateZeroPool(PrintBuffSize
);
2858 if (CommandLine2
== NULL
) {
2859 FreePool(CommandLine
);
2860 DeleteScriptFileStruct(NewScriptFile
);
2861 return (EFI_OUT_OF_RESOURCES
);
2864 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
2865 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
2866 ; // conditional increment in the body of the loop
2868 ASSERT(CommandLine2
!= NULL
);
2869 StrnCpyS( CommandLine2
,
2870 PrintBuffSize
/sizeof(CHAR16
),
2871 NewScriptFile
->CurrentCommand
->Cl
,
2872 PrintBuffSize
/sizeof(CHAR16
) - 1
2875 SaveBufferList(&OldBufferList
);
2878 // NULL out comments
2880 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
2881 if (*CommandLine3
== L
'^') {
2882 if ( *(CommandLine3
+1) == L
':') {
2883 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
2884 } else if (*(CommandLine3
+1) == L
'#') {
2887 } else if (*CommandLine3
== L
'#') {
2888 *CommandLine3
= CHAR_NULL
;
2892 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
2894 // Due to variability in starting the find and replace action we need to have both buffers the same.
2896 StrnCpyS( CommandLine
,
2897 PrintBuffSize
/sizeof(CHAR16
),
2899 PrintBuffSize
/sizeof(CHAR16
) - 1
2903 // Remove the %0 to %9 from the command line (if we have some arguments)
2905 if (NewScriptFile
->Argv
!= NULL
) {
2906 switch (NewScriptFile
->Argc
) {
2908 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%9", NewScriptFile
->Argv
[9], FALSE
, FALSE
);
2909 ASSERT_EFI_ERROR(Status
);
2911 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%8", NewScriptFile
->Argv
[8], FALSE
, FALSE
);
2912 ASSERT_EFI_ERROR(Status
);
2914 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%7", NewScriptFile
->Argv
[7], FALSE
, FALSE
);
2915 ASSERT_EFI_ERROR(Status
);
2917 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%6", NewScriptFile
->Argv
[6], FALSE
, FALSE
);
2918 ASSERT_EFI_ERROR(Status
);
2920 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%5", NewScriptFile
->Argv
[5], FALSE
, FALSE
);
2921 ASSERT_EFI_ERROR(Status
);
2923 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%4", NewScriptFile
->Argv
[4], FALSE
, FALSE
);
2924 ASSERT_EFI_ERROR(Status
);
2926 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%3", NewScriptFile
->Argv
[3], FALSE
, FALSE
);
2927 ASSERT_EFI_ERROR(Status
);
2929 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%2", NewScriptFile
->Argv
[2], FALSE
, FALSE
);
2930 ASSERT_EFI_ERROR(Status
);
2932 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%1", NewScriptFile
->Argv
[1], FALSE
, FALSE
);
2933 ASSERT_EFI_ERROR(Status
);
2935 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%0", NewScriptFile
->Argv
[0], FALSE
, FALSE
);
2936 ASSERT_EFI_ERROR(Status
);
2942 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%1", L
"\"\"", FALSE
, FALSE
);
2943 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%2", L
"\"\"", FALSE
, FALSE
);
2944 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%3", L
"\"\"", FALSE
, FALSE
);
2945 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%4", L
"\"\"", FALSE
, FALSE
);
2946 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%5", L
"\"\"", FALSE
, FALSE
);
2947 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%6", L
"\"\"", FALSE
, FALSE
);
2948 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%7", L
"\"\"", FALSE
, FALSE
);
2949 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PrintBuffSize
, L
"%8", L
"\"\"", FALSE
, FALSE
);
2950 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PrintBuffSize
, L
"%9", L
"\"\"", FALSE
, FALSE
);
2952 StrnCpyS( CommandLine2
,
2953 PrintBuffSize
/sizeof(CHAR16
),
2955 PrintBuffSize
/sizeof(CHAR16
) - 1
2958 LastCommand
= NewScriptFile
->CurrentCommand
;
2960 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
2962 if (CommandLine3
!= NULL
&& CommandLine3
[0] == L
':' ) {
2964 // This line is a goto target / label
2967 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
2968 if (CommandLine3
[0] == L
'@') {
2970 // We need to save the current echo state
2971 // and disable echo for just this command.
2973 PreCommandEchoState
= ShellCommandGetEchoState();
2974 ShellCommandSetEchoState(FALSE
);
2975 Status
= RunCommand(CommandLine3
+1);
2978 // If command was "@echo -off" or "@echo -on" then don't restore echo state
2980 if (StrCmp (L
"@echo -off", CommandLine3
) != 0 &&
2981 StrCmp (L
"@echo -on", CommandLine3
) != 0) {
2983 // Now restore the pre-'@' echo state.
2985 ShellCommandSetEchoState(PreCommandEchoState
);
2988 if (ShellCommandGetEchoState()) {
2989 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
2990 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
2991 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
2993 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
2995 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
2997 Status
= RunCommand(CommandLine3
);
3001 if (ShellCommandGetScriptExit()) {
3003 // ShellCommandGetExitCode() always returns a UINT64
3005 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ShellCommandGetExitCode());
3006 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
3007 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
3009 ShellCommandRegisterExit(FALSE
, 0);
3010 Status
= EFI_SUCCESS
;
3011 RestoreBufferList(&OldBufferList
);
3014 if (ShellGetExecutionBreakFlag()) {
3015 RestoreBufferList(&OldBufferList
);
3018 if (EFI_ERROR(Status
)) {
3019 RestoreBufferList(&OldBufferList
);
3022 if (ShellCommandGetExit()) {
3023 RestoreBufferList(&OldBufferList
);
3028 // If that commend did not update the CurrentCommand then we need to advance it...
3030 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
3031 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
3032 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
3033 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
3037 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
3038 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
3039 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
3042 RestoreBufferList(&OldBufferList
);
3046 FreePool(CommandLine
);
3047 FreePool(CommandLine2
);
3048 ShellCommandSetNewScript (NULL
);
3051 // Only if this was the last script reset the state.
3053 if (ShellCommandGetCurrentScriptFile()==NULL
) {
3054 ShellCommandSetEchoState(PreScriptEchoState
);
3056 return (EFI_SUCCESS
);
3060 Function to process a NSH script file.
3062 @param[in] ScriptPath Pointer to the script file name (including file system path).
3063 @param[in] Handle the handle of the script file already opened.
3064 @param[in] CmdLine the command line to run.
3065 @param[in] ParamProtocol the shell parameters protocol pointer
3067 @retval EFI_SUCCESS the script completed successfully
3072 IN CONST CHAR16
*ScriptPath
,
3073 IN SHELL_FILE_HANDLE Handle OPTIONAL
,
3074 IN CONST CHAR16
*CmdLine
,
3075 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
3079 SHELL_FILE_HANDLE FileHandle
;
3083 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
3084 return (EFI_INVALID_PARAMETER
);
3088 // get the argc and argv updated for scripts
3090 Status
= UpdateArgcArgv(ParamProtocol
, CmdLine
, Script_File_Name
, &Argv
, &Argc
);
3091 if (!EFI_ERROR(Status
)) {
3093 if (Handle
== NULL
) {
3097 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
3098 if (!EFI_ERROR(Status
)) {
3102 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
);
3105 // now close the file
3107 ShellCloseFile(&FileHandle
);
3110 Status
= RunScriptFileHandle(Handle
, ScriptPath
);
3115 // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
3116 // This is safe even if the update API failed. In this case, it may be a no-op.
3118 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);
3124 Return the pointer to the first occurrence of any character from a list of characters.
3126 @param[in] String the string to parse
3127 @param[in] CharacterList the list of character to look for
3128 @param[in] EscapeCharacter An escape character to skip
3130 @return the location of the first character in the string
3131 @retval CHAR_NULL no instance of any character in CharacterList was found in String
3136 IN CONST CHAR16
*String
,
3137 IN CONST CHAR16
*CharacterList
,
3138 IN CONST CHAR16 EscapeCharacter
3144 for (WalkStr
= 0; WalkStr
< StrLen(String
); WalkStr
++) {
3145 if (String
[WalkStr
] == EscapeCharacter
) {
3149 for (WalkChar
= 0; WalkChar
< StrLen(CharacterList
); WalkChar
++) {
3150 if (String
[WalkStr
] == CharacterList
[WalkChar
]) {
3151 return (&String
[WalkStr
]);
3155 return (String
+ StrLen(String
));