2 This is THE shell (application)
4 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2013, 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
;
249 SHELL_STATUS ExitStatus
;
251 if (PcdGet8(PcdShellSupportLevel
) > 3) {
252 return (EFI_UNSUPPORTED
);
258 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
259 if (EFI_ERROR(Status
)) {
264 // Populate the global structure from PCDs
266 ShellInfoObject
.ImageDevPath
= NULL
;
267 ShellInfoObject
.FileDevPath
= NULL
;
268 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
269 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
270 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
273 // verify we dont allow for spec violation
275 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
278 // Initialize the LIST ENTRY objects...
280 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
281 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
282 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
285 // Check PCDs for optional features that are not implemented yet.
287 if ( PcdGetBool(PcdShellSupportOldProtocols
)
288 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
289 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
291 return (EFI_UNSUPPORTED
);
295 // turn off the watchdog timer
297 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
300 // install our console logger. This will keep a log of the output for back-browsing
302 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
303 if(EFI_ERROR (Status
)) {
304 ExitStatus
= (SHELL_STATUS
) (Status
& (~MAX_BIT
));
306 ExitStatus
= SHELL_SUCCESS
;
309 if (!EFI_ERROR(Status
)) {
311 // Enable the cursor to be visible
313 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
316 // If supporting EFI 1.1 we need to install HII protocol
317 // only do this if PcdShellRequireHiiPlatform == FALSE
319 // remove EFI_UNSUPPORTED check above when complete.
320 ///@todo add support for Framework HII
323 // install our (solitary) HII package
325 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
326 if (ShellInfoObject
.HiiHandle
== NULL
) {
327 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
328 ///@todo Add our package into Framework HII
330 if (ShellInfoObject
.HiiHandle
== NULL
) {
331 Status
= EFI_NOT_STARTED
;
337 // create and install the EfiShellParametersProtocol
339 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
340 ASSERT_EFI_ERROR(Status
);
341 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
344 // create and install the EfiShellProtocol
346 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
347 ASSERT_EFI_ERROR(Status
);
348 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
351 // Now initialize the shell library (it requires Shell Parameters protocol)
353 Status
= ShellInitialize();
354 ASSERT_EFI_ERROR(Status
);
356 Status
= CommandInit();
357 ASSERT_EFI_ERROR(Status
);
360 // Check the command line
362 Status
= ProcessCommandLine();
365 // If shell support level is >= 1 create the mappings and paths
367 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
368 Status
= ShellCommandCreateInitialMappingsAndPaths();
372 // save the device path for the loaded image and the device path for the filepath (under loaded image)
373 // These are where to look for the startup.nsh file
375 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
376 ASSERT_EFI_ERROR(Status
);
379 // Display the version
381 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
384 gST
->ConOut
->Mode
->CursorRow
,
386 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL
),
387 ShellInfoObject
.HiiHandle
,
388 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
389 gEfiShellProtocol
->MajorVersion
,
390 gEfiShellProtocol
->MinorVersion
397 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER
),
398 ShellInfoObject
.HiiHandle
,
399 (CHAR16
*) PcdGetPtr (PcdShellSupplier
)
406 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI
),
407 ShellInfoObject
.HiiHandle
,
408 (gST
->Hdr
.Revision
&0xffff0000)>>16,
409 (gST
->Hdr
.Revision
&0x0000ffff),
411 gST
->FirmwareRevision
416 // Display the mapping
418 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
419 Status
= RunCommand(L
"map", NULL
);
420 ASSERT_EFI_ERROR(Status
);
424 // init all the built in alias'
426 Status
= SetBuiltInAlias();
427 ASSERT_EFI_ERROR(Status
);
430 // Initialize environment variables
432 if (ShellCommandGetProfileList() != NULL
) {
433 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
434 ASSERT_EFI_ERROR(Status
);
438 TempString
= AllocateZeroPool(Size
);
440 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
441 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
442 ASSERT_EFI_ERROR(Status
);
444 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
445 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
446 ASSERT_EFI_ERROR(Status
);
448 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
449 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
450 ASSERT_EFI_ERROR(Status
);
452 FreePool(TempString
);
454 if (!EFI_ERROR(Status
)) {
455 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
457 // Set up the event for CTRL-C monitoring...
459 Status
= InernalEfiShellStartMonitor();
462 if (!EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
464 // Set up the event for CTRL-S monitoring...
466 Status
= InternalEfiShellStartCtrlSMonitor();
469 if (!EFI_ERROR(Status
) && ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
471 // close off the gST->ConIn
473 OldConIn
= gST
->ConIn
;
474 ConInHandle
= gST
->ConsoleInHandle
;
475 gST
->ConIn
= CreateSimpleTextInOnFile((SHELL_FILE_HANDLE
)&FileInterfaceNulFile
, &gST
->ConsoleInHandle
);
481 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
483 // process the startup script or launch the called app.
485 Status
= DoStartupScript(
486 ShellInfoObject
.ImageDevPath
,
487 ShellInfoObject
.FileDevPath
,
492 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
&& !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
494 // begin the UI waiting loop
498 // clean out all the memory allocated for CONST <something> * return values
499 // between each shell prompt presentation
501 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
502 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
506 // Reset page break back to default.
508 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
509 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
510 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
513 // Reset the CTRL-C event (yes we ignore the return values)
515 Status
= gBS
->CheckEvent (ShellInfoObject
.NewEfiShellProtocol
->ExecutionBreak
);
520 Status
= DoShellPrompt();
521 } while (!ShellCommandGetExit());
522 ExitStatus
= (SHELL_STATUS
) ShellCommandGetExitCode();
524 if (OldConIn
!= NULL
&& ConInHandle
!= NULL
) {
525 CloseSimpleTextInOnFile (gST
->ConIn
);
526 gST
->ConIn
= OldConIn
;
527 gST
->ConsoleInHandle
= ConInHandle
;
534 // uninstall protocols / free memory / etc...
536 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
537 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
538 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
540 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
541 FreePool(ShellInfoObject
.ImageDevPath
);
542 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
544 if (ShellInfoObject
.FileDevPath
!= NULL
) {
545 FreePool(ShellInfoObject
.FileDevPath
);
546 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
548 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
549 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
550 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
552 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
553 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
554 InternalEfiShellSetEnv(L
"cwd", NULL
, TRUE
);
556 CleanUpShellProtocol(ShellInfoObject
.NewEfiShellProtocol
);
557 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
560 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
561 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
564 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
565 ASSERT(FALSE
); ///@todo finish this de-allocation.
568 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
569 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
570 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
573 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
574 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
575 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
578 if (ShellInfoObject
.HiiHandle
!= NULL
) {
579 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
580 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
583 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
584 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
587 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
588 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
589 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
590 FreePool(ShellInfoObject
.ConsoleInfo
);
591 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
594 if (!EFI_ERROR (Status
)) {
595 // If the command exited with an error, we pass this error out in the ExitData
596 // so that it can be retrieved by the EfiShellExecute function (which may
597 // start the shell with gBS->StartImage)
598 if (ExitStatus
!= SHELL_SUCCESS
) {
599 // Allocate a buffer for exit data to pass to gBS->Exit().
600 // This buffer will contain the empty string immediately followed by
601 // the shell's exit status. (The empty string is required by the UEFI spec)
602 ExitDataSize
= (sizeof (CHAR16
) + sizeof (SHELL_STATUS
));
603 ExitData
= AllocatePool (ExitDataSize
);
604 if (ExitData
== NULL
) {
605 return EFI_OUT_OF_RESOURCES
;
608 // Use CopyMem to avoid alignment faults
609 CopyMem ((ExitData
+ 1), &ExitStatus
, sizeof (ExitStatus
));
611 gBS
->Exit (ImageHandle
, EFI_ABORTED
, ExitDataSize
, ExitData
);
624 Sets all the alias' that were registered with the ShellCommandLib library.
626 @retval EFI_SUCCESS all init commands were run sucessfully.
634 CONST ALIAS_LIST
*List
;
638 // Get all the commands we want to run
640 List
= ShellCommandGetInitAliasList();
643 // for each command in the List
645 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
646 ; !IsNull (&List
->Link
, &Node
->Link
)
647 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
650 // install the alias'
652 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
653 ASSERT_EFI_ERROR(Status
);
655 return (EFI_SUCCESS
);
659 Internal function to determine if 2 command names are really the same.
661 @param[in] Command1 The pointer to the first command name.
662 @param[in] Command2 The pointer to the second command name.
664 @retval TRUE The 2 command names are the same.
665 @retval FALSE The 2 command names are not the same.
670 IN CONST CHAR16
*Command1
,
671 IN CONST CHAR16
*Command2
674 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
681 Internal function to determine if a command is a script only command.
683 @param[in] CommandName The pointer to the command name.
685 @retval TRUE The command is a script only command.
686 @retval FALSE The command is not a script only command.
691 IN CONST CHAR16
*CommandName
694 if (IsCommand(CommandName
, L
"for")
695 ||IsCommand(CommandName
, L
"endfor")
696 ||IsCommand(CommandName
, L
"if")
697 ||IsCommand(CommandName
, L
"else")
698 ||IsCommand(CommandName
, L
"endif")
699 ||IsCommand(CommandName
, L
"goto")) {
706 This function will populate the 2 device path protocol parameters based on the
707 global gImageHandle. The DevPath will point to the device path for the handle that has
708 loaded image protocol installed on it. The FilePath will point to the device path
709 for the file that was loaded.
711 @param[in, out] DevPath On a sucessful return the device path to the loaded image.
712 @param[in, out] FilePath On a sucessful return the device path to the file.
714 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
715 @retval other A error from gBS->HandleProtocol.
721 GetDevicePathsForImageAndFile (
722 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
723 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
727 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
728 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
730 ASSERT(DevPath
!= NULL
);
731 ASSERT(FilePath
!= NULL
);
733 Status
= gBS
->OpenProtocol (
735 &gEfiLoadedImageProtocolGuid
,
736 (VOID
**)&LoadedImage
,
739 EFI_OPEN_PROTOCOL_GET_PROTOCOL
741 if (!EFI_ERROR (Status
)) {
742 Status
= gBS
->OpenProtocol (
743 LoadedImage
->DeviceHandle
,
744 &gEfiDevicePathProtocolGuid
,
745 (VOID
**)&ImageDevicePath
,
748 EFI_OPEN_PROTOCOL_GET_PROTOCOL
750 if (!EFI_ERROR (Status
)) {
751 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
752 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
754 LoadedImage
->DeviceHandle
,
755 &gEfiDevicePathProtocolGuid
,
761 &gEfiLoadedImageProtocolGuid
,
769 Process all Uefi Shell 2.0 command line options.
771 see Uefi Shell 2.0 section 3.2 for full details.
773 the command line must resemble the following:
775 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
777 ShellOpt-options Options which control the initialization behavior of the shell.
778 These options are read from the EFI global variable "ShellOpt"
779 and are processed before options or file-name.
781 options Options which control the initialization behavior of the shell.
783 file-name The name of a UEFI shell application or script to be executed
784 after initialization is complete. By default, if file-name is
785 specified, then -nostartup is implied. Scripts are not supported
788 file-name-options The command-line options that are passed to file-name when it
791 This will initialize the ShellInfoObject.ShellInitSettings global variable.
793 @retval EFI_SUCCESS The variable is initialized.
804 CHAR16
*DelayValueStr
;
807 EFI_UNICODE_COLLATION_PROTOCOL
*UnicodeCollation
;
809 // `file-name-options` will contain arguments to `file-name` that we don't
810 // know about. This would cause ShellCommandLineParse to error, so we parse
811 // arguments manually, ignoring those after the first thing that doesn't look
812 // like a shell option (which is assumed to be `file-name`).
814 Status
= gBS
->LocateProtocol (
815 &gEfiUnicodeCollationProtocolGuid
,
817 (VOID
**) &UnicodeCollation
819 if (EFI_ERROR (Status
)) {
823 // Set default options
824 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= FALSE
;
825 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= FALSE
;
826 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= FALSE
;
827 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= FALSE
;
828 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= FALSE
;
829 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= FALSE
;
830 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= FALSE
;
831 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= FALSE
;
832 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= FALSE
;
833 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
835 // Start LoopVar at 1 to ignore Argv[0] which is the name of this binary
836 // (probably "Shell.efi")
837 for (LoopVar
= 1 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
838 CurrentArg
= gEfiShellParametersProtocol
->Argv
[LoopVar
];
839 if (UnicodeCollation
->StriColl (
844 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= TRUE
;
846 else if (UnicodeCollation
->StriColl (
851 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= TRUE
;
853 else if (UnicodeCollation
->StriColl (
858 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= TRUE
;
860 else if (UnicodeCollation
->StriColl (
865 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= TRUE
;
867 else if (UnicodeCollation
->StriColl (
872 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= TRUE
;
874 else if (UnicodeCollation
->StriColl (
879 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= TRUE
;
881 else if (UnicodeCollation
->StriColl (
886 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= TRUE
;
888 else if (UnicodeCollation
->StriColl (
893 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= TRUE
;
894 // Check for optional delay value following "-delay"
895 DelayValueStr
= gEfiShellParametersProtocol
->Argv
[LoopVar
+ 1];
896 if (DelayValueStr
!= NULL
){
897 if (*DelayValueStr
== L
':') {
900 if (!EFI_ERROR(ShellConvertStringToUint64 (
906 ShellInfoObject
.ShellInitSettings
.Delay
= (UINTN
)DelayValue
;
910 } else if (UnicodeCollation
->StriColl (
915 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= TRUE
;
916 } else if (StrnCmp (L
"-", CurrentArg
, 1) == 0) {
917 // Unrecognised option
918 ShellPrintHiiEx(-1, -1, NULL
,
919 STRING_TOKEN (STR_GEN_PROBLEM
),
920 ShellInfoObject
.HiiHandle
,
923 return EFI_INVALID_PARAMETER
;
925 ShellInfoObject
.ShellInitSettings
.FileName
= AllocateZeroPool(StrSize(CurrentArg
));
926 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
927 return (EFI_OUT_OF_RESOURCES
);
930 // We found `file-name`.
932 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
934 StrCpy (ShellInfoObject
.ShellInitSettings
.FileName
, CurrentArg
);
937 // Add `file-name-options`
938 for ( ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
939 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
940 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
944 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
945 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
946 return (EFI_OUT_OF_RESOURCES
);
948 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
950 gEfiShellParametersProtocol
->Argv
[LoopVar
],
952 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
953 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
954 return (EFI_OUT_OF_RESOURCES
);
960 // "-nointerrupt" overrides "-delay"
961 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
962 ShellInfoObject
.ShellInitSettings
.Delay
= 0;
969 Handles all interaction with the default startup script.
971 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
973 @param ImagePath the path to the image for shell. first place to look for the startup script
974 @param FilePath the path to the file for shell. second place to look for the startup script.
976 @param[out] ExitStatus The exit code of the script. Ignored if NULL.
978 @retval EFI_SUCCESS the variable is initialized.
983 IN EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
984 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
,
985 OUT SHELL_STATUS
*ExitStatus
991 SHELL_FILE_HANDLE FileHandle
;
992 EFI_DEVICE_PATH_PROTOCOL
*NewPath
;
993 EFI_DEVICE_PATH_PROTOCOL
*NamePath
;
994 CHAR16
*FileStringPath
;
997 CONST CHAR16
*MapName
;
999 Key
.UnicodeChar
= CHAR_NULL
;
1003 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
1005 // launch something else instead
1007 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
1008 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1009 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
1011 FileStringPath
= AllocateZeroPool(NewSize
);
1012 if (FileStringPath
== NULL
) {
1013 return (EFI_OUT_OF_RESOURCES
);
1015 StrCpy(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileName
);
1016 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
1017 StrCat(FileStringPath
, L
" ");
1018 StrCat(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileOptions
);
1020 Status
= RunCommand(FileStringPath
, ExitStatus
);
1021 FreePool(FileStringPath
);
1027 // for shell level 0 we do no scripts
1028 // Without the Startup bit overriding we allow for nostartup to prevent scripts
1030 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
1031 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
1033 return (EFI_SUCCESS
);
1036 gST
->ConOut
->EnableCursor(gST
->ConOut
, FALSE
);
1038 // print out our warning and see if they press a key
1040 for ( Status
= EFI_UNSUPPORTED
, Delay
= ShellInfoObject
.ShellInitSettings
.Delay
1041 ; Delay
!= 0 && EFI_ERROR(Status
)
1044 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
);
1045 gBS
->Stall (1000000);
1046 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
1047 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
1050 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
1051 gST
->ConOut
->EnableCursor(gST
->ConOut
, TRUE
);
1056 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
1057 return (EFI_SUCCESS
);
1061 // Try the first location (must be file system)
1063 MapName
= ShellInfoObject
.NewEfiShellProtocol
->GetMapFromDevicePath(&ImagePath
);
1064 if (MapName
!= NULL
) {
1065 FileStringPath
= NULL
;
1067 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, MapName
, 0);
1068 if (FileStringPath
== NULL
) {
1069 Status
= EFI_OUT_OF_RESOURCES
;
1071 TempSpot
= StrStr(FileStringPath
, L
";");
1072 if (TempSpot
!= NULL
) {
1073 *TempSpot
= CHAR_NULL
;
1075 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, ((FILEPATH_DEVICE_PATH
*)FilePath
)->PathName
, 0);
1076 PathRemoveLastItem(FileStringPath
);
1077 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, mStartupScript
, 0);
1078 Status
= ShellInfoObject
.NewEfiShellProtocol
->OpenFileByName(FileStringPath
, &FileHandle
, EFI_FILE_MODE_READ
);
1079 FreePool(FileStringPath
);
1082 if (EFI_ERROR(Status
)) {
1083 NamePath
= FileDevicePath (NULL
, mStartupScript
);
1084 NewPath
= AppendDevicePathNode (ImagePath
, NamePath
);
1090 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
1094 // If we got a file, run it
1096 if (!EFI_ERROR(Status
) && FileHandle
!= NULL
) {
1097 Status
= RunScriptFile (
1101 ShellInfoObject
.NewShellParametersProtocol
,
1104 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
1106 FileStringPath
= ShellFindFilePath(mStartupScript
);
1107 if (FileStringPath
== NULL
) {
1109 // we return success since we dont need to have a startup script
1111 Status
= EFI_SUCCESS
;
1112 ASSERT(FileHandle
== NULL
);
1114 Status
= RunScriptFile(
1118 ShellInfoObject
.NewShellParametersProtocol
,
1121 FreePool(FileStringPath
);
1130 Function to perform the shell prompt looping. It will do a single prompt,
1131 dispatch the result, and then return. It is expected that the caller will
1132 call this function in a loop many times.
1135 @retval RETURN_ABORTED
1146 CONST CHAR16
*CurDir
;
1153 // Get screen setting to decide size of the command line buffer
1155 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
1156 BufferSize
= Column
* Row
* sizeof (CHAR16
);
1157 CmdLine
= AllocateZeroPool (BufferSize
);
1158 if (CmdLine
== NULL
) {
1159 return EFI_OUT_OF_RESOURCES
;
1162 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1167 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
1169 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1170 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1172 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1176 // Read a line from the console
1178 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
1181 // Null terminate the string and parse it
1183 if (!EFI_ERROR (Status
)) {
1184 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
1185 Status
= RunCommand(CmdLine
, NULL
);
1189 // Done with this command
1196 Add a buffer to the Buffer To Free List for safely returning buffers to other
1197 places without risking letting them modify internal shell information.
1199 @param Buffer Something to pass to FreePool when the shell is exiting.
1203 AddBufferToFreeList(
1207 BUFFER_LIST
*BufferListEntry
;
1209 if (Buffer
== NULL
) {
1213 BufferListEntry
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1214 ASSERT(BufferListEntry
!= NULL
);
1215 BufferListEntry
->Buffer
= Buffer
;
1216 InsertTailList(&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
1221 Add a buffer to the Line History List
1223 @param Buffer The line buffer to add.
1227 AddLineToCommandHistory(
1228 IN CONST CHAR16
*Buffer
1233 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1234 ASSERT(Node
!= NULL
);
1235 Node
->Buffer
= AllocateZeroPool(StrSize(Buffer
));
1236 ASSERT(Node
->Buffer
!= NULL
);
1237 StrCpy(Node
->Buffer
, Buffer
);
1239 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1243 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1244 with the correct command name.
1246 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1247 command name if it was an alias. If it was not
1248 an alias it will be unchanged. This function may
1249 change the buffer to fit the command name.
1251 @retval EFI_SUCCESS The name was changed.
1252 @retval EFI_SUCCESS The name was not an alias.
1253 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1258 IN OUT CHAR16
**CommandString
1261 CONST CHAR16
*NewString
;
1263 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
1264 if (NewString
== NULL
) {
1265 return (EFI_SUCCESS
);
1267 FreePool(*CommandString
);
1268 *CommandString
= AllocateZeroPool(StrSize(NewString
));
1269 if (*CommandString
== NULL
) {
1270 return (EFI_OUT_OF_RESOURCES
);
1272 StrCpy(*CommandString
, NewString
);
1273 return (EFI_SUCCESS
);
1277 Function allocates a new command line and replaces all instances of environment
1278 variable names that are correctly preset to their values.
1280 If the return value is not NULL the memory must be caller freed.
1282 @param[in] OriginalCommandLine The original command line
1284 @retval NULL An error ocurred.
1285 @return The new command line with no environment variables present.
1289 ShellConvertVariables (
1290 IN CONST CHAR16
*OriginalCommandLine
1293 CONST CHAR16
*MasterEnvList
;
1295 CHAR16
*NewCommandLine1
;
1296 CHAR16
*NewCommandLine2
;
1301 SCRIPT_FILE
*CurrentScriptFile
;
1302 ALIAS_LIST
*AliasListNode
;
1304 ASSERT(OriginalCommandLine
!= NULL
);
1307 NewSize
= StrSize(OriginalCommandLine
);
1308 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
1311 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1314 // calculate the size required for the post-conversion string...
1316 if (CurrentScriptFile
!= NULL
) {
1317 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1318 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1319 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1321 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
1323 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
1326 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
1328 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
1329 NewSize
+= StrSize(AliasListNode
->CommandString
);
1335 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1336 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1337 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1339 if (StrSize(MasterEnvList
) > ItemSize
) {
1340 ItemSize
= StrSize(MasterEnvList
);
1342 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
1344 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
1347 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
1349 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
1350 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
1351 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
1357 // now do the replacements...
1359 NewCommandLine1
= AllocateZeroPool(NewSize
);
1360 NewCommandLine2
= AllocateZeroPool(NewSize
);
1361 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
1362 if (NewCommandLine1
== NULL
|| NewCommandLine2
== NULL
|| ItemTemp
== NULL
) {
1363 SHELL_FREE_NON_NULL(NewCommandLine1
);
1364 SHELL_FREE_NON_NULL(NewCommandLine2
);
1365 SHELL_FREE_NON_NULL(ItemTemp
);
1368 StrCpy(NewCommandLine1
, OriginalCommandLine
);
1369 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1370 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1371 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1373 StrCpy(ItemTemp
, L
"%");
1374 StrCat(ItemTemp
, MasterEnvList
);
1375 StrCat(ItemTemp
, L
"%");
1376 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1377 StrCpy(NewCommandLine1
, NewCommandLine2
);
1379 if (CurrentScriptFile
!= NULL
) {
1380 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1381 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1382 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1384 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1385 StrCpy(NewCommandLine1
, NewCommandLine2
);
1389 // Remove non-existant environment variables in scripts only
1391 for (Temp
= NewCommandLine1
; Temp
!= NULL
; ) {
1392 Temp
= StrStr(Temp
, L
"%");
1396 while (*(Temp
- 1) == L
'^') {
1397 Temp
= StrStr(Temp
+ 1, L
"%");
1406 Temp2
= StrStr(Temp
+ 1, L
"%");
1407 if (Temp2
== NULL
) {
1410 while (*(Temp2
- 1) == L
'^') {
1411 Temp2
= StrStr(Temp2
+ 1, L
"%");
1412 if (Temp2
== NULL
) {
1416 if (Temp2
== NULL
) {
1421 CopyMem(Temp
, Temp2
, StrSize(Temp2
));
1427 // Now cleanup any straggler intentionally ignored "%" characters
1429 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, L
"^%", L
"%", TRUE
, FALSE
);
1430 StrCpy(NewCommandLine1
, NewCommandLine2
);
1432 FreePool(NewCommandLine2
);
1435 return (NewCommandLine1
);
1439 Internal function to run a command line with pipe usage.
1441 @param[in] CmdLine The pointer to the command line.
1442 @param[in] StdIn The pointer to the Standard input.
1443 @param[in] StdOut The pointer to the Standard output.
1445 @param[out] ExitStatus The exit code of the last command in the pipeline.
1448 @retval EFI_SUCCESS The split command is executed successfully.
1449 @retval other Some error occurs when executing the split command.
1454 IN CONST CHAR16
*CmdLine
,
1455 IN SHELL_FILE_HANDLE
*StdIn
,
1456 IN SHELL_FILE_HANDLE
*StdOut
,
1457 OUT SHELL_STATUS
*ExitStatus
1461 CHAR16
*NextCommandLine
;
1462 CHAR16
*OurCommandLine
;
1466 SHELL_FILE_HANDLE
*TempFileHandle
;
1469 ASSERT(StdOut
== NULL
);
1471 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1473 Status
= EFI_SUCCESS
;
1474 NextCommandLine
= NULL
;
1475 OurCommandLine
= NULL
;
1479 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1480 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1482 if (NextCommandLine
== NULL
|| OurCommandLine
== NULL
) {
1483 SHELL_FREE_NON_NULL(OurCommandLine
);
1484 SHELL_FREE_NON_NULL(NextCommandLine
);
1485 return (EFI_OUT_OF_RESOURCES
);
1486 } else if (StrStr(OurCommandLine
, L
"|") != NULL
|| Size1
== 0 || Size2
== 0) {
1487 SHELL_FREE_NON_NULL(OurCommandLine
);
1488 SHELL_FREE_NON_NULL(NextCommandLine
);
1489 return (EFI_INVALID_PARAMETER
);
1490 } else if (NextCommandLine
[0] != CHAR_NULL
&&
1491 NextCommandLine
[0] == L
'a' &&
1492 NextCommandLine
[1] == L
' '
1494 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1502 // make a SPLIT_LIST item and add to list
1504 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1505 ASSERT(Split
!= NULL
);
1506 Split
->SplitStdIn
= StdIn
;
1507 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1508 ASSERT(Split
->SplitStdOut
!= NULL
);
1509 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1511 Status
= RunCommand(OurCommandLine
, NULL
);
1514 // move the output from the first to the in to the second.
1516 TempFileHandle
= Split
->SplitStdOut
;
1517 if (Split
->SplitStdIn
== StdIn
) {
1518 Split
->SplitStdOut
= NULL
;
1520 Split
->SplitStdOut
= Split
->SplitStdIn
;
1522 Split
->SplitStdIn
= TempFileHandle
;
1523 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
), 0);
1525 if (!EFI_ERROR(Status
)) {
1526 Status
= RunCommand(NextCommandLine
, ExitStatus
);
1530 // remove the top level from the ScriptList
1532 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1533 RemoveEntryList(&Split
->Link
);
1536 // Note that the original StdIn is now the StdOut...
1538 if (Split
->SplitStdOut
!= NULL
&& Split
->SplitStdOut
!= StdIn
) {
1539 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdOut
));
1541 if (Split
->SplitStdIn
!= NULL
) {
1542 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
));
1546 FreePool(NextCommandLine
);
1547 FreePool(OurCommandLine
);
1553 Take the original command line, substitute any variables, free
1554 the original string, return the modified copy.
1556 @param[in] CmdLine pointer to the command line to update.
1558 @retval EFI_SUCCESS the function was successful.
1559 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1563 ShellSubstituteVariables(
1568 NewCmdLine
= ShellConvertVariables(*CmdLine
);
1569 SHELL_FREE_NON_NULL(*CmdLine
);
1570 if (NewCmdLine
== NULL
) {
1571 return (EFI_OUT_OF_RESOURCES
);
1573 *CmdLine
= NewCmdLine
;
1574 return (EFI_SUCCESS
);
1578 Take the original command line, substitute any alias in the first group of space delimited characters, free
1579 the original string, return the modified copy.
1581 @param[in] CmdLine pointer to the command line to update.
1583 @retval EFI_SUCCESS the function was successful.
1584 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1588 ShellSubstituteAliases(
1593 CHAR16
*CommandName
;
1595 UINTN PostAliasSize
;
1596 ASSERT(CmdLine
!= NULL
);
1597 ASSERT(*CmdLine
!= NULL
);
1601 if (StrStr((*CmdLine
), L
" ") == NULL
){
1602 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), 0);
1604 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), StrStr((*CmdLine
), L
" ") - (*CmdLine
));
1608 // This cannot happen 'inline' since the CmdLine can need extra space.
1611 if (!ShellCommandIsCommandOnList(CommandName
)) {
1613 // Convert via alias
1615 Status
= ShellConvertAlias(&CommandName
);
1616 if (EFI_ERROR(Status
)){
1620 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, CommandName
, 0);
1621 if (NewCmdLine
== NULL
) {
1622 SHELL_FREE_NON_NULL(CommandName
);
1623 SHELL_FREE_NON_NULL(*CmdLine
);
1624 return (EFI_OUT_OF_RESOURCES
);
1626 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, StrStr((*CmdLine
), L
" "), 0);
1627 if (NewCmdLine
== NULL
) {
1628 SHELL_FREE_NON_NULL(CommandName
);
1629 SHELL_FREE_NON_NULL(*CmdLine
);
1630 return (EFI_OUT_OF_RESOURCES
);
1633 NewCmdLine
= StrnCatGrow(&NewCmdLine
, NULL
, (*CmdLine
), 0);
1636 SHELL_FREE_NON_NULL(*CmdLine
);
1637 SHELL_FREE_NON_NULL(CommandName
);
1640 // re-assign the passed in double pointer to point to our newly allocated buffer
1642 *CmdLine
= NewCmdLine
;
1644 return (EFI_SUCCESS
);
1648 Takes the Argv[0] part of the command line and determine the meaning of it.
1650 @param[in] CmdName pointer to the command line to update.
1652 @retval Internal_Command The name is an internal command.
1653 @retval File_Sys_Change the name is a file system change.
1654 @retval Script_File_Name the name is a NSH script file.
1655 @retval Unknown_Invalid the name is unknown.
1656 @retval Efi_Application the name is an application (.EFI).
1658 SHELL_OPERATION_TYPES
1661 IN CONST CHAR16
*CmdName
1664 CHAR16
* FileWithPath
;
1665 CONST CHAR16
* TempLocation
;
1666 CONST CHAR16
* TempLocation2
;
1668 FileWithPath
= NULL
;
1670 // test for an internal command.
1672 if (ShellCommandIsCommandOnList(CmdName
)) {
1673 return (Internal_Command
);
1677 // Test for file system change request. anything ending with : and cant have spaces.
1679 if (CmdName
[(StrLen(CmdName
)-1)] == L
':') {
1680 if (StrStr(CmdName
, L
" ") != NULL
) {
1681 return (Unknown_Invalid
);
1683 return (File_Sys_Change
);
1689 if ((FileWithPath
= ShellFindFilePathEx(CmdName
, mExecutableExtensions
)) != NULL
) {
1691 // See if that file has a script file extension
1693 if (StrLen(FileWithPath
) > 4) {
1694 TempLocation
= FileWithPath
+StrLen(FileWithPath
)-4;
1695 TempLocation2
= mScriptExtension
;
1696 if (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0) {
1697 SHELL_FREE_NON_NULL(FileWithPath
);
1698 return (Script_File_Name
);
1703 // Was a file, but not a script. we treat this as an application.
1705 SHELL_FREE_NON_NULL(FileWithPath
);
1706 return (Efi_Application
);
1709 SHELL_FREE_NON_NULL(FileWithPath
);
1711 // No clue what this is... return invalid flag...
1713 return (Unknown_Invalid
);
1717 Determine if the first item in a command line is valid.
1719 @param[in] CmdLine The command line to parse.
1721 @retval EFI_SUCCESS The item is valid.
1722 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1723 @retval EFI_NOT_FOUND The operation type is unknown or invalid.
1728 IN CONST CHAR16
*CmdLine
1732 CHAR16
*FirstParameter
;
1738 Temp
= StrnCatGrow(&Temp
, NULL
, CmdLine
, 0);
1740 return (EFI_OUT_OF_RESOURCES
);
1743 FirstParameter
= StrStr(Temp
, L
"|");
1744 if (FirstParameter
!= NULL
) {
1745 *FirstParameter
= CHAR_NULL
;
1748 FirstParameter
= NULL
;
1751 // Process the command line
1753 Status
= ProcessCommandLineToFinal(&Temp
);
1755 if (!EFI_ERROR(Status
)) {
1756 FirstParameter
= AllocateZeroPool(StrSize(CmdLine
));
1757 if (FirstParameter
== NULL
) {
1758 SHELL_FREE_NON_NULL(Temp
);
1759 return (EFI_OUT_OF_RESOURCES
);
1761 TempWalker
= (CHAR16
*)Temp
;
1762 GetNextParameter(&TempWalker
, &FirstParameter
);
1764 if (GetOperationType(FirstParameter
) == Unknown_Invalid
) {
1765 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
1766 SetLastError(SHELL_NOT_FOUND
);
1767 Status
= EFI_NOT_FOUND
;
1771 SHELL_FREE_NON_NULL(Temp
);
1772 SHELL_FREE_NON_NULL(FirstParameter
);
1777 Determine if a command line contains with a split contains only valid commands.
1779 @param[in] CmdLine The command line to parse.
1781 @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.
1782 @retval EFI_ABORTED CmdLine has at least one invalid command or application.
1787 IN CONST CHAR16
*CmdLine
1790 CONST CHAR16
*TempSpot
;
1794 // Verify up to the pipe or end character
1796 Status
= IsValidSplit(CmdLine
);
1797 if (EFI_ERROR(Status
)) {
1802 // If this was the only item, then get out
1804 if (!ContainsSplit(CmdLine
)) {
1805 return (EFI_SUCCESS
);
1809 // recurse to verify the next item
1811 TempSpot
= FindSplit(CmdLine
)+1;
1812 return (VerifySplit(TempSpot
));
1816 Process a split based operation.
1818 @param[in] CmdLine Pointer to the command line to process
1819 @param[out] ExitStatus The exit status of the command. Ignored if NULL.
1820 Invalid if this function returns an error.
1822 @retval EFI_SUCCESS The operation was successful
1823 @return an error occured.
1827 ProcessNewSplitCommandLine(
1828 IN CONST CHAR16
*CmdLine
,
1829 OUT SHELL_STATUS
*ExitStatus
1835 Status
= VerifySplit(CmdLine
);
1836 if (EFI_ERROR(Status
)) {
1843 // are we in an existing split???
1845 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
1846 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
1849 if (Split
== NULL
) {
1850 Status
= RunSplitCommand(CmdLine
, NULL
, NULL
, ExitStatus
);
1852 Status
= RunSplitCommand(
1859 if (EFI_ERROR(Status
)) {
1860 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_SPLIT
), ShellInfoObject
.HiiHandle
, CmdLine
);
1866 Handle a request to change the current file system.
1868 @param[in] CmdLine The passed in command line.
1870 @retval EFI_SUCCESS The operation was successful.
1875 IN CONST CHAR16
*CmdLine
1879 Status
= EFI_SUCCESS
;
1882 // make sure we are the right operation
1884 ASSERT(CmdLine
[(StrLen(CmdLine
)-1)] == L
':' && StrStr(CmdLine
, L
" ") == NULL
);
1887 // Call the protocol API to do the work
1889 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, CmdLine
);
1892 // Report any errors
1894 if (EFI_ERROR(Status
)) {
1895 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, CmdLine
);
1902 Reprocess the command line to direct all -? to the help command.
1904 if found, will add "help" as argv[0], and move the rest later.
1906 @param[in,out] CmdLine pointer to the command line to update
1911 IN OUT CHAR16
**CmdLine
1914 CHAR16
*CurrentParameter
;
1917 CHAR16
*NewCommandLine
;
1920 Status
= EFI_SUCCESS
;
1922 CurrentParameter
= AllocateZeroPool(StrSize(*CmdLine
));
1923 if (CurrentParameter
== NULL
) {
1924 return (EFI_OUT_OF_RESOURCES
);
1928 while(Walker
!= NULL
&& *Walker
!= CHAR_NULL
) {
1929 LastWalker
= Walker
;
1930 GetNextParameter(&Walker
, &CurrentParameter
);
1931 if (StrStr(CurrentParameter
, L
"-?") == CurrentParameter
) {
1932 LastWalker
[0] = L
' ';
1933 LastWalker
[1] = L
' ';
1934 NewCommandLine
= AllocateZeroPool(StrSize(L
"help ") + StrSize(*CmdLine
));
1935 if (NewCommandLine
== NULL
) {
1936 Status
= EFI_OUT_OF_RESOURCES
;
1939 StrCpy(NewCommandLine
, L
"help ");
1940 StrCat(NewCommandLine
, *CmdLine
);
1941 SHELL_FREE_NON_NULL(*CmdLine
);
1942 *CmdLine
= NewCommandLine
;
1947 SHELL_FREE_NON_NULL(CurrentParameter
);
1953 Function to update the shell variable "lasterror".
1955 @param[in] ErrorCode the error code to put into lasterror.
1960 IN CONST SHELL_STATUS ErrorCode
1963 CHAR16 LeString
[19];
1964 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
1965 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ErrorCode
);
1967 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", ErrorCode
);
1969 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
1970 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
1972 return (EFI_SUCCESS
);
1976 Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.
1978 @param[in,out] CmdLine pointer to the command line to update
1980 @retval EFI_SUCCESS The operation was successful
1981 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1982 @return some other error occured
1986 ProcessCommandLineToFinal(
1987 IN OUT CHAR16
**CmdLine
1991 TrimSpaces(CmdLine
);
1993 Status
= ShellSubstituteAliases(CmdLine
);
1994 if (EFI_ERROR(Status
)) {
1998 TrimSpaces(CmdLine
);
2000 Status
= ShellSubstituteVariables(CmdLine
);
2001 if (EFI_ERROR(Status
)) {
2005 TrimSpaces(CmdLine
);
2008 // update for help parsing
2010 if (StrStr(*CmdLine
, L
"?") != NULL
) {
2012 // This may do nothing if the ? does not indicate help.
2013 // Save all the details for in the API below.
2015 Status
= DoHelpUpdate(CmdLine
);
2018 TrimSpaces(CmdLine
);
2020 return (EFI_SUCCESS
);
2024 Run an internal shell command.
2026 This API will upadate the shell's environment since these commands are libraries.
2028 @param[in] CmdLine the command line to run.
2029 @param[in] FirstParameter the first parameter on the command line
2030 @param[in] ParamProtocol the shell parameters protocol pointer
2032 @param[out] ExitStatus The exit code of the command. Ignored if NULL.
2034 @retval EFI_SUCCESS The command was completed.
2035 @retval EFI_ABORTED The command's operation was aborted.
2040 IN CONST CHAR16
*CmdLine
,
2041 IN CHAR16
*FirstParameter
,
2042 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2043 OUT SHELL_STATUS
*ExitStatus OPTIONAL
2049 SHELL_STATUS CommandReturnedStatus
;
2053 // get the argc and argv updated for internal commands
2055 Status
= UpdateArgcArgv(ParamProtocol
, CmdLine
, &Argv
, &Argc
);
2056 if (!EFI_ERROR(Status
)) {
2058 // Run the internal command.
2060 Status
= ShellCommandRunCommandHandler(FirstParameter
, &CommandReturnedStatus
, &LastError
);
2062 if (!EFI_ERROR(Status
)) {
2064 // Update last error status.
2065 // some commands do not update last error.
2068 SetLastError(CommandReturnedStatus
);
2070 if (ExitStatus
!= NULL
) {
2071 *ExitStatus
= CommandReturnedStatus
;
2075 // Pass thru the exitcode from the app.
2077 if (ShellCommandGetExit()) {
2079 // An Exit was requested ("exit" command), pass its value up.
2081 Status
= CommandReturnedStatus
;
2082 } else if (CommandReturnedStatus
!= SHELL_SUCCESS
&& IsScriptOnlyCommand(FirstParameter
)) {
2084 // Always abort when a script only command fails for any reason
2086 Status
= EFI_ABORTED
;
2087 } else if (ShellCommandGetCurrentScriptFile() != NULL
&& CommandReturnedStatus
== SHELL_ABORTED
) {
2089 // Abort when in a script and a command aborted
2091 Status
= EFI_ABORTED
;
2097 // This is guarenteed to be called after UpdateArgcArgv no matter what else happened.
2098 // This is safe even if the update API failed. In this case, it may be a no-op.
2100 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);
2103 // If a script is running and the command is not a scipt only command, then
2104 // change return value to success so the script won't halt (unless aborted).
2106 // Script only commands have to be able halt the script since the script will
2107 // not operate if they are failing.
2109 if ( ShellCommandGetCurrentScriptFile() != NULL
2110 && !IsScriptOnlyCommand(FirstParameter
)
2111 && Status
!= EFI_ABORTED
2113 Status
= EFI_SUCCESS
;
2120 Function to run the command or file.
2122 @param[in] Type the type of operation being run.
2123 @param[in] CmdLine the command line to run.
2124 @param[in] FirstParameter the first parameter on the command line
2125 @param[in] ParamProtocol the shell parameters protocol pointer
2127 @param[out] ExitStatus The exit code of the command or file.
2130 @retval EFI_SUCCESS The command was completed.
2131 @retval EFI_ABORTED The command's operation was aborted.
2136 IN SHELL_OPERATION_TYPES Type
,
2137 IN CONST CHAR16
*CmdLine
,
2138 IN CHAR16
*FirstParameter
,
2139 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2140 OUT SHELL_STATUS
*ExitStatus
2144 CHAR16
*CommandWithPath
;
2145 EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
2146 SHELL_STATUS CalleeExitStatus
;
2148 Status
= EFI_SUCCESS
;
2149 CommandWithPath
= NULL
;
2151 CalleeExitStatus
= SHELL_INVALID_PARAMETER
;
2154 case Internal_Command
:
2155 Status
= RunInternalCommand(
2162 case Script_File_Name
:
2163 case Efi_Application
:
2165 // Process a fully qualified path
2167 if (StrStr(FirstParameter
, L
":") != NULL
) {
2168 ASSERT (CommandWithPath
== NULL
);
2169 if (ShellIsFile(FirstParameter
) == EFI_SUCCESS
) {
2170 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, FirstParameter
, 0);
2175 // Process a relative path and also check in the path environment variable
2177 if (CommandWithPath
== NULL
) {
2178 CommandWithPath
= ShellFindFilePathEx(FirstParameter
, mExecutableExtensions
);
2182 // This should be impossible now.
2184 ASSERT(CommandWithPath
!= NULL
);
2187 // Make sure that path is not just a directory (or not found)
2189 if (!EFI_ERROR(ShellIsDirectory(CommandWithPath
))) {
2190 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2191 SetLastError(SHELL_NOT_FOUND
);
2194 case Script_File_Name
:
2195 Status
= RunScriptFile (
2203 case Efi_Application
:
2205 // Get the device path of the application image
2207 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
2208 if (DevPath
== NULL
){
2209 Status
= EFI_OUT_OF_RESOURCES
;
2214 // Execute the device path
2216 Status
= InternalShellExecuteDevicePath(
2225 SHELL_FREE_NON_NULL(DevPath
);
2227 if(EFI_ERROR (Status
)) {
2228 CalleeExitStatus
= (SHELL_STATUS
) (Status
& (~MAX_BIT
));
2230 CalleeExitStatus
= SHELL_SUCCESS
;
2234 // Update last error status.
2236 // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
2237 SetLastError(CalleeExitStatus
);
2253 SHELL_FREE_NON_NULL(CommandWithPath
);
2255 if (ExitStatus
!= NULL
) {
2256 *ExitStatus
= CalleeExitStatus
;
2263 Function to setup StdIn, StdErr, StdOut, and then run the command or file.
2265 @param[in] Type the type of operation being run.
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
2270 @param[out] ExitStatus The exit code of the command or file.
2273 @retval EFI_SUCCESS The command was completed.
2274 @retval EFI_ABORTED The command's operation was aborted.
2278 SetupAndRunCommandOrFile(
2279 IN SHELL_OPERATION_TYPES Type
,
2281 IN CHAR16
*FirstParameter
,
2282 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2283 OUT SHELL_STATUS
*ExitStatus
2287 SHELL_FILE_HANDLE OriginalStdIn
;
2288 SHELL_FILE_HANDLE OriginalStdOut
;
2289 SHELL_FILE_HANDLE OriginalStdErr
;
2290 SYSTEM_TABLE_INFO OriginalSystemTableInfo
;
2293 // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
2295 Status
= UpdateStdInStdOutStdErr(ParamProtocol
, CmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2298 // The StdIn, StdOut, and StdErr are set up.
2299 // Now run the command, script, or application
2301 if (!EFI_ERROR(Status
)) {
2302 Status
= RunCommandOrFile(
2314 if (EFI_ERROR(Status
)) {
2315 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
));
2319 // put back the original StdIn, StdOut, and StdErr
2321 RestoreStdInStdOutStdErr(ParamProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2327 Function will process and run a command line.
2329 This will determine if the command line represents an internal shell
2330 command or dispatch an external application.
2332 @param[in] CmdLine The command line to parse.
2333 @param[out] ExitStatus The exit code of the command. Ignored if NULL.
2335 @retval EFI_SUCCESS The command was completed.
2336 @retval EFI_ABORTED The command's operation was aborted.
2341 IN CONST CHAR16
*CmdLine
,
2342 OUT SHELL_STATUS
*ExitStatus
2346 CHAR16
*CleanOriginal
;
2347 CHAR16
*FirstParameter
;
2349 SHELL_OPERATION_TYPES Type
;
2351 ASSERT(CmdLine
!= NULL
);
2352 if (StrLen(CmdLine
) == 0) {
2353 return (EFI_SUCCESS
);
2356 Status
= EFI_SUCCESS
;
2357 CleanOriginal
= NULL
;
2359 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
2360 if (CleanOriginal
== NULL
) {
2361 return (EFI_OUT_OF_RESOURCES
);
2364 TrimSpaces(&CleanOriginal
);
2367 // Handle case that passed in command line is just 1 or more " " characters.
2369 if (StrLen (CleanOriginal
) == 0) {
2370 SHELL_FREE_NON_NULL(CleanOriginal
);
2371 return (EFI_SUCCESS
);
2374 Status
= ProcessCommandLineToFinal(&CleanOriginal
);
2375 if (EFI_ERROR(Status
)) {
2376 SHELL_FREE_NON_NULL(CleanOriginal
);
2381 // We dont do normal processing with a split command line (output from one command input to another)
2383 if (ContainsSplit(CleanOriginal
)) {
2384 Status
= ProcessNewSplitCommandLine(CleanOriginal
, ExitStatus
);
2385 SHELL_FREE_NON_NULL(CleanOriginal
);
2390 // We need the first parameter information so we can determine the operation type
2392 FirstParameter
= AllocateZeroPool(StrSize(CleanOriginal
));
2393 if (FirstParameter
== NULL
) {
2394 SHELL_FREE_NON_NULL(CleanOriginal
);
2395 return (EFI_OUT_OF_RESOURCES
);
2397 TempWalker
= CleanOriginal
;
2398 GetNextParameter(&TempWalker
, &FirstParameter
);
2401 // Depending on the first parameter we change the behavior
2403 switch (Type
= GetOperationType(FirstParameter
)) {
2404 case File_Sys_Change
:
2405 Status
= ChangeMappedDrive(CleanOriginal
);
2407 case Internal_Command
:
2408 case Script_File_Name
:
2409 case Efi_Application
:
2410 Status
= SetupAndRunCommandOrFile(
2414 ShellInfoObject
.NewShellParametersProtocol
,
2420 // Whatever was typed, it was invalid.
2422 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2423 SetLastError(SHELL_NOT_FOUND
);
2427 SHELL_FREE_NON_NULL(CleanOriginal
);
2428 SHELL_FREE_NON_NULL(FirstParameter
);
2433 STATIC CONST UINT16 InvalidChars
[] = {L
'*', L
'?', L
'<', L
'>', L
'\\', L
'/', L
'\"', 0x0001, 0x0002};
2435 Function determins if the CommandName COULD be a valid command. It does not determine whether
2436 this is a valid command. It only checks for invalid characters.
2438 @param[in] CommandName The name to check
2440 @retval TRUE CommandName could be a command name
2441 @retval FALSE CommandName could not be a valid command name
2446 IN CONST CHAR16
*CommandName
2450 if (CommandName
== NULL
) {
2455 ; Count
< sizeof(InvalidChars
) / sizeof(InvalidChars
[0])
2458 if (ScanMem16(CommandName
, StrSize(CommandName
), InvalidChars
[Count
]) != NULL
) {
2466 Function to process a NSH script file via SHELL_FILE_HANDLE.
2468 @param[in] Handle The handle to the already opened file.
2469 @param[in] Name The name of the script file.
2471 @param[out] ExitStatus The exit code of the script. Ignored if NULL.
2473 @retval EFI_SUCCESS the script completed sucessfully
2477 RunScriptFileHandle (
2478 IN SHELL_FILE_HANDLE Handle
,
2479 IN CONST CHAR16
*Name
,
2480 OUT SHELL_STATUS
*ExitStatus
2484 SCRIPT_FILE
*NewScriptFile
;
2486 CHAR16
*CommandLine
;
2487 CHAR16
*CommandLine2
;
2488 CHAR16
*CommandLine3
;
2489 SCRIPT_COMMAND_LIST
*LastCommand
;
2491 BOOLEAN PreScriptEchoState
;
2492 BOOLEAN PreCommandEchoState
;
2493 CONST CHAR16
*CurDir
;
2495 CHAR16 LeString
[50];
2496 SHELL_STATUS CalleeExitStatus
;
2498 ASSERT(!ShellCommandGetScriptExit());
2500 CalleeExitStatus
= SHELL_SUCCESS
;
2502 PreScriptEchoState
= ShellCommandGetEchoState();
2504 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
2505 if (NewScriptFile
== NULL
) {
2506 return (EFI_OUT_OF_RESOURCES
);
2512 ASSERT(NewScriptFile
->ScriptName
== NULL
);
2513 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
2514 if (NewScriptFile
->ScriptName
== NULL
) {
2515 DeleteScriptFileStruct(NewScriptFile
);
2516 return (EFI_OUT_OF_RESOURCES
);
2520 // Save the parameters (used to replace %0 to %9 later on)
2522 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
2523 if (NewScriptFile
->Argc
!= 0) {
2524 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
2525 if (NewScriptFile
->Argv
== NULL
) {
2526 DeleteScriptFileStruct(NewScriptFile
);
2527 return (EFI_OUT_OF_RESOURCES
);
2529 for (LoopVar
= 0 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
2530 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
2531 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
2532 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
2533 DeleteScriptFileStruct(NewScriptFile
);
2534 return (EFI_OUT_OF_RESOURCES
);
2538 NewScriptFile
->Argv
= NULL
;
2541 InitializeListHead(&NewScriptFile
->CommandList
);
2542 InitializeListHead(&NewScriptFile
->SubstList
);
2545 // Now build the list of all script commands.
2548 while(!ShellFileHandleEof(Handle
)) {
2549 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
2551 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0 || CommandLine
[0] == '#') {
2552 SHELL_FREE_NON_NULL(CommandLine
);
2555 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
2556 if (NewScriptFile
->CurrentCommand
== NULL
) {
2557 SHELL_FREE_NON_NULL(CommandLine
);
2558 DeleteScriptFileStruct(NewScriptFile
);
2559 return (EFI_OUT_OF_RESOURCES
);
2562 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
2563 NewScriptFile
->CurrentCommand
->Data
= NULL
;
2564 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
2566 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2570 // Add this as the topmost script file
2572 ShellCommandSetNewScript (NewScriptFile
);
2575 // Now enumerate through the commands and run each one.
2577 CommandLine
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
2578 if (CommandLine
== NULL
) {
2579 DeleteScriptFileStruct(NewScriptFile
);
2580 return (EFI_OUT_OF_RESOURCES
);
2582 CommandLine2
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
2583 if (CommandLine2
== NULL
) {
2584 FreePool(CommandLine
);
2585 DeleteScriptFileStruct(NewScriptFile
);
2586 return (EFI_OUT_OF_RESOURCES
);
2589 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
2590 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
2591 ; // conditional increment in the body of the loop
2593 ASSERT(CommandLine2
!= NULL
);
2594 StrCpy(CommandLine2
, NewScriptFile
->CurrentCommand
->Cl
);
2597 // NULL out comments
2599 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
2600 if (*CommandLine3
== L
'^') {
2601 if (*(CommandLine3
+1) == L
'#' || *(CommandLine3
+1) == L
':') {
2602 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
2604 } else if (*CommandLine3
== L
'#') {
2605 *CommandLine3
= CHAR_NULL
;
2609 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
2611 // Due to variability in starting the find and replace action we need to have both buffers the same.
2613 StrCpy(CommandLine
, CommandLine2
);
2616 // Remove the %0 to %9 from the command line (if we have some arguments)
2618 if (NewScriptFile
->Argv
!= NULL
) {
2619 switch (NewScriptFile
->Argc
) {
2621 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", NewScriptFile
->Argv
[9], FALSE
, TRUE
);
2622 ASSERT_EFI_ERROR(Status
);
2624 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", NewScriptFile
->Argv
[8], FALSE
, TRUE
);
2625 ASSERT_EFI_ERROR(Status
);
2627 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", NewScriptFile
->Argv
[7], FALSE
, TRUE
);
2628 ASSERT_EFI_ERROR(Status
);
2630 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", NewScriptFile
->Argv
[6], FALSE
, TRUE
);
2631 ASSERT_EFI_ERROR(Status
);
2633 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", NewScriptFile
->Argv
[5], FALSE
, TRUE
);
2634 ASSERT_EFI_ERROR(Status
);
2636 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", NewScriptFile
->Argv
[4], FALSE
, TRUE
);
2637 ASSERT_EFI_ERROR(Status
);
2639 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", NewScriptFile
->Argv
[3], FALSE
, TRUE
);
2640 ASSERT_EFI_ERROR(Status
);
2642 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", NewScriptFile
->Argv
[2], FALSE
, TRUE
);
2643 ASSERT_EFI_ERROR(Status
);
2645 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", NewScriptFile
->Argv
[1], FALSE
, TRUE
);
2646 ASSERT_EFI_ERROR(Status
);
2648 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%0", NewScriptFile
->Argv
[0], FALSE
, TRUE
);
2649 ASSERT_EFI_ERROR(Status
);
2655 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", L
"\"\"", FALSE
, FALSE
);
2656 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", L
"\"\"", FALSE
, FALSE
);
2657 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", L
"\"\"", FALSE
, FALSE
);
2658 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", L
"\"\"", FALSE
, FALSE
);
2659 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", L
"\"\"", FALSE
, FALSE
);
2660 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", L
"\"\"", FALSE
, FALSE
);
2661 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", L
"\"\"", FALSE
, FALSE
);
2662 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", L
"\"\"", FALSE
, FALSE
);
2663 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", L
"\"\"", FALSE
, FALSE
);
2665 StrCpy(CommandLine2
, CommandLine
);
2667 LastCommand
= NewScriptFile
->CurrentCommand
;
2669 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
2671 if (CommandLine3
!= NULL
&& CommandLine3
[0] == L
':' ) {
2673 // This line is a goto target / label
2676 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
2677 if (CommandLine3
[0] == L
'@') {
2679 // We need to save the current echo state
2680 // and disable echo for just this command.
2682 PreCommandEchoState
= ShellCommandGetEchoState();
2683 ShellCommandSetEchoState(FALSE
);
2684 Status
= RunCommand(CommandLine3
+1, NULL
);
2687 // If command was "@echo -off" or "@echo -on" then don't restore echo state
2689 if (StrCmp (L
"@echo -off", CommandLine3
) != 0 &&
2690 StrCmp (L
"@echo -on", CommandLine3
) != 0) {
2692 // Now restore the pre-'@' echo state.
2694 ShellCommandSetEchoState(PreCommandEchoState
);
2697 if (ShellCommandGetEchoState()) {
2698 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
2699 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
2700 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
2702 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
2704 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
2706 Status
= RunCommand(CommandLine3
, NULL
);
2710 if (ShellCommandGetScriptExit()) {
2712 // ShellCommandGetExitCode() always returns a UINT64
2714 CalleeExitStatus
= (SHELL_STATUS
) ShellCommandGetExitCode();
2715 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", CalleeExitStatus
);
2716 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
2717 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
2719 ShellCommandRegisterExit(FALSE
, 0);
2720 Status
= EFI_SUCCESS
;
2723 if (ShellGetExecutionBreakFlag()) {
2726 if (EFI_ERROR(Status
)) {
2727 CalleeExitStatus
= (SHELL_STATUS
) Status
;
2730 if (ShellCommandGetExit()) {
2731 CalleeExitStatus
= (SHELL_STATUS
) ShellCommandGetExitCode();
2736 // If that commend did not update the CurrentCommand then we need to advance it...
2738 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
2739 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2740 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2741 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2745 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2746 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2747 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2753 FreePool(CommandLine
);
2754 FreePool(CommandLine2
);
2755 ShellCommandSetNewScript (NULL
);
2758 // Only if this was the last script reset the state.
2760 if (ShellCommandGetCurrentScriptFile()==NULL
) {
2761 ShellCommandSetEchoState(PreScriptEchoState
);
2764 if (ExitStatus
!= NULL
) {
2765 *ExitStatus
= CalleeExitStatus
;
2768 return (EFI_SUCCESS
);
2772 Function to process a NSH script file.
2774 @param[in] ScriptPath Pointer to the script file name (including file system path).
2775 @param[in] Handle the handle of the script file already opened.
2776 @param[in] CmdLine the command line to run.
2777 @param[in] ParamProtocol the shell parameters protocol pointer
2779 @param[out] ExitStatus The exit code of the script. Ignored if NULL.
2781 @retval EFI_SUCCESS the script completed sucessfully
2786 IN CONST CHAR16
*ScriptPath
,
2787 IN SHELL_FILE_HANDLE Handle OPTIONAL
,
2788 IN CONST CHAR16
*CmdLine
,
2789 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2790 OUT SHELL_STATUS
*ExitStatus
2794 SHELL_FILE_HANDLE FileHandle
;
2798 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
2799 return (EFI_INVALID_PARAMETER
);
2803 // get the argc and argv updated for scripts
2805 Status
= UpdateArgcArgv(ParamProtocol
, CmdLine
, &Argv
, &Argc
);
2806 if (!EFI_ERROR(Status
)) {
2808 if (Handle
== NULL
) {
2812 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
2813 if (!EFI_ERROR(Status
)) {
2817 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
, ExitStatus
);
2820 // now close the file
2822 ShellCloseFile(&FileHandle
);
2825 Status
= RunScriptFileHandle(Handle
, ScriptPath
, ExitStatus
);
2830 // This is guarenteed to be called after UpdateArgcArgv no matter what else happened.
2831 // This is safe even if the update API failed. In this case, it may be a no-op.
2833 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);