2 This is THE shell (application)
4 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2013-2014, Hewlett-Packard Development Company, L.P.
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
= {
56 {{NULL
, NULL
}, NULL
, NULL
},
57 {{NULL
, NULL
}, NULL
, NULL
},
69 STATIC CONST CHAR16 mScriptExtension
[] = L
".NSH";
70 STATIC CONST CHAR16 mExecutableExtensions
[] = L
".NSH;.EFI";
71 STATIC CONST CHAR16 mStartupScript
[] = L
"startup.nsh";
74 Cleans off leading and trailing spaces and tabs.
76 @param[in] String pointer to the string to trim them off.
84 ASSERT(String
!= NULL
);
85 ASSERT(*String
!= NULL
);
87 // Remove any spaces and tabs at the beginning of the (*String).
89 while (((*String
)[0] == L
' ') || ((*String
)[0] == L
'\t')) {
90 CopyMem((*String
), (*String
)+1, StrSize((*String
)) - sizeof((*String
)[0]));
94 // Remove any spaces and tabs at the end of the (*String).
96 while ((StrLen (*String
) > 0) && (((*String
)[StrLen((*String
))-1] == L
' ') || ((*String
)[StrLen((*String
))-1] == L
'\t'))) {
97 (*String
)[StrLen((*String
))-1] = CHAR_NULL
;
100 return (EFI_SUCCESS
);
104 Find a command line contains a split operation
106 @param[in] CmdLine The command line to parse.
108 @retval A pointer to the | character in CmdLine or NULL if not present.
113 IN CONST CHAR16
*CmdLine
116 CONST CHAR16
*TempSpot
;
118 if (StrStr(CmdLine
, L
"|") != NULL
) {
119 for (TempSpot
= CmdLine
; TempSpot
!= NULL
&& *TempSpot
!= CHAR_NULL
; TempSpot
++) {
120 if (*TempSpot
== L
'^' && *(TempSpot
+1) == L
'|') {
122 } else if (*TempSpot
== L
'|') {
131 Determine if a command line contains a split operation
133 @param[in] CmdLine The command line to parse.
135 @retval TRUE CmdLine has a valid split.
136 @retval FALSE CmdLine does not have a valid split.
141 IN CONST CHAR16
*CmdLine
144 CONST CHAR16
*TempSpot
;
145 TempSpot
= FindSplit(CmdLine
);
146 return (BOOLEAN
) ((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
));
150 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
151 feature's enabled state was not known when the shell initially launched.
153 @retval EFI_SUCCESS The feature is enabled.
154 @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.
158 InternalEfiShellStartCtrlSMonitor(
162 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
*SimpleEx
;
163 EFI_KEY_DATA KeyData
;
166 Status
= gBS
->OpenProtocol(
167 gST
->ConsoleInHandle
,
168 &gEfiSimpleTextInputExProtocolGuid
,
172 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
173 if (EFI_ERROR(Status
)) {
178 STRING_TOKEN (STR_SHELL_NO_IN_EX
),
179 ShellInfoObject
.HiiHandle
);
180 return (EFI_SUCCESS
);
183 KeyData
.KeyState
.KeyToggleState
= 0;
184 KeyData
.Key
.ScanCode
= 0;
185 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
186 KeyData
.Key
.UnicodeChar
= L
's';
188 Status
= SimpleEx
->RegisterKeyNotify(
191 NotificationFunction
,
192 &ShellInfoObject
.CtrlSNotifyHandle1
);
194 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
195 if (!EFI_ERROR(Status
)) {
196 Status
= SimpleEx
->RegisterKeyNotify(
199 NotificationFunction
,
200 &ShellInfoObject
.CtrlSNotifyHandle2
);
202 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
203 KeyData
.Key
.UnicodeChar
= 19;
205 if (!EFI_ERROR(Status
)) {
206 Status
= SimpleEx
->RegisterKeyNotify(
209 NotificationFunction
,
210 &ShellInfoObject
.CtrlSNotifyHandle3
);
212 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
213 if (!EFI_ERROR(Status
)) {
214 Status
= SimpleEx
->RegisterKeyNotify(
217 NotificationFunction
,
218 &ShellInfoObject
.CtrlSNotifyHandle4
);
226 The entry point for the application.
228 @param[in] ImageHandle The firmware allocated handle for the EFI image.
229 @param[in] SystemTable A pointer to the EFI System Table.
231 @retval EFI_SUCCESS The entry point is executed successfully.
232 @retval other Some error occurs when executing this entry point.
238 IN EFI_HANDLE ImageHandle
,
239 IN EFI_SYSTEM_TABLE
*SystemTable
245 EFI_HANDLE ConInHandle
;
246 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*OldConIn
;
248 if (PcdGet8(PcdShellSupportLevel
) > 3) {
249 return (EFI_UNSUPPORTED
);
255 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
256 if (EFI_ERROR(Status
)) {
261 // Populate the global structure from PCDs
263 ShellInfoObject
.ImageDevPath
= NULL
;
264 ShellInfoObject
.FileDevPath
= NULL
;
265 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
266 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
267 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
270 // verify we dont allow for spec violation
272 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
275 // Initialize the LIST ENTRY objects...
277 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
278 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
279 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
282 // Check PCDs for optional features that are not implemented yet.
284 if ( PcdGetBool(PcdShellSupportOldProtocols
)
285 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
286 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
288 return (EFI_UNSUPPORTED
);
292 // turn off the watchdog timer
294 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
297 // install our console logger. This will keep a log of the output for back-browsing
299 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
300 if (!EFI_ERROR(Status
)) {
302 // Enable the cursor to be visible
304 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
307 // If supporting EFI 1.1 we need to install HII protocol
308 // only do this if PcdShellRequireHiiPlatform == FALSE
310 // remove EFI_UNSUPPORTED check above when complete.
311 ///@todo add support for Framework HII
314 // install our (solitary) HII package
316 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
317 if (ShellInfoObject
.HiiHandle
== NULL
) {
318 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
319 ///@todo Add our package into Framework HII
321 if (ShellInfoObject
.HiiHandle
== NULL
) {
322 Status
= EFI_NOT_STARTED
;
328 // create and install the EfiShellParametersProtocol
330 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
331 ASSERT_EFI_ERROR(Status
);
332 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
335 // create and install the EfiShellProtocol
337 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
338 ASSERT_EFI_ERROR(Status
);
339 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
342 // Now initialize the shell library (it requires Shell Parameters protocol)
344 Status
= ShellInitialize();
345 ASSERT_EFI_ERROR(Status
);
347 Status
= CommandInit();
348 ASSERT_EFI_ERROR(Status
);
351 // Check the command line
353 Status
= ProcessCommandLine ();
354 if (EFI_ERROR (Status
)) {
359 // If shell support level is >= 1 create the mappings and paths
361 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
362 Status
= ShellCommandCreateInitialMappingsAndPaths();
366 // save the device path for the loaded image and the device path for the filepath (under loaded image)
367 // These are where to look for the startup.nsh file
369 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
370 ASSERT_EFI_ERROR(Status
);
373 // Display the version
375 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
378 gST
->ConOut
->Mode
->CursorRow
,
380 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL
),
381 ShellInfoObject
.HiiHandle
,
382 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
383 gEfiShellProtocol
->MajorVersion
,
384 gEfiShellProtocol
->MinorVersion
391 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER
),
392 ShellInfoObject
.HiiHandle
,
393 (CHAR16
*) PcdGetPtr (PcdShellSupplier
)
400 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI
),
401 ShellInfoObject
.HiiHandle
,
402 (gST
->Hdr
.Revision
&0xffff0000)>>16,
403 (gST
->Hdr
.Revision
&0x0000ffff),
405 gST
->FirmwareRevision
410 // Display the mapping
412 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
413 Status
= RunCommand(L
"map");
414 ASSERT_EFI_ERROR(Status
);
418 // init all the built in alias'
420 Status
= SetBuiltInAlias();
421 ASSERT_EFI_ERROR(Status
);
424 // Initialize environment variables
426 if (ShellCommandGetProfileList() != NULL
) {
427 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
428 ASSERT_EFI_ERROR(Status
);
432 TempString
= AllocateZeroPool(Size
);
434 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
435 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
436 ASSERT_EFI_ERROR(Status
);
438 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
439 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
440 ASSERT_EFI_ERROR(Status
);
442 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
443 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
444 ASSERT_EFI_ERROR(Status
);
446 FreePool(TempString
);
448 if (!EFI_ERROR(Status
)) {
449 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
451 // Set up the event for CTRL-C monitoring...
453 Status
= InernalEfiShellStartMonitor();
456 if (!EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
458 // Set up the event for CTRL-S monitoring...
460 Status
= InternalEfiShellStartCtrlSMonitor();
463 if (!EFI_ERROR(Status
) && ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
465 // close off the gST->ConIn
467 OldConIn
= gST
->ConIn
;
468 ConInHandle
= gST
->ConsoleInHandle
;
469 gST
->ConIn
= CreateSimpleTextInOnFile((SHELL_FILE_HANDLE
)&FileInterfaceNulFile
, &gST
->ConsoleInHandle
);
475 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
477 // process the startup script or launch the called app.
479 Status
= DoStartupScript(ShellInfoObject
.ImageDevPath
, ShellInfoObject
.FileDevPath
);
482 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
&& !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
484 // begin the UI waiting loop
488 // clean out all the memory allocated for CONST <something> * return values
489 // between each shell prompt presentation
491 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
492 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
496 // Reset page break back to default.
498 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
499 ASSERT (ShellInfoObject
.ConsoleInfo
!= NULL
);
500 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
501 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
504 // Reset the CTRL-C event (yes we ignore the return values)
506 Status
= gBS
->CheckEvent (ShellInfoObject
.NewEfiShellProtocol
->ExecutionBreak
);
511 Status
= DoShellPrompt();
512 } while (!ShellCommandGetExit());
514 if (OldConIn
!= NULL
&& ConInHandle
!= NULL
) {
515 CloseSimpleTextInOnFile (gST
->ConIn
);
516 gST
->ConIn
= OldConIn
;
517 gST
->ConsoleInHandle
= ConInHandle
;
524 // uninstall protocols / free memory / etc...
526 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
527 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
528 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
530 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
531 FreePool(ShellInfoObject
.ImageDevPath
);
532 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
534 if (ShellInfoObject
.FileDevPath
!= NULL
) {
535 FreePool(ShellInfoObject
.FileDevPath
);
536 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
538 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
539 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
540 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
542 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
543 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
544 InternalEfiShellSetEnv(L
"cwd", NULL
, TRUE
);
546 CleanUpShellProtocol(ShellInfoObject
.NewEfiShellProtocol
);
547 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
550 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
551 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
554 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
555 ASSERT(FALSE
); ///@todo finish this de-allocation.
558 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
559 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
560 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
563 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
564 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
565 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
568 if (ShellInfoObject
.HiiHandle
!= NULL
) {
569 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
570 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
573 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
574 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
577 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
578 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
579 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
580 FreePool(ShellInfoObject
.ConsoleInfo
);
581 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
584 if (ShellCommandGetExit()) {
585 return ((EFI_STATUS
)ShellCommandGetExitCode());
591 Sets all the alias' that were registered with the ShellCommandLib library.
593 @retval EFI_SUCCESS all init commands were run sucessfully.
601 CONST ALIAS_LIST
*List
;
605 // Get all the commands we want to run
607 List
= ShellCommandGetInitAliasList();
610 // for each command in the List
612 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
613 ; !IsNull (&List
->Link
, &Node
->Link
)
614 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
617 // install the alias'
619 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
620 ASSERT_EFI_ERROR(Status
);
622 return (EFI_SUCCESS
);
626 Internal function to determine if 2 command names are really the same.
628 @param[in] Command1 The pointer to the first command name.
629 @param[in] Command2 The pointer to the second command name.
631 @retval TRUE The 2 command names are the same.
632 @retval FALSE The 2 command names are not the same.
637 IN CONST CHAR16
*Command1
,
638 IN CONST CHAR16
*Command2
641 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
648 Internal function to determine if a command is a script only command.
650 @param[in] CommandName The pointer to the command name.
652 @retval TRUE The command is a script only command.
653 @retval FALSE The command is not a script only command.
658 IN CONST CHAR16
*CommandName
661 if (IsCommand(CommandName
, L
"for")
662 ||IsCommand(CommandName
, L
"endfor")
663 ||IsCommand(CommandName
, L
"if")
664 ||IsCommand(CommandName
, L
"else")
665 ||IsCommand(CommandName
, L
"endif")
666 ||IsCommand(CommandName
, L
"goto")) {
673 This function will populate the 2 device path protocol parameters based on the
674 global gImageHandle. The DevPath will point to the device path for the handle that has
675 loaded image protocol installed on it. The FilePath will point to the device path
676 for the file that was loaded.
678 @param[in, out] DevPath On a sucessful return the device path to the loaded image.
679 @param[in, out] FilePath On a sucessful return the device path to the file.
681 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
682 @retval other A error from gBS->HandleProtocol.
688 GetDevicePathsForImageAndFile (
689 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
690 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
694 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
695 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
697 ASSERT(DevPath
!= NULL
);
698 ASSERT(FilePath
!= NULL
);
700 Status
= gBS
->OpenProtocol (
702 &gEfiLoadedImageProtocolGuid
,
703 (VOID
**)&LoadedImage
,
706 EFI_OPEN_PROTOCOL_GET_PROTOCOL
708 if (!EFI_ERROR (Status
)) {
709 Status
= gBS
->OpenProtocol (
710 LoadedImage
->DeviceHandle
,
711 &gEfiDevicePathProtocolGuid
,
712 (VOID
**)&ImageDevicePath
,
715 EFI_OPEN_PROTOCOL_GET_PROTOCOL
717 if (!EFI_ERROR (Status
)) {
718 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
719 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
721 LoadedImage
->DeviceHandle
,
722 &gEfiDevicePathProtocolGuid
,
728 &gEfiLoadedImageProtocolGuid
,
736 Process all Uefi Shell 2.0 command line options.
738 see Uefi Shell 2.0 section 3.2 for full details.
740 the command line must resemble the following:
742 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
744 ShellOpt-options Options which control the initialization behavior of the shell.
745 These options are read from the EFI global variable "ShellOpt"
746 and are processed before options or file-name.
748 options Options which control the initialization behavior of the shell.
750 file-name The name of a UEFI shell application or script to be executed
751 after initialization is complete. By default, if file-name is
752 specified, then -nostartup is implied. Scripts are not supported
755 file-name-options The command-line options that are passed to file-name when it
758 This will initialize the ShellInfoObject.ShellInitSettings global variable.
760 @retval EFI_SUCCESS The variable is initialized.
771 CHAR16
*DelayValueStr
;
774 EFI_UNICODE_COLLATION_PROTOCOL
*UnicodeCollation
;
776 // `file-name-options` will contain arguments to `file-name` that we don't
777 // know about. This would cause ShellCommandLineParse to error, so we parse
778 // arguments manually, ignoring those after the first thing that doesn't look
779 // like a shell option (which is assumed to be `file-name`).
781 Status
= gBS
->LocateProtocol (
782 &gEfiUnicodeCollationProtocolGuid
,
784 (VOID
**) &UnicodeCollation
786 if (EFI_ERROR (Status
)) {
790 // Set default options
791 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= FALSE
;
792 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= FALSE
;
793 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= FALSE
;
794 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= FALSE
;
795 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= FALSE
;
796 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= FALSE
;
797 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= FALSE
;
798 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= FALSE
;
799 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= FALSE
;
800 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
803 // Start LoopVar at 0 to parse only optional arguments at Argv[0]
804 // and parse other parameters from Argv[1]. This is for use case that
805 // UEFI Shell boot option is created, and OptionalData is provided
806 // that starts with shell command-line options.
808 for (LoopVar
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
809 CurrentArg
= gEfiShellParametersProtocol
->Argv
[LoopVar
];
810 if (UnicodeCollation
->StriColl (
815 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= TRUE
;
817 else if (UnicodeCollation
->StriColl (
822 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= TRUE
;
824 else if (UnicodeCollation
->StriColl (
829 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= TRUE
;
831 else if (UnicodeCollation
->StriColl (
836 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= TRUE
;
838 else if (UnicodeCollation
->StriColl (
843 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= TRUE
;
845 else if (UnicodeCollation
->StriColl (
850 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= TRUE
;
852 else if (UnicodeCollation
->StriColl (
857 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= TRUE
;
859 else if (UnicodeCollation
->StriColl (
864 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= TRUE
;
865 // Check for optional delay value following "-delay"
866 DelayValueStr
= gEfiShellParametersProtocol
->Argv
[LoopVar
+ 1];
867 if (DelayValueStr
!= NULL
){
868 if (*DelayValueStr
== L
':') {
871 if (!EFI_ERROR(ShellConvertStringToUint64 (
877 ShellInfoObject
.ShellInitSettings
.Delay
= (UINTN
)DelayValue
;
881 } else if (UnicodeCollation
->StriColl (
886 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= TRUE
;
887 } else if (StrnCmp (L
"-", CurrentArg
, 1) == 0) {
888 // Unrecognised option
889 ShellPrintHiiEx(-1, -1, NULL
,
890 STRING_TOKEN (STR_GEN_PROBLEM
),
891 ShellInfoObject
.HiiHandle
,
894 return EFI_INVALID_PARAMETER
;
897 // First argument should be Shell.efi image name
903 ShellInfoObject
.ShellInitSettings
.FileName
= AllocateCopyPool(StrSize(CurrentArg
), CurrentArg
);
904 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
905 return (EFI_OUT_OF_RESOURCES
);
908 // We found `file-name`.
910 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
913 // Add `file-name-options`
914 for (Size
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
915 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
916 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
920 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
921 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
922 return (EFI_OUT_OF_RESOURCES
);
924 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
926 gEfiShellParametersProtocol
->Argv
[LoopVar
],
928 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
929 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
930 return (EFI_OUT_OF_RESOURCES
);
936 // "-nointerrupt" overrides "-delay"
937 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
938 ShellInfoObject
.ShellInitSettings
.Delay
= 0;
945 Handles all interaction with the default startup script.
947 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
949 @param ImagePath the path to the image for shell. first place to look for the startup script
950 @param FilePath the path to the file for shell. second place to look for the startup script.
952 @retval EFI_SUCCESS the variable is initialized.
957 IN EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
958 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
964 SHELL_FILE_HANDLE FileHandle
;
965 EFI_DEVICE_PATH_PROTOCOL
*NewPath
;
966 EFI_DEVICE_PATH_PROTOCOL
*NamePath
;
967 CHAR16
*FileStringPath
;
970 CONST CHAR16
*MapName
;
972 Key
.UnicodeChar
= CHAR_NULL
;
976 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
978 // launch something else instead
980 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
981 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
982 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
984 FileStringPath
= AllocateZeroPool(NewSize
);
985 if (FileStringPath
== NULL
) {
986 return (EFI_OUT_OF_RESOURCES
);
988 StrnCpy(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileName
, NewSize
/sizeof(CHAR16
) -1);
989 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
990 StrnCat(FileStringPath
, L
" ", NewSize
/sizeof(CHAR16
) - StrLen(FileStringPath
) -1);
991 StrnCat(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileOptions
, NewSize
/sizeof(CHAR16
) - StrLen(FileStringPath
) -1);
993 Status
= RunCommand(FileStringPath
);
994 FreePool(FileStringPath
);
1000 // for shell level 0 we do no scripts
1001 // Without the Startup bit overriding we allow for nostartup to prevent scripts
1003 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
1004 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
1006 return (EFI_SUCCESS
);
1009 gST
->ConOut
->EnableCursor(gST
->ConOut
, FALSE
);
1011 // print out our warning and see if they press a key
1013 for ( Status
= EFI_UNSUPPORTED
, Delay
= ShellInfoObject
.ShellInitSettings
.Delay
1014 ; Delay
!= 0 && EFI_ERROR(Status
)
1017 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
);
1018 gBS
->Stall (1000000);
1019 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
1020 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
1023 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
1024 gST
->ConOut
->EnableCursor(gST
->ConOut
, TRUE
);
1029 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
1030 return (EFI_SUCCESS
);
1034 // Try the first location (must be file system)
1036 MapName
= ShellInfoObject
.NewEfiShellProtocol
->GetMapFromDevicePath(&ImagePath
);
1037 if (MapName
!= NULL
) {
1038 FileStringPath
= NULL
;
1040 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, MapName
, 0);
1041 if (FileStringPath
== NULL
) {
1042 Status
= EFI_OUT_OF_RESOURCES
;
1044 TempSpot
= StrStr(FileStringPath
, L
";");
1045 if (TempSpot
!= NULL
) {
1046 *TempSpot
= CHAR_NULL
;
1048 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, ((FILEPATH_DEVICE_PATH
*)FilePath
)->PathName
, 0);
1049 PathRemoveLastItem(FileStringPath
);
1050 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, mStartupScript
, 0);
1051 Status
= ShellInfoObject
.NewEfiShellProtocol
->OpenFileByName(FileStringPath
, &FileHandle
, EFI_FILE_MODE_READ
);
1052 FreePool(FileStringPath
);
1055 if (EFI_ERROR(Status
)) {
1056 NamePath
= FileDevicePath (NULL
, mStartupScript
);
1057 NewPath
= AppendDevicePathNode (ImagePath
, NamePath
);
1063 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
1067 // If we got a file, run it
1069 if (!EFI_ERROR(Status
) && FileHandle
!= NULL
) {
1070 Status
= RunScriptFile (mStartupScript
, FileHandle
, L
"", ShellInfoObject
.NewShellParametersProtocol
);
1071 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
1073 FileStringPath
= ShellFindFilePath(mStartupScript
);
1074 if (FileStringPath
== NULL
) {
1076 // we return success since we dont need to have a startup script
1078 Status
= EFI_SUCCESS
;
1079 ASSERT(FileHandle
== NULL
);
1081 Status
= RunScriptFile(FileStringPath
, NULL
, L
"", ShellInfoObject
.NewShellParametersProtocol
);
1082 FreePool(FileStringPath
);
1091 Function to perform the shell prompt looping. It will do a single prompt,
1092 dispatch the result, and then return. It is expected that the caller will
1093 call this function in a loop many times.
1096 @retval RETURN_ABORTED
1107 CONST CHAR16
*CurDir
;
1114 // Get screen setting to decide size of the command line buffer
1116 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
1117 BufferSize
= Column
* Row
* sizeof (CHAR16
);
1118 CmdLine
= AllocateZeroPool (BufferSize
);
1119 if (CmdLine
== NULL
) {
1120 return EFI_OUT_OF_RESOURCES
;
1123 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1128 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
1130 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1131 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1133 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1137 // Read a line from the console
1139 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
1142 // Null terminate the string and parse it
1144 if (!EFI_ERROR (Status
)) {
1145 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
1146 Status
= RunCommand(CmdLine
);
1150 // Done with this command
1157 Add a buffer to the Buffer To Free List for safely returning buffers to other
1158 places without risking letting them modify internal shell information.
1160 @param Buffer Something to pass to FreePool when the shell is exiting.
1164 AddBufferToFreeList(
1168 BUFFER_LIST
*BufferListEntry
;
1170 if (Buffer
== NULL
) {
1174 BufferListEntry
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1175 ASSERT(BufferListEntry
!= NULL
);
1176 BufferListEntry
->Buffer
= Buffer
;
1177 InsertTailList(&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
1182 Add a buffer to the Line History List
1184 @param Buffer The line buffer to add.
1188 AddLineToCommandHistory(
1189 IN CONST CHAR16
*Buffer
1194 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1195 ASSERT(Node
!= NULL
);
1196 Node
->Buffer
= AllocateCopyPool(StrSize(Buffer
), Buffer
);
1197 ASSERT(Node
->Buffer
!= NULL
);
1199 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1203 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1204 with the correct command name.
1206 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1207 command name if it was an alias. If it was not
1208 an alias it will be unchanged. This function may
1209 change the buffer to fit the command name.
1211 @retval EFI_SUCCESS The name was changed.
1212 @retval EFI_SUCCESS The name was not an alias.
1213 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1218 IN OUT CHAR16
**CommandString
1221 CONST CHAR16
*NewString
;
1223 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
1224 if (NewString
== NULL
) {
1225 return (EFI_SUCCESS
);
1227 FreePool(*CommandString
);
1228 *CommandString
= AllocateCopyPool(StrSize(NewString
), NewString
);
1229 if (*CommandString
== NULL
) {
1230 return (EFI_OUT_OF_RESOURCES
);
1232 return (EFI_SUCCESS
);
1236 Parse for the next instance of one string within another string. Can optionally make sure that
1237 the string was not escaped (^ character) per the shell specification.
1239 @param[in] SourceString The string to search within
1240 @param[in] FindString The string to look for
1241 @param[in] CheckForEscapeCharacter TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances
1246 IN CONST CHAR16
*SourceString
,
1247 IN CONST CHAR16
*FindString
,
1248 IN CONST BOOLEAN CheckForEscapeCharacter
1252 if (SourceString
== NULL
) {
1255 Temp
= StrStr(SourceString
, FindString
);
1258 // If nothing found, or we dont care about escape characters
1260 if (Temp
== NULL
|| !CheckForEscapeCharacter
) {
1265 // If we found an escaped character, try again on the remainder of the string
1267 if ((Temp
> (SourceString
)) && *(Temp
-1) == L
'^') {
1268 return FindNextInstance(Temp
+1, FindString
, CheckForEscapeCharacter
);
1272 // we found the right character
1278 This function will eliminate unreplaced (and therefore non-found) environment variables.
1280 @param[in,out] CmdLine The command line to update.
1284 StripUnreplacedEnvironmentVariables(
1285 IN OUT CHAR16
*CmdLine
1288 CHAR16
*FirstPercent
;
1290 CHAR16
*SecondPercent
;
1291 CHAR16
*SecondQuote
;
1292 CHAR16
*CurrentLocator
;
1294 for (CurrentLocator
= CmdLine
; CurrentLocator
!= NULL
; ) {
1295 FirstQuote
= FindNextInstance(CurrentLocator
, L
"\"", TRUE
);
1296 FirstPercent
= FindNextInstance(CurrentLocator
, L
"%", TRUE
);
1297 SecondPercent
= FirstPercent
!=NULL
?FindNextInstance(FirstPercent
+1, L
"%", TRUE
):NULL
;
1298 if (FirstPercent
== NULL
|| SecondPercent
== NULL
) {
1300 // If we ever dont have 2 % we are done.
1305 if (FirstQuote
< FirstPercent
) {
1306 SecondQuote
= FirstQuote
!= NULL
?FindNextInstance(FirstQuote
+1, L
"\"", TRUE
):NULL
;
1308 // Quote is first found
1311 if (SecondQuote
< FirstPercent
) {
1313 // restart after the pair of "
1315 CurrentLocator
= SecondQuote
+ 1;
1316 } else /* FirstPercent < SecondQuote */{
1318 // Restart on the first percent
1320 CurrentLocator
= FirstPercent
;
1324 ASSERT(FirstPercent
< FirstQuote
);
1325 if (SecondPercent
< FirstQuote
) {
1326 FirstPercent
[0] = L
'\"';
1327 SecondPercent
[0] = L
'\"';
1330 // We need to remove from FirstPercent to SecondPercent
1332 CopyMem(FirstPercent
+ 1, SecondPercent
, StrSize(SecondPercent
));
1333 CurrentLocator
= FirstPercent
+ 2;
1336 ASSERT(FirstQuote
< SecondPercent
);
1337 CurrentLocator
= FirstQuote
;
1339 return (EFI_SUCCESS
);
1343 Function allocates a new command line and replaces all instances of environment
1344 variable names that are correctly preset to their values.
1346 If the return value is not NULL the memory must be caller freed.
1348 @param[in] OriginalCommandLine The original command line
1350 @retval NULL An error ocurred.
1351 @return The new command line with no environment variables present.
1355 ShellConvertVariables (
1356 IN CONST CHAR16
*OriginalCommandLine
1359 CONST CHAR16
*MasterEnvList
;
1361 CHAR16
*NewCommandLine1
;
1362 CHAR16
*NewCommandLine2
;
1366 SCRIPT_FILE
*CurrentScriptFile
;
1367 ALIAS_LIST
*AliasListNode
;
1369 ASSERT(OriginalCommandLine
!= NULL
);
1372 NewSize
= StrSize(OriginalCommandLine
);
1373 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
1376 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1379 // calculate the size required for the post-conversion string...
1381 if (CurrentScriptFile
!= NULL
) {
1382 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1383 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1384 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1386 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
1388 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
1391 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
1393 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
1394 NewSize
+= StrSize(AliasListNode
->CommandString
);
1400 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1401 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1402 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1404 if (StrSize(MasterEnvList
) > ItemSize
) {
1405 ItemSize
= StrSize(MasterEnvList
);
1407 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
1409 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
1412 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
1414 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
1415 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
1416 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
1422 // now do the replacements...
1424 NewCommandLine1
= AllocateCopyPool(NewSize
, OriginalCommandLine
);
1425 NewCommandLine2
= AllocateZeroPool(NewSize
);
1426 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
1427 if (NewCommandLine1
== NULL
|| NewCommandLine2
== NULL
|| ItemTemp
== NULL
) {
1428 SHELL_FREE_NON_NULL(NewCommandLine1
);
1429 SHELL_FREE_NON_NULL(NewCommandLine2
);
1430 SHELL_FREE_NON_NULL(ItemTemp
);
1433 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1434 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
1435 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1437 StrnCpy(ItemTemp
, L
"%", ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
))-1);
1438 StrnCat(ItemTemp
, MasterEnvList
, ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
))-1 - StrLen(ItemTemp
));
1439 StrnCat(ItemTemp
, L
"%", ((ItemSize
+(2*sizeof(CHAR16
)))/sizeof(CHAR16
))-1 - StrLen(ItemTemp
));
1440 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1441 StrnCpy(NewCommandLine1
, NewCommandLine2
, NewSize
/sizeof(CHAR16
)-1);
1443 if (CurrentScriptFile
!= NULL
) {
1444 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1445 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1446 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1448 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1449 StrnCpy(NewCommandLine1
, NewCommandLine2
, NewSize
/sizeof(CHAR16
)-1);
1453 // Remove non-existant environment variables in scripts only
1455 StripUnreplacedEnvironmentVariables(NewCommandLine1
);
1459 // Now cleanup any straggler intentionally ignored "%" characters
1461 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, L
"^%", L
"%", TRUE
, FALSE
);
1462 StrnCpy(NewCommandLine1
, NewCommandLine2
, NewSize
/sizeof(CHAR16
)-1);
1464 FreePool(NewCommandLine2
);
1467 return (NewCommandLine1
);
1471 Internal function to run a command line with pipe usage.
1473 @param[in] CmdLine The pointer to the command line.
1474 @param[in] StdIn The pointer to the Standard input.
1475 @param[in] StdOut The pointer to the Standard output.
1477 @retval EFI_SUCCESS The split command is executed successfully.
1478 @retval other Some error occurs when executing the split command.
1483 IN CONST CHAR16
*CmdLine
,
1484 IN SHELL_FILE_HANDLE
*StdIn
,
1485 IN SHELL_FILE_HANDLE
*StdOut
1489 CHAR16
*NextCommandLine
;
1490 CHAR16
*OurCommandLine
;
1494 SHELL_FILE_HANDLE
*TempFileHandle
;
1497 ASSERT(StdOut
== NULL
);
1499 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1501 Status
= EFI_SUCCESS
;
1502 NextCommandLine
= NULL
;
1503 OurCommandLine
= NULL
;
1507 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1508 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1510 if (NextCommandLine
== NULL
|| OurCommandLine
== NULL
) {
1511 SHELL_FREE_NON_NULL(OurCommandLine
);
1512 SHELL_FREE_NON_NULL(NextCommandLine
);
1513 return (EFI_OUT_OF_RESOURCES
);
1514 } else if (StrStr(OurCommandLine
, L
"|") != NULL
|| Size1
== 0 || Size2
== 0) {
1515 SHELL_FREE_NON_NULL(OurCommandLine
);
1516 SHELL_FREE_NON_NULL(NextCommandLine
);
1517 return (EFI_INVALID_PARAMETER
);
1518 } else if (NextCommandLine
[0] != CHAR_NULL
&&
1519 NextCommandLine
[0] == L
'a' &&
1520 NextCommandLine
[1] == L
' '
1522 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1530 // make a SPLIT_LIST item and add to list
1532 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1533 ASSERT(Split
!= NULL
);
1534 Split
->SplitStdIn
= StdIn
;
1535 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1536 ASSERT(Split
->SplitStdOut
!= NULL
);
1537 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1539 Status
= RunCommand(OurCommandLine
);
1542 // move the output from the first to the in to the second.
1544 TempFileHandle
= Split
->SplitStdOut
;
1545 if (Split
->SplitStdIn
== StdIn
) {
1546 Split
->SplitStdOut
= NULL
;
1548 Split
->SplitStdOut
= Split
->SplitStdIn
;
1550 Split
->SplitStdIn
= TempFileHandle
;
1551 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
), 0);
1553 if (!EFI_ERROR(Status
)) {
1554 Status
= RunCommand(NextCommandLine
);
1558 // remove the top level from the ScriptList
1560 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1561 RemoveEntryList(&Split
->Link
);
1564 // Note that the original StdIn is now the StdOut...
1566 if (Split
->SplitStdOut
!= NULL
&& Split
->SplitStdOut
!= StdIn
) {
1567 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdOut
));
1569 if (Split
->SplitStdIn
!= NULL
) {
1570 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
));
1574 FreePool(NextCommandLine
);
1575 FreePool(OurCommandLine
);
1581 Take the original command line, substitute any variables, free
1582 the original string, return the modified copy.
1584 @param[in] CmdLine pointer to the command line to update.
1586 @retval EFI_SUCCESS the function was successful.
1587 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1591 ShellSubstituteVariables(
1596 NewCmdLine
= ShellConvertVariables(*CmdLine
);
1597 SHELL_FREE_NON_NULL(*CmdLine
);
1598 if (NewCmdLine
== NULL
) {
1599 return (EFI_OUT_OF_RESOURCES
);
1601 *CmdLine
= NewCmdLine
;
1602 return (EFI_SUCCESS
);
1606 Take the original command line, substitute any alias in the first group of space delimited characters, free
1607 the original string, return the modified copy.
1609 @param[in] CmdLine pointer to the command line to update.
1611 @retval EFI_SUCCESS the function was successful.
1612 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1616 ShellSubstituteAliases(
1621 CHAR16
*CommandName
;
1623 UINTN PostAliasSize
;
1624 ASSERT(CmdLine
!= NULL
);
1625 ASSERT(*CmdLine
!= NULL
);
1629 if (StrStr((*CmdLine
), L
" ") == NULL
){
1630 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), 0);
1632 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), StrStr((*CmdLine
), L
" ") - (*CmdLine
));
1636 // This cannot happen 'inline' since the CmdLine can need extra space.
1639 if (!ShellCommandIsCommandOnList(CommandName
)) {
1641 // Convert via alias
1643 Status
= ShellConvertAlias(&CommandName
);
1644 if (EFI_ERROR(Status
)){
1648 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, CommandName
, 0);
1649 if (NewCmdLine
== NULL
) {
1650 SHELL_FREE_NON_NULL(CommandName
);
1651 SHELL_FREE_NON_NULL(*CmdLine
);
1652 return (EFI_OUT_OF_RESOURCES
);
1654 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, StrStr((*CmdLine
), L
" "), 0);
1655 if (NewCmdLine
== NULL
) {
1656 SHELL_FREE_NON_NULL(CommandName
);
1657 SHELL_FREE_NON_NULL(*CmdLine
);
1658 return (EFI_OUT_OF_RESOURCES
);
1661 NewCmdLine
= StrnCatGrow(&NewCmdLine
, NULL
, (*CmdLine
), 0);
1664 SHELL_FREE_NON_NULL(*CmdLine
);
1665 SHELL_FREE_NON_NULL(CommandName
);
1668 // re-assign the passed in double pointer to point to our newly allocated buffer
1670 *CmdLine
= NewCmdLine
;
1672 return (EFI_SUCCESS
);
1676 Takes the Argv[0] part of the command line and determine the meaning of it.
1678 @param[in] CmdName pointer to the command line to update.
1680 @retval Internal_Command The name is an internal command.
1681 @retval File_Sys_Change the name is a file system change.
1682 @retval Script_File_Name the name is a NSH script file.
1683 @retval Unknown_Invalid the name is unknown.
1684 @retval Efi_Application the name is an application (.EFI).
1686 SHELL_OPERATION_TYPES
1689 IN CONST CHAR16
*CmdName
1692 CHAR16
* FileWithPath
;
1693 CONST CHAR16
* TempLocation
;
1694 CONST CHAR16
* TempLocation2
;
1696 FileWithPath
= NULL
;
1698 // test for an internal command.
1700 if (ShellCommandIsCommandOnList(CmdName
)) {
1701 return (Internal_Command
);
1705 // Test for file system change request. anything ending with first : and cant have spaces.
1707 if (CmdName
[(StrLen(CmdName
)-1)] == L
':') {
1708 if ( StrStr(CmdName
, L
" ") != NULL
1709 || StrLen(StrStr(CmdName
, L
":")) > 1
1711 return (Unknown_Invalid
);
1713 return (File_Sys_Change
);
1719 if ((FileWithPath
= ShellFindFilePathEx(CmdName
, mExecutableExtensions
)) != NULL
) {
1721 // See if that file has a script file extension
1723 if (StrLen(FileWithPath
) > 4) {
1724 TempLocation
= FileWithPath
+StrLen(FileWithPath
)-4;
1725 TempLocation2
= mScriptExtension
;
1726 if (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0) {
1727 SHELL_FREE_NON_NULL(FileWithPath
);
1728 return (Script_File_Name
);
1733 // Was a file, but not a script. we treat this as an application.
1735 SHELL_FREE_NON_NULL(FileWithPath
);
1736 return (Efi_Application
);
1739 SHELL_FREE_NON_NULL(FileWithPath
);
1741 // No clue what this is... return invalid flag...
1743 return (Unknown_Invalid
);
1747 Determine if the first item in a command line is valid.
1749 @param[in] CmdLine The command line to parse.
1751 @retval EFI_SUCCESS The item is valid.
1752 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1753 @retval EFI_NOT_FOUND The operation type is unknown or invalid.
1758 IN CONST CHAR16
*CmdLine
1762 CHAR16
*FirstParameter
;
1768 Temp
= StrnCatGrow(&Temp
, NULL
, CmdLine
, 0);
1770 return (EFI_OUT_OF_RESOURCES
);
1773 FirstParameter
= StrStr(Temp
, L
"|");
1774 if (FirstParameter
!= NULL
) {
1775 *FirstParameter
= CHAR_NULL
;
1778 FirstParameter
= NULL
;
1781 // Process the command line
1783 Status
= ProcessCommandLineToFinal(&Temp
);
1785 if (!EFI_ERROR(Status
)) {
1786 FirstParameter
= AllocateZeroPool(StrSize(CmdLine
));
1787 if (FirstParameter
== NULL
) {
1788 SHELL_FREE_NON_NULL(Temp
);
1789 return (EFI_OUT_OF_RESOURCES
);
1791 TempWalker
= (CHAR16
*)Temp
;
1792 GetNextParameter(&TempWalker
, &FirstParameter
, StrSize(CmdLine
));
1794 if (GetOperationType(FirstParameter
) == Unknown_Invalid
) {
1795 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
1796 SetLastError(SHELL_NOT_FOUND
);
1797 Status
= EFI_NOT_FOUND
;
1801 SHELL_FREE_NON_NULL(Temp
);
1802 SHELL_FREE_NON_NULL(FirstParameter
);
1807 Determine if a command line contains with a split contains only valid commands.
1809 @param[in] CmdLine The command line to parse.
1811 @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.
1812 @retval EFI_ABORTED CmdLine has at least one invalid command or application.
1817 IN CONST CHAR16
*CmdLine
1820 CONST CHAR16
*TempSpot
;
1824 // Verify up to the pipe or end character
1826 Status
= IsValidSplit(CmdLine
);
1827 if (EFI_ERROR(Status
)) {
1832 // If this was the only item, then get out
1834 if (!ContainsSplit(CmdLine
)) {
1835 return (EFI_SUCCESS
);
1839 // recurse to verify the next item
1841 TempSpot
= FindSplit(CmdLine
)+1;
1842 return (VerifySplit(TempSpot
));
1846 Process a split based operation.
1848 @param[in] CmdLine pointer to the command line to process
1850 @retval EFI_SUCCESS The operation was successful
1851 @return an error occured.
1855 ProcessNewSplitCommandLine(
1856 IN CONST CHAR16
*CmdLine
1862 Status
= VerifySplit(CmdLine
);
1863 if (EFI_ERROR(Status
)) {
1870 // are we in an existing split???
1872 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
1873 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
1876 if (Split
== NULL
) {
1877 Status
= RunSplitCommand(CmdLine
, NULL
, NULL
);
1879 Status
= RunSplitCommand(CmdLine
, Split
->SplitStdIn
, Split
->SplitStdOut
);
1881 if (EFI_ERROR(Status
)) {
1882 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_SPLIT
), ShellInfoObject
.HiiHandle
, CmdLine
);
1888 Handle a request to change the current file system.
1890 @param[in] CmdLine The passed in command line.
1892 @retval EFI_SUCCESS The operation was successful.
1897 IN CONST CHAR16
*CmdLine
1901 Status
= EFI_SUCCESS
;
1904 // make sure we are the right operation
1906 ASSERT(CmdLine
[(StrLen(CmdLine
)-1)] == L
':' && StrStr(CmdLine
, L
" ") == NULL
);
1909 // Call the protocol API to do the work
1911 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, CmdLine
);
1914 // Report any errors
1916 if (EFI_ERROR(Status
)) {
1917 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, CmdLine
);
1924 Reprocess the command line to direct all -? to the help command.
1926 if found, will add "help" as argv[0], and move the rest later.
1928 @param[in,out] CmdLine pointer to the command line to update
1933 IN OUT CHAR16
**CmdLine
1936 CHAR16
*CurrentParameter
;
1939 CHAR16
*NewCommandLine
;
1942 Status
= EFI_SUCCESS
;
1944 CurrentParameter
= AllocateZeroPool(StrSize(*CmdLine
));
1945 if (CurrentParameter
== NULL
) {
1946 return (EFI_OUT_OF_RESOURCES
);
1950 while(Walker
!= NULL
&& *Walker
!= CHAR_NULL
) {
1951 LastWalker
= Walker
;
1952 GetNextParameter(&Walker
, &CurrentParameter
, StrSize(*CmdLine
));
1953 if (StrStr(CurrentParameter
, L
"-?") == CurrentParameter
) {
1954 LastWalker
[0] = L
' ';
1955 LastWalker
[1] = L
' ';
1956 NewCommandLine
= AllocateZeroPool(StrSize(L
"help ") + StrSize(*CmdLine
));
1957 if (NewCommandLine
== NULL
) {
1958 Status
= EFI_OUT_OF_RESOURCES
;
1963 // We know the space is sufficient since we just calculated it.
1965 StrnCpy(NewCommandLine
, L
"help ", 5);
1966 StrnCat(NewCommandLine
, *CmdLine
, StrLen(*CmdLine
));
1967 SHELL_FREE_NON_NULL(*CmdLine
);
1968 *CmdLine
= NewCommandLine
;
1973 SHELL_FREE_NON_NULL(CurrentParameter
);
1979 Function to update the shell variable "lasterror".
1981 @param[in] ErrorCode the error code to put into lasterror.
1986 IN CONST SHELL_STATUS ErrorCode
1989 CHAR16 LeString
[19];
1990 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
1991 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ErrorCode
);
1993 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", ErrorCode
);
1995 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
1996 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
1998 return (EFI_SUCCESS
);
2002 Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.
2004 @param[in,out] CmdLine pointer to the command line to update
2006 @retval EFI_SUCCESS The operation was successful
2007 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
2008 @return some other error occured
2012 ProcessCommandLineToFinal(
2013 IN OUT CHAR16
**CmdLine
2017 TrimSpaces(CmdLine
);
2019 Status
= ShellSubstituteAliases(CmdLine
);
2020 if (EFI_ERROR(Status
)) {
2024 TrimSpaces(CmdLine
);
2026 Status
= ShellSubstituteVariables(CmdLine
);
2027 if (EFI_ERROR(Status
)) {
2030 ASSERT (*CmdLine
!= NULL
);
2032 TrimSpaces(CmdLine
);
2035 // update for help parsing
2037 if (StrStr(*CmdLine
, L
"?") != NULL
) {
2039 // This may do nothing if the ? does not indicate help.
2040 // Save all the details for in the API below.
2042 Status
= DoHelpUpdate(CmdLine
);
2045 TrimSpaces(CmdLine
);
2047 return (EFI_SUCCESS
);
2051 Run an internal shell command.
2053 This API will upadate the shell's environment since these commands are libraries.
2055 @param[in] CmdLine the command line to run.
2056 @param[in] FirstParameter the first parameter on the command line
2057 @param[in] ParamProtocol the shell parameters protocol pointer
2059 @retval EFI_SUCCESS The command was completed.
2060 @retval EFI_ABORTED The command's operation was aborted.
2065 IN CONST CHAR16
*CmdLine
,
2066 IN CHAR16
*FirstParameter
,
2067 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
2073 SHELL_STATUS CommandReturnedStatus
;
2078 NewCmdLine
= AllocateCopyPool (StrSize (CmdLine
), CmdLine
);
2079 if (NewCmdLine
== NULL
) {
2080 return EFI_OUT_OF_RESOURCES
;
2083 for (Walker
= NewCmdLine
; Walker
!= NULL
&& *Walker
!= CHAR_NULL
; Walker
++) {
2084 if (*Walker
== L
'^' && *(Walker
+1) == L
'#') {
2085 CopyMem(Walker
, Walker
+1, StrSize(Walker
) - sizeof(Walker
[0]));
2090 // get the argc and argv updated for internal commands
2092 Status
= UpdateArgcArgv(ParamProtocol
, NewCmdLine
, &Argv
, &Argc
);
2093 if (!EFI_ERROR(Status
)) {
2095 // Run the internal command.
2097 Status
= ShellCommandRunCommandHandler(FirstParameter
, &CommandReturnedStatus
, &LastError
);
2099 if (!EFI_ERROR(Status
)) {
2101 // Update last error status.
2102 // some commands do not update last error.
2105 SetLastError(CommandReturnedStatus
);
2109 // Pass thru the exitcode from the app.
2111 if (ShellCommandGetExit()) {
2113 // An Exit was requested ("exit" command), pass its value up.
2115 Status
= CommandReturnedStatus
;
2116 } else if (CommandReturnedStatus
!= SHELL_SUCCESS
&& IsScriptOnlyCommand(FirstParameter
)) {
2118 // Always abort when a script only command fails for any reason
2120 Status
= EFI_ABORTED
;
2121 } else if (ShellCommandGetCurrentScriptFile() != NULL
&& CommandReturnedStatus
== SHELL_ABORTED
) {
2123 // Abort when in a script and a command aborted
2125 Status
= EFI_ABORTED
;
2131 // This is guarenteed to be called after UpdateArgcArgv no matter what else happened.
2132 // This is safe even if the update API failed. In this case, it may be a no-op.
2134 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);
2137 // If a script is running and the command is not a scipt only command, then
2138 // change return value to success so the script won't halt (unless aborted).
2140 // Script only commands have to be able halt the script since the script will
2141 // not operate if they are failing.
2143 if ( ShellCommandGetCurrentScriptFile() != NULL
2144 && !IsScriptOnlyCommand(FirstParameter
)
2145 && Status
!= EFI_ABORTED
2147 Status
= EFI_SUCCESS
;
2150 FreePool (NewCmdLine
);
2155 Function to run the command or file.
2157 @param[in] Type the type of operation being run.
2158 @param[in] CmdLine the command line to run.
2159 @param[in] FirstParameter the first parameter on the command line
2160 @param[in] ParamProtocol the shell parameters protocol pointer
2162 @retval EFI_SUCCESS The command was completed.
2163 @retval EFI_ABORTED The command's operation was aborted.
2168 IN SHELL_OPERATION_TYPES Type
,
2169 IN CONST CHAR16
*CmdLine
,
2170 IN CHAR16
*FirstParameter
,
2171 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
2175 EFI_STATUS StartStatus
;
2176 CHAR16
*CommandWithPath
;
2177 EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
2178 SHELL_STATUS CalleeExitStatus
;
2180 Status
= EFI_SUCCESS
;
2181 CommandWithPath
= NULL
;
2183 CalleeExitStatus
= SHELL_INVALID_PARAMETER
;
2186 case Internal_Command
:
2187 Status
= RunInternalCommand(CmdLine
, FirstParameter
, ParamProtocol
);
2189 case Script_File_Name
:
2190 case Efi_Application
:
2192 // Process a fully qualified path
2194 if (StrStr(FirstParameter
, L
":") != NULL
) {
2195 ASSERT (CommandWithPath
== NULL
);
2196 if (ShellIsFile(FirstParameter
) == EFI_SUCCESS
) {
2197 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, FirstParameter
, 0);
2202 // Process a relative path and also check in the path environment variable
2204 if (CommandWithPath
== NULL
) {
2205 CommandWithPath
= ShellFindFilePathEx(FirstParameter
, mExecutableExtensions
);
2209 // This should be impossible now.
2211 ASSERT(CommandWithPath
!= NULL
);
2214 // Make sure that path is not just a directory (or not found)
2216 if (!EFI_ERROR(ShellIsDirectory(CommandWithPath
))) {
2217 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2218 SetLastError(SHELL_NOT_FOUND
);
2221 case Script_File_Name
:
2222 Status
= RunScriptFile (CommandWithPath
, NULL
, CmdLine
, ParamProtocol
);
2224 case Efi_Application
:
2226 // Get the device path of the application image
2228 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
2229 if (DevPath
== NULL
){
2230 Status
= EFI_OUT_OF_RESOURCES
;
2235 // Execute the device path
2237 Status
= InternalShellExecuteDevicePath(
2245 SHELL_FREE_NON_NULL(DevPath
);
2247 if(EFI_ERROR (Status
)) {
2248 CalleeExitStatus
= (SHELL_STATUS
) (Status
& (~MAX_BIT
));
2250 CalleeExitStatus
= (SHELL_STATUS
) StartStatus
;
2254 // Update last error status.
2256 // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
2257 SetLastError(CalleeExitStatus
);
2273 SHELL_FREE_NON_NULL(CommandWithPath
);
2279 Function to setup StdIn, StdErr, StdOut, and then run the command or file.
2281 @param[in] Type the type of operation being run.
2282 @param[in] CmdLine the command line to run.
2283 @param[in] FirstParameter the first parameter on the command line.
2284 @param[in] ParamProtocol the shell parameters protocol pointer
2286 @retval EFI_SUCCESS The command was completed.
2287 @retval EFI_ABORTED The command's operation was aborted.
2291 SetupAndRunCommandOrFile(
2292 IN SHELL_OPERATION_TYPES Type
,
2294 IN CHAR16
*FirstParameter
,
2295 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
2299 SHELL_FILE_HANDLE OriginalStdIn
;
2300 SHELL_FILE_HANDLE OriginalStdOut
;
2301 SHELL_FILE_HANDLE OriginalStdErr
;
2302 SYSTEM_TABLE_INFO OriginalSystemTableInfo
;
2305 // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
2307 Status
= UpdateStdInStdOutStdErr(ParamProtocol
, CmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2310 // The StdIn, StdOut, and StdErr are set up.
2311 // Now run the command, script, or application
2313 if (!EFI_ERROR(Status
)) {
2314 TrimSpaces(&CmdLine
);
2315 Status
= RunCommandOrFile(Type
, CmdLine
, FirstParameter
, ParamProtocol
);
2321 if (EFI_ERROR(Status
)) {
2322 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
));
2326 // put back the original StdIn, StdOut, and StdErr
2328 RestoreStdInStdOutStdErr(ParamProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2334 Function will process and run a command line.
2336 This will determine if the command line represents an internal shell
2337 command or dispatch an external application.
2339 @param[in] CmdLine The command line to parse.
2341 @retval EFI_SUCCESS The command was completed.
2342 @retval EFI_ABORTED The command's operation was aborted.
2347 IN CONST CHAR16
*CmdLine
2351 CHAR16
*CleanOriginal
;
2352 CHAR16
*FirstParameter
;
2354 SHELL_OPERATION_TYPES Type
;
2356 ASSERT(CmdLine
!= NULL
);
2357 if (StrLen(CmdLine
) == 0) {
2358 return (EFI_SUCCESS
);
2361 Status
= EFI_SUCCESS
;
2362 CleanOriginal
= NULL
;
2364 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
2365 if (CleanOriginal
== NULL
) {
2366 return (EFI_OUT_OF_RESOURCES
);
2369 TrimSpaces(&CleanOriginal
);
2372 // NULL out comments (leveraged from RunScriptFileHandle() ).
2373 // The # character on a line is used to denote that all characters on the same line
2374 // and to the right of the # are to be ignored by the shell.
2375 // Afterward, again remove spaces, in case any were between the last command-parameter and '#'.
2377 for (TempWalker
= CleanOriginal
; TempWalker
!= NULL
&& *TempWalker
!= CHAR_NULL
; TempWalker
++) {
2378 if (*TempWalker
== L
'^') {
2379 if (*(TempWalker
+ 1) == L
'#') {
2382 } else if (*TempWalker
== L
'#') {
2383 *TempWalker
= CHAR_NULL
;
2387 TrimSpaces(&CleanOriginal
);
2390 // Handle case that passed in command line is just 1 or more " " characters.
2392 if (StrLen (CleanOriginal
) == 0) {
2393 SHELL_FREE_NON_NULL(CleanOriginal
);
2394 return (EFI_SUCCESS
);
2397 Status
= ProcessCommandLineToFinal(&CleanOriginal
);
2398 if (EFI_ERROR(Status
)) {
2399 SHELL_FREE_NON_NULL(CleanOriginal
);
2404 // We dont do normal processing with a split command line (output from one command input to another)
2406 if (ContainsSplit(CleanOriginal
)) {
2407 Status
= ProcessNewSplitCommandLine(CleanOriginal
);
2408 SHELL_FREE_NON_NULL(CleanOriginal
);
2413 // We need the first parameter information so we can determine the operation type
2415 FirstParameter
= AllocateZeroPool(StrSize(CleanOriginal
));
2416 if (FirstParameter
== NULL
) {
2417 SHELL_FREE_NON_NULL(CleanOriginal
);
2418 return (EFI_OUT_OF_RESOURCES
);
2420 TempWalker
= CleanOriginal
;
2421 GetNextParameter(&TempWalker
, &FirstParameter
, StrSize(CleanOriginal
));
2424 // Depending on the first parameter we change the behavior
2426 switch (Type
= GetOperationType(FirstParameter
)) {
2427 case File_Sys_Change
:
2428 Status
= ChangeMappedDrive (FirstParameter
);
2430 case Internal_Command
:
2431 case Script_File_Name
:
2432 case Efi_Application
:
2433 Status
= SetupAndRunCommandOrFile(Type
, CleanOriginal
, FirstParameter
, ShellInfoObject
.NewShellParametersProtocol
);
2437 // Whatever was typed, it was invalid.
2439 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2440 SetLastError(SHELL_NOT_FOUND
);
2444 SHELL_FREE_NON_NULL(CleanOriginal
);
2445 SHELL_FREE_NON_NULL(FirstParameter
);
2450 STATIC CONST UINT16 InvalidChars
[] = {L
'*', L
'?', L
'<', L
'>', L
'\\', L
'/', L
'\"', 0x0001, 0x0002};
2452 Function determins if the CommandName COULD be a valid command. It does not determine whether
2453 this is a valid command. It only checks for invalid characters.
2455 @param[in] CommandName The name to check
2457 @retval TRUE CommandName could be a command name
2458 @retval FALSE CommandName could not be a valid command name
2463 IN CONST CHAR16
*CommandName
2467 if (CommandName
== NULL
) {
2472 ; Count
< sizeof(InvalidChars
) / sizeof(InvalidChars
[0])
2475 if (ScanMem16(CommandName
, StrSize(CommandName
), InvalidChars
[Count
]) != NULL
) {
2483 Function to process a NSH script file via SHELL_FILE_HANDLE.
2485 @param[in] Handle The handle to the already opened file.
2486 @param[in] Name The name of the script file.
2488 @retval EFI_SUCCESS the script completed sucessfully
2492 RunScriptFileHandle (
2493 IN SHELL_FILE_HANDLE Handle
,
2494 IN CONST CHAR16
*Name
2498 SCRIPT_FILE
*NewScriptFile
;
2500 CHAR16
*CommandLine
;
2501 CHAR16
*CommandLine2
;
2502 CHAR16
*CommandLine3
;
2503 SCRIPT_COMMAND_LIST
*LastCommand
;
2505 BOOLEAN PreScriptEchoState
;
2506 BOOLEAN PreCommandEchoState
;
2507 CONST CHAR16
*CurDir
;
2509 CHAR16 LeString
[50];
2511 ASSERT(!ShellCommandGetScriptExit());
2513 PreScriptEchoState
= ShellCommandGetEchoState();
2515 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
2516 if (NewScriptFile
== NULL
) {
2517 return (EFI_OUT_OF_RESOURCES
);
2523 ASSERT(NewScriptFile
->ScriptName
== NULL
);
2524 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
2525 if (NewScriptFile
->ScriptName
== NULL
) {
2526 DeleteScriptFileStruct(NewScriptFile
);
2527 return (EFI_OUT_OF_RESOURCES
);
2531 // Save the parameters (used to replace %0 to %9 later on)
2533 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
2534 if (NewScriptFile
->Argc
!= 0) {
2535 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
2536 if (NewScriptFile
->Argv
== NULL
) {
2537 DeleteScriptFileStruct(NewScriptFile
);
2538 return (EFI_OUT_OF_RESOURCES
);
2540 for (LoopVar
= 0 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
2541 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
2542 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
2543 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
2544 DeleteScriptFileStruct(NewScriptFile
);
2545 return (EFI_OUT_OF_RESOURCES
);
2549 NewScriptFile
->Argv
= NULL
;
2552 InitializeListHead(&NewScriptFile
->CommandList
);
2553 InitializeListHead(&NewScriptFile
->SubstList
);
2556 // Now build the list of all script commands.
2559 while(!ShellFileHandleEof(Handle
)) {
2560 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
2562 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0 || CommandLine
[0] == '#') {
2563 SHELL_FREE_NON_NULL(CommandLine
);
2566 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
2567 if (NewScriptFile
->CurrentCommand
== NULL
) {
2568 SHELL_FREE_NON_NULL(CommandLine
);
2569 DeleteScriptFileStruct(NewScriptFile
);
2570 return (EFI_OUT_OF_RESOURCES
);
2573 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
2574 NewScriptFile
->CurrentCommand
->Data
= NULL
;
2575 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
2577 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2581 // Add this as the topmost script file
2583 ShellCommandSetNewScript (NewScriptFile
);
2586 // Now enumerate through the commands and run each one.
2588 CommandLine
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
2589 if (CommandLine
== NULL
) {
2590 DeleteScriptFileStruct(NewScriptFile
);
2591 return (EFI_OUT_OF_RESOURCES
);
2593 CommandLine2
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
2594 if (CommandLine2
== NULL
) {
2595 FreePool(CommandLine
);
2596 DeleteScriptFileStruct(NewScriptFile
);
2597 return (EFI_OUT_OF_RESOURCES
);
2600 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
2601 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
2602 ; // conditional increment in the body of the loop
2604 ASSERT(CommandLine2
!= NULL
);
2605 StrnCpy(CommandLine2
, NewScriptFile
->CurrentCommand
->Cl
, PcdGet16(PcdShellPrintBufferSize
)/sizeof(CHAR16
)-1);
2608 // NULL out comments
2610 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
2611 if (*CommandLine3
== L
'^') {
2612 if ( *(CommandLine3
+1) == L
':') {
2613 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
2614 } else if (*(CommandLine3
+1) == L
'#') {
2617 } else if (*CommandLine3
== L
'#') {
2618 *CommandLine3
= CHAR_NULL
;
2622 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
2624 // Due to variability in starting the find and replace action we need to have both buffers the same.
2626 StrnCpy(CommandLine
, CommandLine2
, PcdGet16(PcdShellPrintBufferSize
)/sizeof(CHAR16
)-1);
2629 // Remove the %0 to %9 from the command line (if we have some arguments)
2631 if (NewScriptFile
->Argv
!= NULL
) {
2632 switch (NewScriptFile
->Argc
) {
2634 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", NewScriptFile
->Argv
[9], FALSE
, FALSE
);
2635 ASSERT_EFI_ERROR(Status
);
2637 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", NewScriptFile
->Argv
[8], FALSE
, FALSE
);
2638 ASSERT_EFI_ERROR(Status
);
2640 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", NewScriptFile
->Argv
[7], FALSE
, FALSE
);
2641 ASSERT_EFI_ERROR(Status
);
2643 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", NewScriptFile
->Argv
[6], FALSE
, FALSE
);
2644 ASSERT_EFI_ERROR(Status
);
2646 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", NewScriptFile
->Argv
[5], FALSE
, FALSE
);
2647 ASSERT_EFI_ERROR(Status
);
2649 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", NewScriptFile
->Argv
[4], FALSE
, FALSE
);
2650 ASSERT_EFI_ERROR(Status
);
2652 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", NewScriptFile
->Argv
[3], FALSE
, FALSE
);
2653 ASSERT_EFI_ERROR(Status
);
2655 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", NewScriptFile
->Argv
[2], FALSE
, FALSE
);
2656 ASSERT_EFI_ERROR(Status
);
2658 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", NewScriptFile
->Argv
[1], FALSE
, FALSE
);
2659 ASSERT_EFI_ERROR(Status
);
2661 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%0", NewScriptFile
->Argv
[0], FALSE
, FALSE
);
2662 ASSERT_EFI_ERROR(Status
);
2668 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", L
"\"\"", FALSE
, FALSE
);
2669 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", L
"\"\"", FALSE
, FALSE
);
2670 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", L
"\"\"", FALSE
, FALSE
);
2671 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", L
"\"\"", FALSE
, FALSE
);
2672 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", L
"\"\"", FALSE
, FALSE
);
2673 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", L
"\"\"", FALSE
, FALSE
);
2674 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", L
"\"\"", FALSE
, FALSE
);
2675 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", L
"\"\"", FALSE
, FALSE
);
2676 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", L
"\"\"", FALSE
, FALSE
);
2678 StrnCpy(CommandLine2
, CommandLine
, PcdGet16(PcdShellPrintBufferSize
)/sizeof(CHAR16
)-1);
2680 LastCommand
= NewScriptFile
->CurrentCommand
;
2682 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
2684 if (CommandLine3
!= NULL
&& CommandLine3
[0] == L
':' ) {
2686 // This line is a goto target / label
2689 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
2690 if (CommandLine3
[0] == L
'@') {
2692 // We need to save the current echo state
2693 // and disable echo for just this command.
2695 PreCommandEchoState
= ShellCommandGetEchoState();
2696 ShellCommandSetEchoState(FALSE
);
2697 Status
= RunCommand(CommandLine3
+1);
2700 // If command was "@echo -off" or "@echo -on" then don't restore echo state
2702 if (StrCmp (L
"@echo -off", CommandLine3
) != 0 &&
2703 StrCmp (L
"@echo -on", CommandLine3
) != 0) {
2705 // Now restore the pre-'@' echo state.
2707 ShellCommandSetEchoState(PreCommandEchoState
);
2710 if (ShellCommandGetEchoState()) {
2711 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
2712 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
2713 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
2715 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
2717 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
2719 Status
= RunCommand(CommandLine3
);
2723 if (ShellCommandGetScriptExit()) {
2725 // ShellCommandGetExitCode() always returns a UINT64
2727 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ShellCommandGetExitCode());
2728 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
2729 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
2731 ShellCommandRegisterExit(FALSE
, 0);
2732 Status
= EFI_SUCCESS
;
2735 if (ShellGetExecutionBreakFlag()) {
2738 if (EFI_ERROR(Status
)) {
2741 if (ShellCommandGetExit()) {
2746 // If that commend did not update the CurrentCommand then we need to advance it...
2748 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
2749 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2750 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2751 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2755 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2756 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2757 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2763 FreePool(CommandLine
);
2764 FreePool(CommandLine2
);
2765 ShellCommandSetNewScript (NULL
);
2768 // Only if this was the last script reset the state.
2770 if (ShellCommandGetCurrentScriptFile()==NULL
) {
2771 ShellCommandSetEchoState(PreScriptEchoState
);
2773 return (EFI_SUCCESS
);
2777 Function to process a NSH script file.
2779 @param[in] ScriptPath Pointer to the script file name (including file system path).
2780 @param[in] Handle the handle of the script file already opened.
2781 @param[in] CmdLine the command line to run.
2782 @param[in] ParamProtocol the shell parameters protocol pointer
2784 @retval EFI_SUCCESS the script completed sucessfully
2789 IN CONST CHAR16
*ScriptPath
,
2790 IN SHELL_FILE_HANDLE Handle OPTIONAL
,
2791 IN CONST CHAR16
*CmdLine
,
2792 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
2796 SHELL_FILE_HANDLE FileHandle
;
2800 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
2801 return (EFI_INVALID_PARAMETER
);
2805 // get the argc and argv updated for scripts
2807 Status
= UpdateArgcArgv(ParamProtocol
, CmdLine
, &Argv
, &Argc
);
2808 if (!EFI_ERROR(Status
)) {
2810 if (Handle
== NULL
) {
2814 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
2815 if (!EFI_ERROR(Status
)) {
2819 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
);
2822 // now close the file
2824 ShellCloseFile(&FileHandle
);
2827 Status
= RunScriptFileHandle(Handle
, ScriptPath
);
2832 // This is guarenteed to be called after UpdateArgcArgv no matter what else happened.
2833 // This is safe even if the update API failed. In this case, it may be a no-op.
2835 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);