diff options
Diffstat (limited to 'ShellPkg/Application')
30 files changed, 17392 insertions, 17392 deletions
diff --git a/ShellPkg/Application/AcpiViewApp/AcpiViewApp.c b/ShellPkg/Application/AcpiViewApp/AcpiViewApp.c index 031f3b9769..318f9077ef 100644 --- a/ShellPkg/Application/AcpiViewApp/AcpiViewApp.c +++ b/ShellPkg/Application/AcpiViewApp/AcpiViewApp.c @@ -1,40 +1,40 @@ -/** @file
- Main file for AcpiViewApp application
-
- Copyright (c) 2020, ARM Limited. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-**/
-
-#include <Library/BaseLib.h>
-#include <Library/UefiBootServicesTableLib.h>
-#include <Library/UefiLib.h>
-#include <Library/ShellLib.h>
-#include <Library/AcpiViewCommandLib.h>
-
-#include <Protocol/ShellParameters.h>
-
-//
-// String token ID of help message text.
-// Shell supports to find help message in the resource section of an application image if
-// .MAN file is not found. This global variable is added to make build tool recognizes
-// that the help string is consumed by user and then build tool will add the string into
-// the resource section. Thus the application can use '-?' option to show help message in
-// Shell.
-//
-GLOBAL_REMOVE_IF_UNREFERENCED EFI_STRING_ID mStringHelpTokenId = STRING_TOKEN (STR_GET_HELP_ACPIVIEW);
-
-/**
- Application Entry Point wrapper around the shell command
-
- @param[in] ImageHandle Handle to the Image (NULL if internal).
- @param[in] SystemTable Pointer to the System Table (NULL if internal).
-**/
-EFI_STATUS
-EFIAPI
-AcpiViewAppMain (
- IN EFI_HANDLE ImageHandle,
- IN EFI_SYSTEM_TABLE *SystemTable
- )
-{
- return ShellCommandRunAcpiView (gImageHandle, SystemTable);
-}
+/** @file + Main file for AcpiViewApp application + + Copyright (c) 2020, ARM Limited. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include <Library/BaseLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> +#include <Library/ShellLib.h> +#include <Library/AcpiViewCommandLib.h> + +#include <Protocol/ShellParameters.h> + +// +// String token ID of help message text. +// Shell supports to find help message in the resource section of an application image if +// .MAN file is not found. This global variable is added to make build tool recognizes +// that the help string is consumed by user and then build tool will add the string into +// the resource section. Thus the application can use '-?' option to show help message in +// Shell. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_STRING_ID mStringHelpTokenId = STRING_TOKEN (STR_GET_HELP_ACPIVIEW); + +/** + Application Entry Point wrapper around the shell command + + @param[in] ImageHandle Handle to the Image (NULL if internal). + @param[in] SystemTable Pointer to the System Table (NULL if internal). +**/ +EFI_STATUS +EFIAPI +AcpiViewAppMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return ShellCommandRunAcpiView (gImageHandle, SystemTable); +} diff --git a/ShellPkg/Application/AcpiViewApp/AcpiViewApp.inf b/ShellPkg/Application/AcpiViewApp/AcpiViewApp.inf index 2ed1d18533..ab5f752da5 100644 --- a/ShellPkg/Application/AcpiViewApp/AcpiViewApp.inf +++ b/ShellPkg/Application/AcpiViewApp/AcpiViewApp.inf @@ -1,34 +1,34 @@ -## @file
-# EFI application that displays and verifies ACPI tables
-#
-# Copyright (c) 2020, ARM Limited. All rights reserved.<BR>
-#
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
-#
-##
-
-[Defines]
- INF_VERSION = 0x0001001B
- BASE_NAME = AcpiViewApp
- FILE_GUID = 46361B5B-AF17-41FF-95F9-E1BCE08435B9
- MODULE_TYPE = UEFI_APPLICATION
- VERSION_STRING = 1.0
- ENTRY_POINT = AcpiViewAppMain
- UEFI_HII_RESOURCE_SECTION = TRUE
-
-[Sources.common]
- AcpiViewApp.c
- AcpiViewApp.uni
-
-[Packages]
- MdeModulePkg/MdeModulePkg.dec
- MdePkg/MdePkg.dec
- ShellPkg/ShellPkg.dec
-
-[LibraryClasses]
- UefiBootServicesTableLib
- UefiLib
- BaseLib
- UefiApplicationEntryPoint
- AcpiViewCommandLib
+## @file +# EFI application that displays and verifies ACPI tables +# +# Copyright (c) 2020, ARM Limited. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = AcpiViewApp + FILE_GUID = 46361B5B-AF17-41FF-95F9-E1BCE08435B9 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = AcpiViewAppMain + UEFI_HII_RESOURCE_SECTION = TRUE + +[Sources.common] + AcpiViewApp.c + AcpiViewApp.uni + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + UefiLib + BaseLib + UefiApplicationEntryPoint + AcpiViewCommandLib diff --git a/ShellPkg/Application/AcpiViewApp/AcpiViewApp.uni b/ShellPkg/Application/AcpiViewApp/AcpiViewApp.uni index c56f210843..a613f51c26 100644 --- a/ShellPkg/Application/AcpiViewApp/AcpiViewApp.uni +++ b/ShellPkg/Application/AcpiViewApp/AcpiViewApp.uni @@ -1,122 +1,122 @@ -// @file
-//
-// Standalone EFI application that displays and verifies ACPI tables
-//
-// Copyright (c) 2020, ARM Limited. All rights reserved.<BR>
-// SPDX-License-Identifier: BSD-2-Clause-Patent
-//
-
-/=#
-
-#langdef en-US "english"
-
-#string STR_GET_HELP_ACPIVIEW #language en-US ""
-".TH acpiview 0 "Display ACPI information."\r\n"
-".SH NAME\r\n"
-"Display ACPI Table information.\r\n"
-".SH SYNOPSIS\r\n"
-" \r\n"
-"ACPIVIEWAPP.EFI [[-?] | [[[[-l] | [-s AcpiTable [-d]]] [-q] [-h]] [-r Spec]]]\r\n"
-" \r\n"
-".SH OPTIONS\r\n"
-" \r\n"
-" -l - Display list of installed ACPI Tables.\r\n"
-" -s - Display only the specified AcpiTable type and only support single\r\n"
-" invocation option.\r\n"
-" AcpiTable : The required ACPI Table type.\r\n"
-" -d - Generate a binary file dump of the specified AcpiTable.\r\n"
-" -q - Quiet. Suppress errors and warnings. Disables consistency checks.\r\n"
-" -h - Enable colour highlighting.\r\n"
-" -r - Validate that all required ACPI tables are installed\r\n"
-" Spec : Specification to validate against.\r\n"
-" For Arm, the possible values are:\r\n"
-" 0 - Server Base Boot Requirements v1.0\r\n"
-" 1 - Server Base Boot Requirements v1.1\r\n"
-" 2 - Server Base Boot Requirements v1.2\r\n"
-" -? - Show help.\r\n"
-" \r\n"
-".SH DESCRIPTION\r\n"
-" \r\n"
-" This program is provided to allow examination of ACPI table values from the\r\n"
-" UEFI Shell. This can help with investigations, especially at that stage\r\n"
-" where the tables are not enabling an OS to boot.\r\n"
-" The program is not exhaustive, and only encapsulates detailed knowledge of a\r\n"
-" limited number of table types.\r\n"
-" \r\n"
-" Default behaviour is to display the content of all tables installed.\r\n"
-" 'Known' table types (listed in NOTES below) will be parsed and displayed\r\n"
-" with descriptions and field values. Where appropriate a degree of\r\n"
-" consistency checking is done and errors may be reported in the output.\r\n"
-" Other table types will be displayed as an array of Hexadecimal bytes.\r\n"
-" \r\n"
-" To facilitate debugging, the -s and -d options can be used to generate a\r\n"
-" binary file image of a table that can be copied elsewhere for investigation\r\n"
-" using tools such as those provided by acpica.org. This is especially\r\n"
-" relevant for AML type tables like DSDT and SSDT.\r\n"
-" \r\n"
-"NOTES:\r\n"
-" 1. The AcpiTable parameter can match any installed table type.\r\n"
-" Tables without specific handling will be displayed as a raw hex dump (or\r\n"
-" dumped to a file if -d is used).\r\n"
-" 2. -s option supports to display the specified AcpiTable type that is present\r\n"
-" in the system. For normal type AcpiTable, it would display the data of the\r\n"
-" AcpiTable and AcpiTable header. The following type may contain header type\r\n"
-" other than AcpiTable header. The actual header can refer to the ACPI spec\r\n"
-" 6.3\r\n"
-" Extra A. Particular types:\r\n"
-" APIC - Multiple APIC Description Table (MADT)\r\n"
-" BGRT - Boot Graphics Resource Table\r\n"
-" DBG2 - Debug Port Table 2\r\n"
-" DSDT - Differentiated System Description Table\r\n"
-" FACP - Fixed ACPI Description Table (FADT)\r\n"
-" GTDT - Generic Timer Description Table\r\n"
-" IORT - IO Remapping Table\r\n"
-" MCFG - Memory Mapped Config Space Base Address Description Table\r\n"
-" PPTT - Processor Properties Topology Table\r\n"
-" RSDP - Root System Description Pointer\r\n"
-" SLIT - System Locality Information Table\r\n"
-" SPCR - Serial Port Console Redirection Table\r\n"
-" SRAT - System Resource Affinity Table\r\n"
-" SSDT - Secondary SystemDescription Table\r\n"
-" XSDT - Extended System Description Table\r\n"
-" \r\n"
-".SH STANDARDS\r\n"
-" \r\n"
-" Table details correspond to those in 'Advanced Configuration and Power\r\n"
-" Interface Specification' Version 6.3 [January 2019]\r\n"
-" (https://uefi.org/specifications)\r\n"
-" \r\n"
-" NOTE: The nature of the ACPI standard means that almost all tables in 6.3\r\n"
-" will be 'backwards compatible' with prior version of the specification\r\n"
-" in terms of structure, so formatted output should be correct. The main\r\n"
-" exception will be that previously 'reserved' fields will be reported\r\n"
-" with new names, where they have been added in later versions of the\r\n"
-" specification.\r\n"
-" \r\n"
-".SH EXAMPLES\r\n"
-" \r\n"
-" \r\n"
-"EXAMPLES:\r\n"
-" * To display a list of the installed table types:\r\n"
-" fs0:\> acpiviewapp.efi -l\r\n"
-" \r\n"
-" * To parse and display a specific table type:\r\n"
-" fs0:\> acpiviewapp.efi -s GTDT\r\n"
-" \r\n"
-" * To save a binary dump of the contents of a table to a file\r\n"
-" in the current working directory:\r\n"
-" fs0:\> acpiviewapp.efi -s DSDT -d\r\n"
-" \r\n"
-" * To display contents of all ACPI tables:\r\n"
-" fs0:\> acpiviewapp.efi\r\n"
-" \r\n"
-" * To check if all Server Base Boot Requirements (SBBR) v1.2 mandatory\r\n"
-" ACPI tables are installed (Arm only):\r\n"
-" fs0:\> acpiviewapp.efi -r 2\r\n"
-" \r\n"
-".SH RETURNVALUES\r\n"
-" \r\n"
-"RETURN VALUES:\r\n"
-" SHELL_SUCCESS Data was displayed as requested.\r\n"
-" SHELL_INVALID_PARAMETER ACPI Table parsing failed.\r\n"
-" \r\n"
+// @file +// +// Standalone EFI application that displays and verifies ACPI tables +// +// Copyright (c) 2020, ARM Limited. All rights reserved.<BR> +// SPDX-License-Identifier: BSD-2-Clause-Patent +// + +/=# + +#langdef en-US "english" + +#string STR_GET_HELP_ACPIVIEW #language en-US "" +".TH acpiview 0 "Display ACPI information."\r\n" +".SH NAME\r\n" +"Display ACPI Table information.\r\n" +".SH SYNOPSIS\r\n" +" \r\n" +"ACPIVIEWAPP.EFI [[-?] | [[[[-l] | [-s AcpiTable [-d]]] [-q] [-h]] [-r Spec]]]\r\n" +" \r\n" +".SH OPTIONS\r\n" +" \r\n" +" -l - Display list of installed ACPI Tables.\r\n" +" -s - Display only the specified AcpiTable type and only support single\r\n" +" invocation option.\r\n" +" AcpiTable : The required ACPI Table type.\r\n" +" -d - Generate a binary file dump of the specified AcpiTable.\r\n" +" -q - Quiet. Suppress errors and warnings. Disables consistency checks.\r\n" +" -h - Enable colour highlighting.\r\n" +" -r - Validate that all required ACPI tables are installed\r\n" +" Spec : Specification to validate against.\r\n" +" For Arm, the possible values are:\r\n" +" 0 - Server Base Boot Requirements v1.0\r\n" +" 1 - Server Base Boot Requirements v1.1\r\n" +" 2 - Server Base Boot Requirements v1.2\r\n" +" -? - Show help.\r\n" +" \r\n" +".SH DESCRIPTION\r\n" +" \r\n" +" This program is provided to allow examination of ACPI table values from the\r\n" +" UEFI Shell. This can help with investigations, especially at that stage\r\n" +" where the tables are not enabling an OS to boot.\r\n" +" The program is not exhaustive, and only encapsulates detailed knowledge of a\r\n" +" limited number of table types.\r\n" +" \r\n" +" Default behaviour is to display the content of all tables installed.\r\n" +" 'Known' table types (listed in NOTES below) will be parsed and displayed\r\n" +" with descriptions and field values. Where appropriate a degree of\r\n" +" consistency checking is done and errors may be reported in the output.\r\n" +" Other table types will be displayed as an array of Hexadecimal bytes.\r\n" +" \r\n" +" To facilitate debugging, the -s and -d options can be used to generate a\r\n" +" binary file image of a table that can be copied elsewhere for investigation\r\n" +" using tools such as those provided by acpica.org. This is especially\r\n" +" relevant for AML type tables like DSDT and SSDT.\r\n" +" \r\n" +"NOTES:\r\n" +" 1. The AcpiTable parameter can match any installed table type.\r\n" +" Tables without specific handling will be displayed as a raw hex dump (or\r\n" +" dumped to a file if -d is used).\r\n" +" 2. -s option supports to display the specified AcpiTable type that is present\r\n" +" in the system. For normal type AcpiTable, it would display the data of the\r\n" +" AcpiTable and AcpiTable header. The following type may contain header type\r\n" +" other than AcpiTable header. The actual header can refer to the ACPI spec\r\n" +" 6.3\r\n" +" Extra A. Particular types:\r\n" +" APIC - Multiple APIC Description Table (MADT)\r\n" +" BGRT - Boot Graphics Resource Table\r\n" +" DBG2 - Debug Port Table 2\r\n" +" DSDT - Differentiated System Description Table\r\n" +" FACP - Fixed ACPI Description Table (FADT)\r\n" +" GTDT - Generic Timer Description Table\r\n" +" IORT - IO Remapping Table\r\n" +" MCFG - Memory Mapped Config Space Base Address Description Table\r\n" +" PPTT - Processor Properties Topology Table\r\n" +" RSDP - Root System Description Pointer\r\n" +" SLIT - System Locality Information Table\r\n" +" SPCR - Serial Port Console Redirection Table\r\n" +" SRAT - System Resource Affinity Table\r\n" +" SSDT - Secondary SystemDescription Table\r\n" +" XSDT - Extended System Description Table\r\n" +" \r\n" +".SH STANDARDS\r\n" +" \r\n" +" Table details correspond to those in 'Advanced Configuration and Power\r\n" +" Interface Specification' Version 6.3 [January 2019]\r\n" +" (https://uefi.org/specifications)\r\n" +" \r\n" +" NOTE: The nature of the ACPI standard means that almost all tables in 6.3\r\n" +" will be 'backwards compatible' with prior version of the specification\r\n" +" in terms of structure, so formatted output should be correct. The main\r\n" +" exception will be that previously 'reserved' fields will be reported\r\n" +" with new names, where they have been added in later versions of the\r\n" +" specification.\r\n" +" \r\n" +".SH EXAMPLES\r\n" +" \r\n" +" \r\n" +"EXAMPLES:\r\n" +" * To display a list of the installed table types:\r\n" +" fs0:\> acpiviewapp.efi -l\r\n" +" \r\n" +" * To parse and display a specific table type:\r\n" +" fs0:\> acpiviewapp.efi -s GTDT\r\n" +" \r\n" +" * To save a binary dump of the contents of a table to a file\r\n" +" in the current working directory:\r\n" +" fs0:\> acpiviewapp.efi -s DSDT -d\r\n" +" \r\n" +" * To display contents of all ACPI tables:\r\n" +" fs0:\> acpiviewapp.efi\r\n" +" \r\n" +" * To check if all Server Base Boot Requirements (SBBR) v1.2 mandatory\r\n" +" ACPI tables are installed (Arm only):\r\n" +" fs0:\> acpiviewapp.efi -r 2\r\n" +" \r\n" +".SH RETURNVALUES\r\n" +" \r\n" +"RETURN VALUES:\r\n" +" SHELL_SUCCESS Data was displayed as requested.\r\n" +" SHELL_INVALID_PARAMETER ACPI Table parsing failed.\r\n" +" \r\n" diff --git a/ShellPkg/Application/Shell/ConsoleLogger.c b/ShellPkg/Application/Shell/ConsoleLogger.c index abef0a60d6..1a8ff25448 100644 --- a/ShellPkg/Application/Shell/ConsoleLogger.c +++ b/ShellPkg/Application/Shell/ConsoleLogger.c @@ -1,1278 +1,1278 @@ -/** @file
- Provides interface to shell console logger.
-
- (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR>
- Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
- (C) Copyright 2016 Hewlett-Packard Development Company, L.P.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-**/
-
-#include "Shell.h"
-
-/**
- Install our intermediate ConOut into the system table to
- keep a log of all the info that is displayed to the user.
-
- @param[in] ScreensToSave Sets how many screen-worths of data to save.
- @param[out] ConsoleInfo The object to pass into later functions.
-
- @retval EFI_SUCCESS The operation was successful.
- @return other The operation failed.
-
- @sa ConsoleLoggerResetBuffers
- @sa InstallProtocolInterface
-**/
-EFI_STATUS
-ConsoleLoggerInstall (
- IN CONST UINTN ScreensToSave,
- OUT CONSOLE_LOGGER_PRIVATE_DATA **ConsoleInfo
- )
-{
- EFI_STATUS Status;
-
- ASSERT (ConsoleInfo != NULL);
-
- (*ConsoleInfo) = AllocateZeroPool (sizeof (CONSOLE_LOGGER_PRIVATE_DATA));
- if ((*ConsoleInfo) == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- (*ConsoleInfo)->Signature = CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE;
- (*ConsoleInfo)->OldConOut = gST->ConOut;
- (*ConsoleInfo)->OldConHandle = gST->ConsoleOutHandle;
- (*ConsoleInfo)->Buffer = NULL;
- (*ConsoleInfo)->BufferSize = 0;
- (*ConsoleInfo)->OriginalStartRow = 0;
- (*ConsoleInfo)->CurrentStartRow = 0;
- (*ConsoleInfo)->RowsPerScreen = 0;
- (*ConsoleInfo)->ColsPerScreen = 0;
- (*ConsoleInfo)->Attributes = NULL;
- (*ConsoleInfo)->AttribSize = 0;
- (*ConsoleInfo)->ScreenCount = ScreensToSave;
- (*ConsoleInfo)->HistoryMode.MaxMode = 1;
- (*ConsoleInfo)->HistoryMode.Mode = 0;
- (*ConsoleInfo)->HistoryMode.Attribute = 0;
- (*ConsoleInfo)->HistoryMode.CursorColumn = 0;
- (*ConsoleInfo)->HistoryMode.CursorRow = 0;
- (*ConsoleInfo)->HistoryMode.CursorVisible = FALSE;
- (*ConsoleInfo)->OurConOut.Reset = ConsoleLoggerReset;
- (*ConsoleInfo)->OurConOut.OutputString = ConsoleLoggerOutputString;
- (*ConsoleInfo)->OurConOut.TestString = ConsoleLoggerTestString;
- (*ConsoleInfo)->OurConOut.QueryMode = ConsoleLoggerQueryMode;
- (*ConsoleInfo)->OurConOut.SetMode = ConsoleLoggerSetMode;
- (*ConsoleInfo)->OurConOut.SetAttribute = ConsoleLoggerSetAttribute;
- (*ConsoleInfo)->OurConOut.ClearScreen = ConsoleLoggerClearScreen;
- (*ConsoleInfo)->OurConOut.SetCursorPosition = ConsoleLoggerSetCursorPosition;
- (*ConsoleInfo)->OurConOut.EnableCursor = ConsoleLoggerEnableCursor;
- (*ConsoleInfo)->OurConOut.Mode = gST->ConOut->Mode;
- (*ConsoleInfo)->Enabled = TRUE;
-
- Status = ConsoleLoggerResetBuffers (*ConsoleInfo);
- if (EFI_ERROR (Status)) {
- SHELL_FREE_NON_NULL ((*ConsoleInfo));
- *ConsoleInfo = NULL;
- return (Status);
- }
-
- Status = gBS->InstallProtocolInterface (&gImageHandle, &gEfiSimpleTextOutProtocolGuid, EFI_NATIVE_INTERFACE, (VOID *)&((*ConsoleInfo)->OurConOut));
- if (EFI_ERROR (Status)) {
- SHELL_FREE_NON_NULL ((*ConsoleInfo)->Buffer);
- SHELL_FREE_NON_NULL ((*ConsoleInfo)->Attributes);
- SHELL_FREE_NON_NULL ((*ConsoleInfo));
- *ConsoleInfo = NULL;
- return (Status);
- }
-
- gST->ConsoleOutHandle = gImageHandle;
- gST->ConOut = &(*ConsoleInfo)->OurConOut;
-
- //
- // Update the CRC32 in the EFI System Table header
- //
- gST->Hdr.CRC32 = 0;
- gBS->CalculateCrc32 (
- (UINT8 *)&gST->Hdr,
- gST->Hdr.HeaderSize,
- &gST->Hdr.CRC32
- );
- return (Status);
-}
-
-/**
- Return the system to the state it was before InstallConsoleLogger
- was installed.
-
- @param[in] ConsoleInfo The object from the install function.
-
- @retval EFI_SUCCESS The operation was successful
- @return other The operation failed. This was from UninstallProtocolInterface.
-**/
-EFI_STATUS
-ConsoleLoggerUninstall (
- IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
- )
-{
- ASSERT (ConsoleInfo != NULL);
- ASSERT (ConsoleInfo->OldConOut != NULL);
-
- if (ConsoleInfo->Buffer != NULL) {
- FreePool (ConsoleInfo->Buffer);
- DEBUG_CODE (
- ConsoleInfo->Buffer = NULL;
- );
- DEBUG_CODE (
- ConsoleInfo->BufferSize = 0;
- );
- }
-
- if (ConsoleInfo->Attributes != NULL) {
- FreePool (ConsoleInfo->Attributes);
- DEBUG_CODE (
- ConsoleInfo->Attributes = NULL;
- );
- DEBUG_CODE (
- ConsoleInfo->AttribSize = 0;
- );
- }
-
- gST->ConsoleOutHandle = ConsoleInfo->OldConHandle;
- gST->ConOut = ConsoleInfo->OldConOut;
-
- //
- // Update the CRC32 in the EFI System Table header
- //
- gST->Hdr.CRC32 = 0;
- gBS->CalculateCrc32 (
- (UINT8 *)&gST->Hdr,
- gST->Hdr.HeaderSize,
- &gST->Hdr.CRC32
- );
-
- return (gBS->UninstallProtocolInterface (gImageHandle, &gEfiSimpleTextOutProtocolGuid, (VOID *)&ConsoleInfo->OurConOut));
-}
-
-/**
- Displays previously logged output back to the screen.
-
- This will scroll the screen forwards and backwards through the log of previous
- output. If Rows is 0 then the size of 1/2 the screen will be scrolled. If Rows
- is (UINTN)(-1) then the size of the screen will be scrolled.
-
- @param[in] Forward If TRUE then the log will be displayed forwards (scroll to newer).
- If FALSE then the log will be displayed backwards (scroll to older).
- @param[in] Rows Determines how many rows the log should scroll.
- @param[in] ConsoleInfo The pointer to the instance of the console logger information.
-**/
-EFI_STATUS
-ConsoleLoggerDisplayHistory (
- IN CONST BOOLEAN Forward,
- IN CONST UINTN Rows,
- IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
- )
-{
- UINTN RowChange;
-
- ASSERT (ConsoleInfo != NULL);
-
- //
- // Calculate the row number change
- //
- switch (Rows) {
- case ((UINTN)(-1)):
- RowChange = ConsoleInfo->RowsPerScreen;
- break;
- case (0):
- RowChange = ConsoleInfo->RowsPerScreen / 2;
- break;
- default:
- RowChange = Rows;
- break;
- }
-
- //
- // Do the math for direction
- //
- if (Forward) {
- if ((ConsoleInfo->OriginalStartRow - ConsoleInfo->CurrentStartRow) < RowChange) {
- RowChange = ConsoleInfo->OriginalStartRow - ConsoleInfo->CurrentStartRow;
- }
- } else {
- if (ConsoleInfo->CurrentStartRow < RowChange) {
- RowChange = ConsoleInfo->CurrentStartRow;
- }
- }
-
- //
- // If we are already at one end or the other
- //
- if (RowChange == 0) {
- return (EFI_SUCCESS);
- }
-
- //
- // Clear the screen
- //
- ConsoleInfo->OldConOut->ClearScreen (ConsoleInfo->OldConOut);
-
- //
- // Set the new start row
- //
- if (Forward) {
- ConsoleInfo->CurrentStartRow += RowChange;
- } else {
- ConsoleInfo->CurrentStartRow -= RowChange;
- }
-
- //
- // Change the screen
- //
- return (UpdateDisplayFromHistory (ConsoleInfo));
-}
-
-/**
- Function to return to normal output whent he scrolling is complete.
- @param[in] ConsoleInfo The pointer to the instance of the console logger information.
-
- @retval EFI_SUCCESS The operation was successful.
- @return other The operation failed. See UpdateDisplayFromHistory.
-
- @sa UpdateDisplayFromHistory
-**/
-EFI_STATUS
-ConsoleLoggerStopHistory (
- IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
- )
-{
- ASSERT (ConsoleInfo != NULL);
- if (ConsoleInfo->CurrentStartRow == ConsoleInfo->OriginalStartRow) {
- return (EFI_SUCCESS);
- }
-
- //
- // Clear the screen
- //
- ConsoleInfo->OldConOut->ClearScreen (ConsoleInfo->OldConOut);
-
- ConsoleInfo->CurrentStartRow = ConsoleInfo->OriginalStartRow;
- return (UpdateDisplayFromHistory (ConsoleInfo));
-}
-
-/**
- Updates the hidden ConOut to be displaying the correct stuff.
- @param[in] ConsoleInfo The pointer to the instance of the console logger information.
-
- @retval EFI_SUCCESS The operation was successful.
- @return other The operation failed.
-**/
-EFI_STATUS
-UpdateDisplayFromHistory (
- IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
- )
-{
- EFI_STATUS Status;
- EFI_STATUS RetVal;
- CHAR16 *Screen;
- INT32 *Attributes;
- UINTN CurrentRow;
- CHAR16 TempCharHolder;
- UINTN Column;
- INT32 CurrentAttrib;
- UINTN CurrentColumn;
- CHAR16 *StringSegment;
- CHAR16 *StringSegmentEnd;
- CHAR16 StringSegmentEndChar;
- INT32 OrigAttribute;
-
- ASSERT (ConsoleInfo != NULL);
- TempCharHolder = CHAR_NULL;
- RetVal = EFI_SUCCESS;
- OrigAttribute = ConsoleInfo->OldConOut->Mode->Attribute;
-
- //
- // Disable cursor visibility and move it to the top left corner
- //
- ConsoleInfo->OldConOut->EnableCursor (ConsoleInfo->OldConOut, FALSE);
- ConsoleInfo->OldConOut->SetCursorPosition (ConsoleInfo->OldConOut, 0, 0);
-
- Screen = &ConsoleInfo->Buffer[(ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->CurrentStartRow];
- Attributes = &ConsoleInfo->Attributes[ConsoleInfo->ColsPerScreen * ConsoleInfo->CurrentStartRow];
- for ( CurrentRow = 0
- ; CurrentRow < ConsoleInfo->RowsPerScreen
- ; CurrentRow++,
- Screen += (ConsoleInfo->ColsPerScreen + 2),
- Attributes += ConsoleInfo->ColsPerScreen
- )
- {
- //
- // dont use the last char - prevents screen scroll
- //
- if (CurrentRow == (ConsoleInfo->RowsPerScreen-1)) {
- TempCharHolder = Screen[ConsoleInfo->ColsPerScreen - 1];
- Screen[ConsoleInfo->ColsPerScreen - 1] = CHAR_NULL;
- }
-
- for ( Column = 0
- ; Column < ConsoleInfo->ColsPerScreen
- ; Column++
- )
- {
- if (Screen[Column] != CHAR_NULL) {
- CurrentAttrib = Attributes[Column];
- CurrentColumn = Column;
- StringSegment = &Screen[Column];
-
- //
- // Find the first char with a different attribute and make that temporarily NULL
- // so we can do fewer printout statements. (later) restore that one and we will
- // start at that column on the next loop.
- //
- StringSegmentEndChar = CHAR_NULL;
- for ( StringSegmentEnd = StringSegment
- ; *StringSegmentEnd != CHAR_NULL
- ; StringSegmentEnd++,
- Column++
- )
- {
- if (Attributes[Column] != CurrentAttrib) {
- StringSegmentEndChar = *StringSegmentEnd;
- *StringSegmentEnd = CHAR_NULL;
- break;
- }
- } // StringSegmentEnd loop
-
- //
- // Now write out as much as had the same Attributes
- //
-
- ConsoleInfo->OldConOut->SetAttribute (ConsoleInfo->OldConOut, CurrentAttrib);
- ConsoleInfo->OldConOut->SetCursorPosition (ConsoleInfo->OldConOut, CurrentColumn, CurrentRow);
- Status = ConsoleInfo->OldConOut->OutputString (ConsoleInfo->OldConOut, StringSegment);
-
- if (EFI_ERROR (Status)) {
- ASSERT (FALSE);
- RetVal = Status;
- }
-
- //
- // If we found a change in attribute put the character back and decrement the column
- // so when it increments it will point at that character and we will start printing
- // a segment with that new attribute
- //
- if (StringSegmentEndChar != CHAR_NULL) {
- *StringSegmentEnd = StringSegmentEndChar;
- StringSegmentEndChar = CHAR_NULL;
- Column--;
- }
- }
- } // column for loop
-
- //
- // If we removed the last char and this was the last row put it back
- //
- if (TempCharHolder != CHAR_NULL) {
- Screen[ConsoleInfo->ColsPerScreen - 1] = TempCharHolder;
- TempCharHolder = CHAR_NULL;
- }
- } // row for loop
-
- //
- // If we are setting the screen back to original turn on the cursor and make it visible
- // and set the attributes back to what they were
- //
- if (ConsoleInfo->CurrentStartRow == ConsoleInfo->OriginalStartRow) {
- ConsoleInfo->OldConOut->SetAttribute (
- ConsoleInfo->OldConOut,
- ConsoleInfo->HistoryMode.Attribute
- );
- ConsoleInfo->OldConOut->SetCursorPosition (
- ConsoleInfo->OldConOut,
- ConsoleInfo->HistoryMode.CursorColumn,
- ConsoleInfo->HistoryMode.CursorRow - ConsoleInfo->OriginalStartRow
- );
-
- Status = ConsoleInfo->OldConOut->EnableCursor (
- ConsoleInfo->OldConOut,
- ConsoleInfo->HistoryMode.CursorVisible
- );
- if (EFI_ERROR (Status)) {
- RetVal = Status;
- }
- } else {
- ConsoleInfo->OldConOut->SetAttribute (
- ConsoleInfo->OldConOut,
- OrigAttribute
- );
- }
-
- return (RetVal);
-}
-
-/**
- Reset the text output device hardware and optionally run diagnostics
-
- @param This pointer to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
- @param ExtendedVerification Indicates that a more extensive test may be performed
-
- @retval EFI_SUCCESS The text output device was reset.
- @retval EFI_DEVICE_ERROR The text output device is not functioning correctly and
- could not be reset.
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerReset (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN BOOLEAN ExtendedVerification
- )
-{
- EFI_STATUS Status;
- CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
-
- ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This);
-
- //
- // Forward the request to the original ConOut
- //
- Status = ConsoleInfo->OldConOut->Reset (ConsoleInfo->OldConOut, ExtendedVerification);
-
- //
- // Check that the buffers are still correct for logging
- //
- if (!EFI_ERROR (Status)) {
- ConsoleLoggerResetBuffers (ConsoleInfo);
- if (ExtendedVerification) {
- ConsoleInfo->OriginalStartRow = 0;
- ConsoleInfo->CurrentStartRow = 0;
- }
- }
-
- return Status;
-}
-
-/**
- Appends a string to the history buffer. If the buffer is full then the oldest
- information in the buffer will be dropped. Information is added in a line by
- line manner such that an empty line takes up just as much space as a full line.
-
- @param[in] String String pointer to add.
- @param[in] ConsoleInfo The pointer to the instance of the console logger information.
-**/
-EFI_STATUS
-AppendStringToHistory (
- IN CONST CHAR16 *String,
- IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
- )
-{
- CONST CHAR16 *Walker;
- UINTN CopySize;
- UINTN PrintIndex;
- UINTN Index;
-
- ASSERT (ConsoleInfo != NULL);
-
- for ( Walker = String
- ; Walker != NULL && *Walker != CHAR_NULL
- ; Walker++
- )
- {
- switch (*Walker) {
- case (CHAR_BACKSPACE):
- if (ConsoleInfo->HistoryMode.CursorColumn > 0) {
- ConsoleInfo->HistoryMode.CursorColumn--;
- }
-
- break;
- case (CHAR_LINEFEED):
- if (ConsoleInfo->HistoryMode.CursorRow >= (INT32)((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)-1)) {
- //
- // Should never be bigger
- //
- ASSERT (ConsoleInfo->HistoryMode.CursorRow == (INT32)((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)-1));
-
- //
- // scroll history attributes 'up' 1 row and set the last row to default attribute
- //
- CopySize = ConsoleInfo->ColsPerScreen
- * ((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) - 1)
- * sizeof (ConsoleInfo->Attributes[0]);
- ASSERT (CopySize < ConsoleInfo->AttribSize);
- CopyMem (
- ConsoleInfo->Attributes,
- ConsoleInfo->Attributes + ConsoleInfo->ColsPerScreen,
- CopySize
- );
-
- for ( Index = 0
- ; Index < ConsoleInfo->ColsPerScreen
- ; Index++
- )
- {
- *(ConsoleInfo->Attributes + (CopySize/sizeof (ConsoleInfo->Attributes[0])) + Index) = ConsoleInfo->HistoryMode.Attribute;
- }
-
- //
- // scroll history buffer 'up' 1 row and set the last row to spaces (L' ')
- //
- CopySize = (ConsoleInfo->ColsPerScreen + 2)
- * ((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) - 1)
- * sizeof (ConsoleInfo->Buffer[0]);
- ASSERT (CopySize < ConsoleInfo->BufferSize);
- CopyMem (
- ConsoleInfo->Buffer,
- ConsoleInfo->Buffer + (ConsoleInfo->ColsPerScreen + 2),
- CopySize
- );
-
- //
- // Set that last row of chars to spaces
- //
- SetMem16 (((UINT8 *)ConsoleInfo->Buffer)+CopySize, ConsoleInfo->ColsPerScreen*sizeof (CHAR16), L' ');
- } else {
- //
- // we are not on the last row
- //
-
- //
- // We should not be scrolling history
- //
- ASSERT (ConsoleInfo->OriginalStartRow == ConsoleInfo->CurrentStartRow);
- //
- // are we at the end of a row?
- //
- if (ConsoleInfo->HistoryMode.CursorRow == (INT32)(ConsoleInfo->OriginalStartRow + ConsoleInfo->RowsPerScreen - 1)) {
- ConsoleInfo->OriginalStartRow++;
- ConsoleInfo->CurrentStartRow++;
- }
-
- ConsoleInfo->HistoryMode.CursorRow++;
- }
-
- break;
- case (CHAR_CARRIAGE_RETURN):
- //
- // Move the cursor to the beginning of the current row.
- //
- ConsoleInfo->HistoryMode.CursorColumn = 0;
- break;
- default:
- //
- // Acrtually print characters into the history buffer
- //
-
- PrintIndex = ConsoleInfo->HistoryMode.CursorRow * ConsoleInfo->ColsPerScreen + ConsoleInfo->HistoryMode.CursorColumn;
-
- for ( // no initializer needed
- ; ConsoleInfo->HistoryMode.CursorColumn < (INT32)ConsoleInfo->ColsPerScreen
- ; ConsoleInfo->HistoryMode.CursorColumn++,
- PrintIndex++,
- Walker++
- )
- {
- if ( (*Walker == CHAR_NULL)
- || (*Walker == CHAR_BACKSPACE)
- || (*Walker == CHAR_LINEFEED)
- || (*Walker == CHAR_CARRIAGE_RETURN)
- )
- {
- Walker--;
- break;
- }
-
- //
- // The buffer is 2*CursorRow more since it has that many \r\n characters at the end of each row.
- //
-
- ASSERT (PrintIndex + ConsoleInfo->HistoryMode.CursorRow < ConsoleInfo->BufferSize);
- ConsoleInfo->Buffer[PrintIndex + (2*ConsoleInfo->HistoryMode.CursorRow)] = *Walker;
- ASSERT (PrintIndex < ConsoleInfo->AttribSize);
- ConsoleInfo->Attributes[PrintIndex] = ConsoleInfo->HistoryMode.Attribute;
- } // for loop
-
- //
- // Add the carriage return and line feed at the end of the lines
- //
- if (ConsoleInfo->HistoryMode.CursorColumn >= (INT32)ConsoleInfo->ColsPerScreen) {
- AppendStringToHistory (L"\r\n", ConsoleInfo);
- Walker--;
- }
-
- break;
- } // switch for character
- } // for loop
-
- return (EFI_SUCCESS);
-}
-
-/**
- Worker function to handle printing the output to the screen
- and the history buffer
-
- @param[in] String The string to output
- @param[in] ConsoleInfo The pointer to the instance of the console logger information.
-
- @retval EFI_SUCCESS The string was printed
- @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
- the text.
- @retval EFI_UNSUPPORTED The output device's mode is not currently in a
- defined text mode.
- @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
- characters in the Unicode string could not be
- rendered and were skipped.
-**/
-EFI_STATUS
-ConsoleLoggerOutputStringSplit (
- IN CONST CHAR16 *String,
- IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
- )
-{
- EFI_STATUS Status;
-
- //
- // Forward the request to the original ConOut
- //
- Status = ConsoleInfo->OldConOut->OutputString (ConsoleInfo->OldConOut, (CHAR16 *)String);
-
- if (EFI_ERROR (Status)) {
- return (Status);
- }
-
- return (AppendStringToHistory (String, ConsoleInfo));
-}
-
-/**
- Function to handle page break mode.
-
- This function will prompt for continue or break.
-
- @retval EFI_SUCCESS Continue was choosen
- @return other Break was choosen
-**/
-EFI_STATUS
-ConsoleLoggerDoPageBreak (
- VOID
- )
-{
- SHELL_PROMPT_RESPONSE *Resp;
- EFI_STATUS Status;
-
- Resp = NULL;
- ASSERT (ShellInfoObject.PageBreakEnabled);
- ShellInfoObject.PageBreakEnabled = FALSE;
- Status = ShellPromptForResponseHii (ShellPromptResponseTypeQuitContinue, STRING_TOKEN (STR_SHELL_QUIT_CONT), ShellInfoObject.HiiHandle, (VOID **)&Resp);
- ShellInfoObject.PageBreakEnabled = TRUE;
- ASSERT (Resp != NULL);
- if (Resp == NULL) {
- return (EFI_NOT_FOUND);
- }
-
- if (EFI_ERROR (Status)) {
- if (Resp != NULL) {
- FreePool (Resp);
- }
-
- return (Status);
- }
-
- if (*Resp == ShellPromptResponseContinue) {
- FreePool (Resp);
- ShellInfoObject.ConsoleInfo->RowCounter = 0;
- // ShellInfoObject.ConsoleInfo->OurConOut.Mode->CursorRow = 0;
- // ShellInfoObject.ConsoleInfo->OurConOut.Mode->CursorColumn = 0;
-
- return (EFI_SUCCESS);
- } else if (*Resp == ShellPromptResponseQuit) {
- FreePool (Resp);
- ShellInfoObject.ConsoleInfo->Enabled = FALSE;
- //
- // When user wants to quit, the shell should stop running the command.
- //
- gBS->SignalEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);
- return (EFI_DEVICE_ERROR);
- } else {
- ASSERT (FALSE);
- }
-
- return (EFI_SUCCESS);
-}
-
-/**
- Worker function to handle printing the output with page breaks.
-
- @param[in] String The string to output
- @param[in] ConsoleInfo The pointer to the instance of the console logger information.
-
- @retval EFI_SUCCESS The string was printed
- @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
- the text.
- @retval EFI_UNSUPPORTED The output device's mode is not currently in a
- defined text mode.
- @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
- characters in the Unicode string could not be
- rendered and were skipped.
-**/
-EFI_STATUS
-ConsoleLoggerPrintWithPageBreak (
- IN CONST CHAR16 *String,
- IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
- )
-{
- CONST CHAR16 *Walker;
- CONST CHAR16 *LineStart;
- CHAR16 *StringCopy;
- CHAR16 TempChar;
-
- StringCopy = NULL;
- StringCopy = StrnCatGrow (&StringCopy, NULL, String, 0);
- if (StringCopy == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- for ( Walker = StringCopy,
- LineStart = StringCopy
- ; Walker != NULL && *Walker != CHAR_NULL
- ; Walker++
- )
- {
- switch (*Walker) {
- case (CHAR_BACKSPACE):
- if (ConsoleInfo->OurConOut.Mode->CursorColumn > 0) {
- ConsoleInfo->OurConOut.Mode->CursorColumn--;
- }
-
- break;
- case (CHAR_LINEFEED):
- //
- // add a temp NULL terminator
- //
- TempChar = *(Walker + 1);
- *((CHAR16 *)(Walker+1)) = CHAR_NULL;
-
- //
- // output the string
- //
- ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo);
-
- //
- // restore the temp NULL terminator to its original character
- //
- *((CHAR16 *)(Walker+1)) = TempChar;
-
- //
- // Update LineStart Variable
- //
- LineStart = Walker + 1;
-
- //
- // increment row count
- //
- ShellInfoObject.ConsoleInfo->RowCounter++;
- ConsoleInfo->OurConOut.Mode->CursorRow++;
-
- break;
- case (CHAR_CARRIAGE_RETURN):
- //
- // Move the cursor to the beginning of the current row.
- //
- ConsoleInfo->OurConOut.Mode->CursorColumn = 0;
- break;
- default:
- //
- // increment column count
- //
- ConsoleInfo->OurConOut.Mode->CursorColumn++;
- //
- // check if that is the last column
- //
- if ((INTN)ConsoleInfo->ColsPerScreen == ConsoleInfo->OurConOut.Mode->CursorColumn + 1) {
- //
- // output a line similar to the linefeed character.
- //
-
- //
- // add a temp NULL terminator
- //
- TempChar = *(Walker + 1);
- *((CHAR16 *)(Walker+1)) = CHAR_NULL;
-
- //
- // output the string
- //
- ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo);
-
- //
- // restore the temp NULL terminator to its original character
- //
- *((CHAR16 *)(Walker+1)) = TempChar;
-
- //
- // Update LineStart Variable
- //
- LineStart = Walker + 1;
-
- //
- // increment row count and zero the column
- //
- ShellInfoObject.ConsoleInfo->RowCounter++;
- ConsoleInfo->OurConOut.Mode->CursorRow++;
- ConsoleInfo->OurConOut.Mode->CursorColumn = 0;
- } // last column on line
-
- break;
- } // switch for character
-
- //
- // check if that was the last printable row. If yes handle PageBreak mode
- //
- if ((ConsoleInfo->RowsPerScreen) -1 == ShellInfoObject.ConsoleInfo->RowCounter) {
- if (EFI_ERROR (ConsoleLoggerDoPageBreak ())) {
- //
- // We got an error which means 'break' and halt the printing
- //
- SHELL_FREE_NON_NULL (StringCopy);
- return (EFI_DEVICE_ERROR);
- }
- }
- } // for loop
-
- if ((LineStart != NULL) && (*LineStart != CHAR_NULL)) {
- ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo);
- }
-
- SHELL_FREE_NON_NULL (StringCopy);
- return (EFI_SUCCESS);
-}
-
-/**
- Write a Unicode string to the output device.
-
- @param[in] This Protocol instance pointer.
- @param[in] WString The NULL-terminated Unicode string to be displayed on the output
- device(s). All output devices must also support the Unicode
- drawing defined in this file.
- @retval EFI_SUCCESS The string was output to the device.
- @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
- the text.
- @retval EFI_UNSUPPORTED The output device's mode is not currently in a
- defined text mode.
- @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
- characters in the Unicode string could not be
- rendered and were skipped.
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerOutputString (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN CHAR16 *WString
- )
-{
- EFI_STATUS Status;
- EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx;
- EFI_KEY_DATA KeyData;
- UINTN EventIndex;
- CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
-
- ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This);
- if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
- return (EFI_UNSUPPORTED);
- }
-
- ASSERT (ShellInfoObject.ConsoleInfo == ConsoleInfo);
-
- Status = gBS->HandleProtocol (gST->ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID **)&TxtInEx);
- if (!EFI_ERROR (Status)) {
- while (ShellInfoObject.HaltOutput) {
- ShellInfoObject.HaltOutput = FALSE;
- //
- // just get some key
- //
- Status = gBS->WaitForEvent (1, &TxtInEx->WaitForKeyEx, &EventIndex);
- ASSERT_EFI_ERROR (Status);
- Status = TxtInEx->ReadKeyStrokeEx (TxtInEx, &KeyData);
- if (EFI_ERROR (Status)) {
- break;
- }
-
- if ((KeyData.Key.UnicodeChar == L's') && (KeyData.Key.ScanCode == SCAN_NULL) &&
- ((KeyData.KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID | EFI_LEFT_CONTROL_PRESSED)) ||
- (KeyData.KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID | EFI_RIGHT_CONTROL_PRESSED))
- )
- )
- {
- ShellInfoObject.HaltOutput = TRUE;
- }
- }
- }
-
- if (!ShellInfoObject.ConsoleInfo->Enabled) {
- return (EFI_DEVICE_ERROR);
- } else if (ShellInfoObject.PageBreakEnabled) {
- return (ConsoleLoggerPrintWithPageBreak (WString, ConsoleInfo));
- } else {
- return (ConsoleLoggerOutputStringSplit (WString, ConsoleInfo));
- }
-}
-
-/**
- Verifies that all characters in a Unicode string can be output to the
- target device.
-
- @param[in] This Protocol instance pointer.
- @param[in] WString The NULL-terminated Unicode string to be examined for the output
- device(s).
-
- @retval EFI_SUCCESS The device(s) are capable of rendering the output string.
- @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be
- rendered by one or more of the output devices mapped
- by the EFI handle.
-
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerTestString (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN CHAR16 *WString
- )
-{
- CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
-
- ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This);
- //
- // Forward the request to the original ConOut
- //
- return (ConsoleInfo->OldConOut->TestString (ConsoleInfo->OldConOut, WString));
-}
-
-/**
- Returns information for an available text mode that the output device(s)
- supports.
-
- @param[in] This Protocol instance pointer.
- @param[in] ModeNumber The mode number to return information on.
- @param[out] Columns Upon return, the number of columns in the selected geometry
- @param[out] Rows Upon return, the number of rows in the selected geometry
-
- @retval EFI_SUCCESS The requested mode information was returned.
- @retval EFI_DEVICE_ERROR The device had an error and could not
- complete the request.
- @retval EFI_UNSUPPORTED The mode number was not valid.
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerQueryMode (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN UINTN ModeNumber,
- OUT UINTN *Columns,
- OUT UINTN *Rows
- )
-{
- CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
-
- ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This);
- //
- // Forward the request to the original ConOut
- //
- return (ConsoleInfo->OldConOut->QueryMode (
- ConsoleInfo->OldConOut,
- ModeNumber,
- Columns,
- Rows
- ));
-}
-
-/**
- Sets the output device(s) to a specified mode.
-
- @param[in] This Protocol instance pointer.
- @param[in] ModeNumber The mode number to set.
-
-
- @retval EFI_SUCCESS The requested text mode was set.
- @retval EFI_DEVICE_ERROR The device had an error and
- could not complete the request.
- @retval EFI_UNSUPPORTED The mode number was not valid.
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerSetMode (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN UINTN ModeNumber
- )
-{
- EFI_STATUS Status;
-
- CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
-
- ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This);
-
- //
- // Forward the request to the original ConOut
- //
- Status = ConsoleInfo->OldConOut->SetMode (ConsoleInfo->OldConOut, ModeNumber);
-
- //
- // Check that the buffers are still correct for logging
- //
- if (!EFI_ERROR (Status)) {
- ConsoleInfo->OurConOut.Mode = ConsoleInfo->OldConOut->Mode;
- ConsoleLoggerResetBuffers (ConsoleInfo);
- ConsoleInfo->OriginalStartRow = 0;
- ConsoleInfo->CurrentStartRow = 0;
- ConsoleInfo->OurConOut.ClearScreen (&ConsoleInfo->OurConOut);
- }
-
- return Status;
-}
-
-/**
- Sets the background and foreground colors for the OutputString () and
- ClearScreen () functions.
-
- @param[in] This Protocol instance pointer.
- @param[in] Attribute The attribute to set. Bits 0..3 are the foreground color, and
- bits 4..6 are the background color. All other bits are undefined
- and must be zero. The valid Attributes are defined in this file.
-
- @retval EFI_SUCCESS The attribute was set.
- @retval EFI_DEVICE_ERROR The device had an error and
- could not complete the request.
- @retval EFI_UNSUPPORTED The attribute requested is not defined.
-
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerSetAttribute (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN UINTN Attribute
- )
-{
- EFI_STATUS Status;
-
- CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
-
- ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This);
-
- //
- // Forward the request to the original ConOut
- //
- Status = ConsoleInfo->OldConOut->SetAttribute (ConsoleInfo->OldConOut, Attribute);
-
- //
- // Record console output history
- //
- if (!EFI_ERROR (Status)) {
- ConsoleInfo->HistoryMode.Attribute = (INT32)Attribute;
- }
-
- return Status;
-}
-
-/**
- Clears the output device(s) display to the currently selected background
- color.
-
- @param[in] This Protocol instance pointer.
-
- @retval EFI_SUCCESS The operation completed successfully.
- @retval EFI_DEVICE_ERROR The device had an error and
- could not complete the request.
- @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerClearScreen (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
- )
-{
- EFI_STATUS Status;
- CHAR16 *Screen;
- INT32 *Attributes;
- UINTN Row;
- UINTN Column;
- CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
-
- if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
- return (EFI_UNSUPPORTED);
- }
-
- ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This);
-
- //
- // Forward the request to the original ConOut
- //
- Status = ConsoleInfo->OldConOut->ClearScreen (ConsoleInfo->OldConOut);
-
- //
- // Record console output history
- //
- if (!EFI_ERROR (Status)) {
- Screen = &ConsoleInfo->Buffer[(ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->CurrentStartRow];
- Attributes = &ConsoleInfo->Attributes[ConsoleInfo->ColsPerScreen * ConsoleInfo->CurrentStartRow];
- for ( Row = ConsoleInfo->OriginalStartRow
- ; Row < (ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)
- ; Row++
- )
- {
- for ( Column = 0
- ; Column < ConsoleInfo->ColsPerScreen
- ; Column++,
- Screen++,
- Attributes++
- )
- {
- *Screen = L' ';
- *Attributes = ConsoleInfo->OldConOut->Mode->Attribute;
- }
-
- //
- // Skip the NULL on each column end in text buffer only
- //
- Screen += 2;
- }
-
- ConsoleInfo->HistoryMode.CursorColumn = 0;
- ConsoleInfo->HistoryMode.CursorRow = 0;
- }
-
- return Status;
-}
-
-/**
- Sets the current coordinates of the cursor position
-
- @param[in] This Protocol instance pointer.
- @param[in] Column Column to put the cursor in. Must be between zero and Column returned from QueryMode
- @param[in] Row Row to put the cursor in. Must be between zero and Row returned from QueryMode
-
- @retval EFI_SUCCESS The operation completed successfully.
- @retval EFI_DEVICE_ERROR The device had an error and
- could not complete the request.
- @retval EFI_UNSUPPORTED The output device is not in a valid text mode, or the
- cursor position is invalid for the current mode.
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerSetCursorPosition (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN UINTN Column,
- IN UINTN Row
- )
-{
- EFI_STATUS Status;
- CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
-
- if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
- return (EFI_UNSUPPORTED);
- }
-
- ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This);
- //
- // Forward the request to the original ConOut
- //
- Status = ConsoleInfo->OldConOut->SetCursorPosition (
- ConsoleInfo->OldConOut,
- Column,
- Row
- );
-
- //
- // Record console output history
- //
- if (!EFI_ERROR (Status)) {
- ConsoleInfo->HistoryMode.CursorColumn = (INT32)Column;
- ConsoleInfo->HistoryMode.CursorRow = (INT32)(ConsoleInfo->OriginalStartRow + Row);
- }
-
- return Status;
-}
-
-/**
- Makes the cursor visible or invisible
-
- @param[in] This Protocol instance pointer.
- @param[in] Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is
- set to be invisible.
-
- @retval EFI_SUCCESS The operation completed successfully.
- @retval EFI_DEVICE_ERROR The device had an error and could not complete the
- request, or the device does not support changing
- the cursor mode.
- @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerEnableCursor (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN BOOLEAN Visible
- )
-{
- EFI_STATUS Status;
-
- CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
-
- ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This);
- //
- // Forward the request to the original ConOut
- //
- Status = ConsoleInfo->OldConOut->EnableCursor (ConsoleInfo->OldConOut, Visible);
-
- //
- // Record console output history
- //
- if (!EFI_ERROR (Status)) {
- ConsoleInfo->HistoryMode.CursorVisible = Visible;
- }
-
- return Status;
-}
-
-/**
- Function to update and verify that the current buffers are correct.
-
- @param[in] ConsoleInfo The pointer to the instance of the console logger information.
-
- This will be used when a mode has changed or a reset occurred to verify all
- history buffers.
-**/
-EFI_STATUS
-ConsoleLoggerResetBuffers (
- IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
- )
-{
- EFI_STATUS Status;
-
- if (ConsoleInfo->Buffer != NULL) {
- FreePool (ConsoleInfo->Buffer);
- ConsoleInfo->Buffer = NULL;
- ConsoleInfo->BufferSize = 0;
- }
-
- if (ConsoleInfo->Attributes != NULL) {
- FreePool (ConsoleInfo->Attributes);
- ConsoleInfo->Attributes = NULL;
- ConsoleInfo->AttribSize = 0;
- }
-
- Status = gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &ConsoleInfo->ColsPerScreen, &ConsoleInfo->RowsPerScreen);
- if (EFI_ERROR (Status)) {
- return (Status);
- }
-
- ConsoleInfo->BufferSize = (ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount * sizeof (ConsoleInfo->Buffer[0]);
- ConsoleInfo->AttribSize = ConsoleInfo->ColsPerScreen * ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount * sizeof (ConsoleInfo->Attributes[0]);
-
- ConsoleInfo->Buffer = (CHAR16 *)AllocateZeroPool (ConsoleInfo->BufferSize);
-
- if (ConsoleInfo->Buffer == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- ConsoleInfo->Attributes = (INT32 *)AllocateZeroPool (ConsoleInfo->AttribSize);
- if (ConsoleInfo->Attributes == NULL) {
- FreePool (ConsoleInfo->Buffer);
- ConsoleInfo->Buffer = NULL;
- return (EFI_OUT_OF_RESOURCES);
- }
-
- CopyMem (&ConsoleInfo->HistoryMode, ConsoleInfo->OldConOut->Mode, sizeof (EFI_SIMPLE_TEXT_OUTPUT_MODE));
-
- return (EFI_SUCCESS);
-}
+/** @file + Provides interface to shell console logger. + + (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR> + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2016 Hewlett-Packard Development Company, L.P.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "Shell.h" + +/** + Install our intermediate ConOut into the system table to + keep a log of all the info that is displayed to the user. + + @param[in] ScreensToSave Sets how many screen-worths of data to save. + @param[out] ConsoleInfo The object to pass into later functions. + + @retval EFI_SUCCESS The operation was successful. + @return other The operation failed. + + @sa ConsoleLoggerResetBuffers + @sa InstallProtocolInterface +**/ +EFI_STATUS +ConsoleLoggerInstall ( + IN CONST UINTN ScreensToSave, + OUT CONSOLE_LOGGER_PRIVATE_DATA **ConsoleInfo + ) +{ + EFI_STATUS Status; + + ASSERT (ConsoleInfo != NULL); + + (*ConsoleInfo) = AllocateZeroPool (sizeof (CONSOLE_LOGGER_PRIVATE_DATA)); + if ((*ConsoleInfo) == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + (*ConsoleInfo)->Signature = CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE; + (*ConsoleInfo)->OldConOut = gST->ConOut; + (*ConsoleInfo)->OldConHandle = gST->ConsoleOutHandle; + (*ConsoleInfo)->Buffer = NULL; + (*ConsoleInfo)->BufferSize = 0; + (*ConsoleInfo)->OriginalStartRow = 0; + (*ConsoleInfo)->CurrentStartRow = 0; + (*ConsoleInfo)->RowsPerScreen = 0; + (*ConsoleInfo)->ColsPerScreen = 0; + (*ConsoleInfo)->Attributes = NULL; + (*ConsoleInfo)->AttribSize = 0; + (*ConsoleInfo)->ScreenCount = ScreensToSave; + (*ConsoleInfo)->HistoryMode.MaxMode = 1; + (*ConsoleInfo)->HistoryMode.Mode = 0; + (*ConsoleInfo)->HistoryMode.Attribute = 0; + (*ConsoleInfo)->HistoryMode.CursorColumn = 0; + (*ConsoleInfo)->HistoryMode.CursorRow = 0; + (*ConsoleInfo)->HistoryMode.CursorVisible = FALSE; + (*ConsoleInfo)->OurConOut.Reset = ConsoleLoggerReset; + (*ConsoleInfo)->OurConOut.OutputString = ConsoleLoggerOutputString; + (*ConsoleInfo)->OurConOut.TestString = ConsoleLoggerTestString; + (*ConsoleInfo)->OurConOut.QueryMode = ConsoleLoggerQueryMode; + (*ConsoleInfo)->OurConOut.SetMode = ConsoleLoggerSetMode; + (*ConsoleInfo)->OurConOut.SetAttribute = ConsoleLoggerSetAttribute; + (*ConsoleInfo)->OurConOut.ClearScreen = ConsoleLoggerClearScreen; + (*ConsoleInfo)->OurConOut.SetCursorPosition = ConsoleLoggerSetCursorPosition; + (*ConsoleInfo)->OurConOut.EnableCursor = ConsoleLoggerEnableCursor; + (*ConsoleInfo)->OurConOut.Mode = gST->ConOut->Mode; + (*ConsoleInfo)->Enabled = TRUE; + + Status = ConsoleLoggerResetBuffers (*ConsoleInfo); + if (EFI_ERROR (Status)) { + SHELL_FREE_NON_NULL ((*ConsoleInfo)); + *ConsoleInfo = NULL; + return (Status); + } + + Status = gBS->InstallProtocolInterface (&gImageHandle, &gEfiSimpleTextOutProtocolGuid, EFI_NATIVE_INTERFACE, (VOID *)&((*ConsoleInfo)->OurConOut)); + if (EFI_ERROR (Status)) { + SHELL_FREE_NON_NULL ((*ConsoleInfo)->Buffer); + SHELL_FREE_NON_NULL ((*ConsoleInfo)->Attributes); + SHELL_FREE_NON_NULL ((*ConsoleInfo)); + *ConsoleInfo = NULL; + return (Status); + } + + gST->ConsoleOutHandle = gImageHandle; + gST->ConOut = &(*ConsoleInfo)->OurConOut; + + // + // Update the CRC32 in the EFI System Table header + // + gST->Hdr.CRC32 = 0; + gBS->CalculateCrc32 ( + (UINT8 *)&gST->Hdr, + gST->Hdr.HeaderSize, + &gST->Hdr.CRC32 + ); + return (Status); +} + +/** + Return the system to the state it was before InstallConsoleLogger + was installed. + + @param[in] ConsoleInfo The object from the install function. + + @retval EFI_SUCCESS The operation was successful + @return other The operation failed. This was from UninstallProtocolInterface. +**/ +EFI_STATUS +ConsoleLoggerUninstall ( + IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ) +{ + ASSERT (ConsoleInfo != NULL); + ASSERT (ConsoleInfo->OldConOut != NULL); + + if (ConsoleInfo->Buffer != NULL) { + FreePool (ConsoleInfo->Buffer); + DEBUG_CODE ( + ConsoleInfo->Buffer = NULL; + ); + DEBUG_CODE ( + ConsoleInfo->BufferSize = 0; + ); + } + + if (ConsoleInfo->Attributes != NULL) { + FreePool (ConsoleInfo->Attributes); + DEBUG_CODE ( + ConsoleInfo->Attributes = NULL; + ); + DEBUG_CODE ( + ConsoleInfo->AttribSize = 0; + ); + } + + gST->ConsoleOutHandle = ConsoleInfo->OldConHandle; + gST->ConOut = ConsoleInfo->OldConOut; + + // + // Update the CRC32 in the EFI System Table header + // + gST->Hdr.CRC32 = 0; + gBS->CalculateCrc32 ( + (UINT8 *)&gST->Hdr, + gST->Hdr.HeaderSize, + &gST->Hdr.CRC32 + ); + + return (gBS->UninstallProtocolInterface (gImageHandle, &gEfiSimpleTextOutProtocolGuid, (VOID *)&ConsoleInfo->OurConOut)); +} + +/** + Displays previously logged output back to the screen. + + This will scroll the screen forwards and backwards through the log of previous + output. If Rows is 0 then the size of 1/2 the screen will be scrolled. If Rows + is (UINTN)(-1) then the size of the screen will be scrolled. + + @param[in] Forward If TRUE then the log will be displayed forwards (scroll to newer). + If FALSE then the log will be displayed backwards (scroll to older). + @param[in] Rows Determines how many rows the log should scroll. + @param[in] ConsoleInfo The pointer to the instance of the console logger information. +**/ +EFI_STATUS +ConsoleLoggerDisplayHistory ( + IN CONST BOOLEAN Forward, + IN CONST UINTN Rows, + IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ) +{ + UINTN RowChange; + + ASSERT (ConsoleInfo != NULL); + + // + // Calculate the row number change + // + switch (Rows) { + case ((UINTN)(-1)): + RowChange = ConsoleInfo->RowsPerScreen; + break; + case (0): + RowChange = ConsoleInfo->RowsPerScreen / 2; + break; + default: + RowChange = Rows; + break; + } + + // + // Do the math for direction + // + if (Forward) { + if ((ConsoleInfo->OriginalStartRow - ConsoleInfo->CurrentStartRow) < RowChange) { + RowChange = ConsoleInfo->OriginalStartRow - ConsoleInfo->CurrentStartRow; + } + } else { + if (ConsoleInfo->CurrentStartRow < RowChange) { + RowChange = ConsoleInfo->CurrentStartRow; + } + } + + // + // If we are already at one end or the other + // + if (RowChange == 0) { + return (EFI_SUCCESS); + } + + // + // Clear the screen + // + ConsoleInfo->OldConOut->ClearScreen (ConsoleInfo->OldConOut); + + // + // Set the new start row + // + if (Forward) { + ConsoleInfo->CurrentStartRow += RowChange; + } else { + ConsoleInfo->CurrentStartRow -= RowChange; + } + + // + // Change the screen + // + return (UpdateDisplayFromHistory (ConsoleInfo)); +} + +/** + Function to return to normal output whent he scrolling is complete. + @param[in] ConsoleInfo The pointer to the instance of the console logger information. + + @retval EFI_SUCCESS The operation was successful. + @return other The operation failed. See UpdateDisplayFromHistory. + + @sa UpdateDisplayFromHistory +**/ +EFI_STATUS +ConsoleLoggerStopHistory ( + IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ) +{ + ASSERT (ConsoleInfo != NULL); + if (ConsoleInfo->CurrentStartRow == ConsoleInfo->OriginalStartRow) { + return (EFI_SUCCESS); + } + + // + // Clear the screen + // + ConsoleInfo->OldConOut->ClearScreen (ConsoleInfo->OldConOut); + + ConsoleInfo->CurrentStartRow = ConsoleInfo->OriginalStartRow; + return (UpdateDisplayFromHistory (ConsoleInfo)); +} + +/** + Updates the hidden ConOut to be displaying the correct stuff. + @param[in] ConsoleInfo The pointer to the instance of the console logger information. + + @retval EFI_SUCCESS The operation was successful. + @return other The operation failed. +**/ +EFI_STATUS +UpdateDisplayFromHistory ( + IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ) +{ + EFI_STATUS Status; + EFI_STATUS RetVal; + CHAR16 *Screen; + INT32 *Attributes; + UINTN CurrentRow; + CHAR16 TempCharHolder; + UINTN Column; + INT32 CurrentAttrib; + UINTN CurrentColumn; + CHAR16 *StringSegment; + CHAR16 *StringSegmentEnd; + CHAR16 StringSegmentEndChar; + INT32 OrigAttribute; + + ASSERT (ConsoleInfo != NULL); + TempCharHolder = CHAR_NULL; + RetVal = EFI_SUCCESS; + OrigAttribute = ConsoleInfo->OldConOut->Mode->Attribute; + + // + // Disable cursor visibility and move it to the top left corner + // + ConsoleInfo->OldConOut->EnableCursor (ConsoleInfo->OldConOut, FALSE); + ConsoleInfo->OldConOut->SetCursorPosition (ConsoleInfo->OldConOut, 0, 0); + + Screen = &ConsoleInfo->Buffer[(ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->CurrentStartRow]; + Attributes = &ConsoleInfo->Attributes[ConsoleInfo->ColsPerScreen * ConsoleInfo->CurrentStartRow]; + for ( CurrentRow = 0 + ; CurrentRow < ConsoleInfo->RowsPerScreen + ; CurrentRow++, + Screen += (ConsoleInfo->ColsPerScreen + 2), + Attributes += ConsoleInfo->ColsPerScreen + ) + { + // + // dont use the last char - prevents screen scroll + // + if (CurrentRow == (ConsoleInfo->RowsPerScreen-1)) { + TempCharHolder = Screen[ConsoleInfo->ColsPerScreen - 1]; + Screen[ConsoleInfo->ColsPerScreen - 1] = CHAR_NULL; + } + + for ( Column = 0 + ; Column < ConsoleInfo->ColsPerScreen + ; Column++ + ) + { + if (Screen[Column] != CHAR_NULL) { + CurrentAttrib = Attributes[Column]; + CurrentColumn = Column; + StringSegment = &Screen[Column]; + + // + // Find the first char with a different attribute and make that temporarily NULL + // so we can do fewer printout statements. (later) restore that one and we will + // start at that column on the next loop. + // + StringSegmentEndChar = CHAR_NULL; + for ( StringSegmentEnd = StringSegment + ; *StringSegmentEnd != CHAR_NULL + ; StringSegmentEnd++, + Column++ + ) + { + if (Attributes[Column] != CurrentAttrib) { + StringSegmentEndChar = *StringSegmentEnd; + *StringSegmentEnd = CHAR_NULL; + break; + } + } // StringSegmentEnd loop + + // + // Now write out as much as had the same Attributes + // + + ConsoleInfo->OldConOut->SetAttribute (ConsoleInfo->OldConOut, CurrentAttrib); + ConsoleInfo->OldConOut->SetCursorPosition (ConsoleInfo->OldConOut, CurrentColumn, CurrentRow); + Status = ConsoleInfo->OldConOut->OutputString (ConsoleInfo->OldConOut, StringSegment); + + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + RetVal = Status; + } + + // + // If we found a change in attribute put the character back and decrement the column + // so when it increments it will point at that character and we will start printing + // a segment with that new attribute + // + if (StringSegmentEndChar != CHAR_NULL) { + *StringSegmentEnd = StringSegmentEndChar; + StringSegmentEndChar = CHAR_NULL; + Column--; + } + } + } // column for loop + + // + // If we removed the last char and this was the last row put it back + // + if (TempCharHolder != CHAR_NULL) { + Screen[ConsoleInfo->ColsPerScreen - 1] = TempCharHolder; + TempCharHolder = CHAR_NULL; + } + } // row for loop + + // + // If we are setting the screen back to original turn on the cursor and make it visible + // and set the attributes back to what they were + // + if (ConsoleInfo->CurrentStartRow == ConsoleInfo->OriginalStartRow) { + ConsoleInfo->OldConOut->SetAttribute ( + ConsoleInfo->OldConOut, + ConsoleInfo->HistoryMode.Attribute + ); + ConsoleInfo->OldConOut->SetCursorPosition ( + ConsoleInfo->OldConOut, + ConsoleInfo->HistoryMode.CursorColumn, + ConsoleInfo->HistoryMode.CursorRow - ConsoleInfo->OriginalStartRow + ); + + Status = ConsoleInfo->OldConOut->EnableCursor ( + ConsoleInfo->OldConOut, + ConsoleInfo->HistoryMode.CursorVisible + ); + if (EFI_ERROR (Status)) { + RetVal = Status; + } + } else { + ConsoleInfo->OldConOut->SetAttribute ( + ConsoleInfo->OldConOut, + OrigAttribute + ); + } + + return (RetVal); +} + +/** + Reset the text output device hardware and optionally run diagnostics + + @param This pointer to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL + @param ExtendedVerification Indicates that a more extensive test may be performed + + @retval EFI_SUCCESS The text output device was reset. + @retval EFI_DEVICE_ERROR The text output device is not functioning correctly and + could not be reset. +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerReset ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; + + ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This); + + // + // Forward the request to the original ConOut + // + Status = ConsoleInfo->OldConOut->Reset (ConsoleInfo->OldConOut, ExtendedVerification); + + // + // Check that the buffers are still correct for logging + // + if (!EFI_ERROR (Status)) { + ConsoleLoggerResetBuffers (ConsoleInfo); + if (ExtendedVerification) { + ConsoleInfo->OriginalStartRow = 0; + ConsoleInfo->CurrentStartRow = 0; + } + } + + return Status; +} + +/** + Appends a string to the history buffer. If the buffer is full then the oldest + information in the buffer will be dropped. Information is added in a line by + line manner such that an empty line takes up just as much space as a full line. + + @param[in] String String pointer to add. + @param[in] ConsoleInfo The pointer to the instance of the console logger information. +**/ +EFI_STATUS +AppendStringToHistory ( + IN CONST CHAR16 *String, + IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ) +{ + CONST CHAR16 *Walker; + UINTN CopySize; + UINTN PrintIndex; + UINTN Index; + + ASSERT (ConsoleInfo != NULL); + + for ( Walker = String + ; Walker != NULL && *Walker != CHAR_NULL + ; Walker++ + ) + { + switch (*Walker) { + case (CHAR_BACKSPACE): + if (ConsoleInfo->HistoryMode.CursorColumn > 0) { + ConsoleInfo->HistoryMode.CursorColumn--; + } + + break; + case (CHAR_LINEFEED): + if (ConsoleInfo->HistoryMode.CursorRow >= (INT32)((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)-1)) { + // + // Should never be bigger + // + ASSERT (ConsoleInfo->HistoryMode.CursorRow == (INT32)((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)-1)); + + // + // scroll history attributes 'up' 1 row and set the last row to default attribute + // + CopySize = ConsoleInfo->ColsPerScreen + * ((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) - 1) + * sizeof (ConsoleInfo->Attributes[0]); + ASSERT (CopySize < ConsoleInfo->AttribSize); + CopyMem ( + ConsoleInfo->Attributes, + ConsoleInfo->Attributes + ConsoleInfo->ColsPerScreen, + CopySize + ); + + for ( Index = 0 + ; Index < ConsoleInfo->ColsPerScreen + ; Index++ + ) + { + *(ConsoleInfo->Attributes + (CopySize/sizeof (ConsoleInfo->Attributes[0])) + Index) = ConsoleInfo->HistoryMode.Attribute; + } + + // + // scroll history buffer 'up' 1 row and set the last row to spaces (L' ') + // + CopySize = (ConsoleInfo->ColsPerScreen + 2) + * ((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) - 1) + * sizeof (ConsoleInfo->Buffer[0]); + ASSERT (CopySize < ConsoleInfo->BufferSize); + CopyMem ( + ConsoleInfo->Buffer, + ConsoleInfo->Buffer + (ConsoleInfo->ColsPerScreen + 2), + CopySize + ); + + // + // Set that last row of chars to spaces + // + SetMem16 (((UINT8 *)ConsoleInfo->Buffer)+CopySize, ConsoleInfo->ColsPerScreen*sizeof (CHAR16), L' '); + } else { + // + // we are not on the last row + // + + // + // We should not be scrolling history + // + ASSERT (ConsoleInfo->OriginalStartRow == ConsoleInfo->CurrentStartRow); + // + // are we at the end of a row? + // + if (ConsoleInfo->HistoryMode.CursorRow == (INT32)(ConsoleInfo->OriginalStartRow + ConsoleInfo->RowsPerScreen - 1)) { + ConsoleInfo->OriginalStartRow++; + ConsoleInfo->CurrentStartRow++; + } + + ConsoleInfo->HistoryMode.CursorRow++; + } + + break; + case (CHAR_CARRIAGE_RETURN): + // + // Move the cursor to the beginning of the current row. + // + ConsoleInfo->HistoryMode.CursorColumn = 0; + break; + default: + // + // Acrtually print characters into the history buffer + // + + PrintIndex = ConsoleInfo->HistoryMode.CursorRow * ConsoleInfo->ColsPerScreen + ConsoleInfo->HistoryMode.CursorColumn; + + for ( // no initializer needed + ; ConsoleInfo->HistoryMode.CursorColumn < (INT32)ConsoleInfo->ColsPerScreen + ; ConsoleInfo->HistoryMode.CursorColumn++, + PrintIndex++, + Walker++ + ) + { + if ( (*Walker == CHAR_NULL) + || (*Walker == CHAR_BACKSPACE) + || (*Walker == CHAR_LINEFEED) + || (*Walker == CHAR_CARRIAGE_RETURN) + ) + { + Walker--; + break; + } + + // + // The buffer is 2*CursorRow more since it has that many \r\n characters at the end of each row. + // + + ASSERT (PrintIndex + ConsoleInfo->HistoryMode.CursorRow < ConsoleInfo->BufferSize); + ConsoleInfo->Buffer[PrintIndex + (2*ConsoleInfo->HistoryMode.CursorRow)] = *Walker; + ASSERT (PrintIndex < ConsoleInfo->AttribSize); + ConsoleInfo->Attributes[PrintIndex] = ConsoleInfo->HistoryMode.Attribute; + } // for loop + + // + // Add the carriage return and line feed at the end of the lines + // + if (ConsoleInfo->HistoryMode.CursorColumn >= (INT32)ConsoleInfo->ColsPerScreen) { + AppendStringToHistory (L"\r\n", ConsoleInfo); + Walker--; + } + + break; + } // switch for character + } // for loop + + return (EFI_SUCCESS); +} + +/** + Worker function to handle printing the output to the screen + and the history buffer + + @param[in] String The string to output + @param[in] ConsoleInfo The pointer to the instance of the console logger information. + + @retval EFI_SUCCESS The string was printed + @retval EFI_DEVICE_ERROR The device reported an error while attempting to output + the text. + @retval EFI_UNSUPPORTED The output device's mode is not currently in a + defined text mode. + @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the + characters in the Unicode string could not be + rendered and were skipped. +**/ +EFI_STATUS +ConsoleLoggerOutputStringSplit ( + IN CONST CHAR16 *String, + IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ) +{ + EFI_STATUS Status; + + // + // Forward the request to the original ConOut + // + Status = ConsoleInfo->OldConOut->OutputString (ConsoleInfo->OldConOut, (CHAR16 *)String); + + if (EFI_ERROR (Status)) { + return (Status); + } + + return (AppendStringToHistory (String, ConsoleInfo)); +} + +/** + Function to handle page break mode. + + This function will prompt for continue or break. + + @retval EFI_SUCCESS Continue was choosen + @return other Break was choosen +**/ +EFI_STATUS +ConsoleLoggerDoPageBreak ( + VOID + ) +{ + SHELL_PROMPT_RESPONSE *Resp; + EFI_STATUS Status; + + Resp = NULL; + ASSERT (ShellInfoObject.PageBreakEnabled); + ShellInfoObject.PageBreakEnabled = FALSE; + Status = ShellPromptForResponseHii (ShellPromptResponseTypeQuitContinue, STRING_TOKEN (STR_SHELL_QUIT_CONT), ShellInfoObject.HiiHandle, (VOID **)&Resp); + ShellInfoObject.PageBreakEnabled = TRUE; + ASSERT (Resp != NULL); + if (Resp == NULL) { + return (EFI_NOT_FOUND); + } + + if (EFI_ERROR (Status)) { + if (Resp != NULL) { + FreePool (Resp); + } + + return (Status); + } + + if (*Resp == ShellPromptResponseContinue) { + FreePool (Resp); + ShellInfoObject.ConsoleInfo->RowCounter = 0; + // ShellInfoObject.ConsoleInfo->OurConOut.Mode->CursorRow = 0; + // ShellInfoObject.ConsoleInfo->OurConOut.Mode->CursorColumn = 0; + + return (EFI_SUCCESS); + } else if (*Resp == ShellPromptResponseQuit) { + FreePool (Resp); + ShellInfoObject.ConsoleInfo->Enabled = FALSE; + // + // When user wants to quit, the shell should stop running the command. + // + gBS->SignalEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak); + return (EFI_DEVICE_ERROR); + } else { + ASSERT (FALSE); + } + + return (EFI_SUCCESS); +} + +/** + Worker function to handle printing the output with page breaks. + + @param[in] String The string to output + @param[in] ConsoleInfo The pointer to the instance of the console logger information. + + @retval EFI_SUCCESS The string was printed + @retval EFI_DEVICE_ERROR The device reported an error while attempting to output + the text. + @retval EFI_UNSUPPORTED The output device's mode is not currently in a + defined text mode. + @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the + characters in the Unicode string could not be + rendered and were skipped. +**/ +EFI_STATUS +ConsoleLoggerPrintWithPageBreak ( + IN CONST CHAR16 *String, + IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ) +{ + CONST CHAR16 *Walker; + CONST CHAR16 *LineStart; + CHAR16 *StringCopy; + CHAR16 TempChar; + + StringCopy = NULL; + StringCopy = StrnCatGrow (&StringCopy, NULL, String, 0); + if (StringCopy == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + for ( Walker = StringCopy, + LineStart = StringCopy + ; Walker != NULL && *Walker != CHAR_NULL + ; Walker++ + ) + { + switch (*Walker) { + case (CHAR_BACKSPACE): + if (ConsoleInfo->OurConOut.Mode->CursorColumn > 0) { + ConsoleInfo->OurConOut.Mode->CursorColumn--; + } + + break; + case (CHAR_LINEFEED): + // + // add a temp NULL terminator + // + TempChar = *(Walker + 1); + *((CHAR16 *)(Walker+1)) = CHAR_NULL; + + // + // output the string + // + ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo); + + // + // restore the temp NULL terminator to its original character + // + *((CHAR16 *)(Walker+1)) = TempChar; + + // + // Update LineStart Variable + // + LineStart = Walker + 1; + + // + // increment row count + // + ShellInfoObject.ConsoleInfo->RowCounter++; + ConsoleInfo->OurConOut.Mode->CursorRow++; + + break; + case (CHAR_CARRIAGE_RETURN): + // + // Move the cursor to the beginning of the current row. + // + ConsoleInfo->OurConOut.Mode->CursorColumn = 0; + break; + default: + // + // increment column count + // + ConsoleInfo->OurConOut.Mode->CursorColumn++; + // + // check if that is the last column + // + if ((INTN)ConsoleInfo->ColsPerScreen == ConsoleInfo->OurConOut.Mode->CursorColumn + 1) { + // + // output a line similar to the linefeed character. + // + + // + // add a temp NULL terminator + // + TempChar = *(Walker + 1); + *((CHAR16 *)(Walker+1)) = CHAR_NULL; + + // + // output the string + // + ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo); + + // + // restore the temp NULL terminator to its original character + // + *((CHAR16 *)(Walker+1)) = TempChar; + + // + // Update LineStart Variable + // + LineStart = Walker + 1; + + // + // increment row count and zero the column + // + ShellInfoObject.ConsoleInfo->RowCounter++; + ConsoleInfo->OurConOut.Mode->CursorRow++; + ConsoleInfo->OurConOut.Mode->CursorColumn = 0; + } // last column on line + + break; + } // switch for character + + // + // check if that was the last printable row. If yes handle PageBreak mode + // + if ((ConsoleInfo->RowsPerScreen) -1 == ShellInfoObject.ConsoleInfo->RowCounter) { + if (EFI_ERROR (ConsoleLoggerDoPageBreak ())) { + // + // We got an error which means 'break' and halt the printing + // + SHELL_FREE_NON_NULL (StringCopy); + return (EFI_DEVICE_ERROR); + } + } + } // for loop + + if ((LineStart != NULL) && (*LineStart != CHAR_NULL)) { + ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo); + } + + SHELL_FREE_NON_NULL (StringCopy); + return (EFI_SUCCESS); +} + +/** + Write a Unicode string to the output device. + + @param[in] This Protocol instance pointer. + @param[in] WString The NULL-terminated Unicode string to be displayed on the output + device(s). All output devices must also support the Unicode + drawing defined in this file. + @retval EFI_SUCCESS The string was output to the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting to output + the text. + @retval EFI_UNSUPPORTED The output device's mode is not currently in a + defined text mode. + @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the + characters in the Unicode string could not be + rendered and were skipped. +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerOutputString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx; + EFI_KEY_DATA KeyData; + UINTN EventIndex; + CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; + + ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This); + if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) { + return (EFI_UNSUPPORTED); + } + + ASSERT (ShellInfoObject.ConsoleInfo == ConsoleInfo); + + Status = gBS->HandleProtocol (gST->ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID **)&TxtInEx); + if (!EFI_ERROR (Status)) { + while (ShellInfoObject.HaltOutput) { + ShellInfoObject.HaltOutput = FALSE; + // + // just get some key + // + Status = gBS->WaitForEvent (1, &TxtInEx->WaitForKeyEx, &EventIndex); + ASSERT_EFI_ERROR (Status); + Status = TxtInEx->ReadKeyStrokeEx (TxtInEx, &KeyData); + if (EFI_ERROR (Status)) { + break; + } + + if ((KeyData.Key.UnicodeChar == L's') && (KeyData.Key.ScanCode == SCAN_NULL) && + ((KeyData.KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID | EFI_LEFT_CONTROL_PRESSED)) || + (KeyData.KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID | EFI_RIGHT_CONTROL_PRESSED)) + ) + ) + { + ShellInfoObject.HaltOutput = TRUE; + } + } + } + + if (!ShellInfoObject.ConsoleInfo->Enabled) { + return (EFI_DEVICE_ERROR); + } else if (ShellInfoObject.PageBreakEnabled) { + return (ConsoleLoggerPrintWithPageBreak (WString, ConsoleInfo)); + } else { + return (ConsoleLoggerOutputStringSplit (WString, ConsoleInfo)); + } +} + +/** + Verifies that all characters in a Unicode string can be output to the + target device. + + @param[in] This Protocol instance pointer. + @param[in] WString The NULL-terminated Unicode string to be examined for the output + device(s). + + @retval EFI_SUCCESS The device(s) are capable of rendering the output string. + @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be + rendered by one or more of the output devices mapped + by the EFI handle. + +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerTestString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ) +{ + CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; + + ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This); + // + // Forward the request to the original ConOut + // + return (ConsoleInfo->OldConOut->TestString (ConsoleInfo->OldConOut, WString)); +} + +/** + Returns information for an available text mode that the output device(s) + supports. + + @param[in] This Protocol instance pointer. + @param[in] ModeNumber The mode number to return information on. + @param[out] Columns Upon return, the number of columns in the selected geometry + @param[out] Rows Upon return, the number of rows in the selected geometry + + @retval EFI_SUCCESS The requested mode information was returned. + @retval EFI_DEVICE_ERROR The device had an error and could not + complete the request. + @retval EFI_UNSUPPORTED The mode number was not valid. +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerQueryMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber, + OUT UINTN *Columns, + OUT UINTN *Rows + ) +{ + CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; + + ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This); + // + // Forward the request to the original ConOut + // + return (ConsoleInfo->OldConOut->QueryMode ( + ConsoleInfo->OldConOut, + ModeNumber, + Columns, + Rows + )); +} + +/** + Sets the output device(s) to a specified mode. + + @param[in] This Protocol instance pointer. + @param[in] ModeNumber The mode number to set. + + + @retval EFI_SUCCESS The requested text mode was set. + @retval EFI_DEVICE_ERROR The device had an error and + could not complete the request. + @retval EFI_UNSUPPORTED The mode number was not valid. +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerSetMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber + ) +{ + EFI_STATUS Status; + + CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; + + ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This); + + // + // Forward the request to the original ConOut + // + Status = ConsoleInfo->OldConOut->SetMode (ConsoleInfo->OldConOut, ModeNumber); + + // + // Check that the buffers are still correct for logging + // + if (!EFI_ERROR (Status)) { + ConsoleInfo->OurConOut.Mode = ConsoleInfo->OldConOut->Mode; + ConsoleLoggerResetBuffers (ConsoleInfo); + ConsoleInfo->OriginalStartRow = 0; + ConsoleInfo->CurrentStartRow = 0; + ConsoleInfo->OurConOut.ClearScreen (&ConsoleInfo->OurConOut); + } + + return Status; +} + +/** + Sets the background and foreground colors for the OutputString () and + ClearScreen () functions. + + @param[in] This Protocol instance pointer. + @param[in] Attribute The attribute to set. Bits 0..3 are the foreground color, and + bits 4..6 are the background color. All other bits are undefined + and must be zero. The valid Attributes are defined in this file. + + @retval EFI_SUCCESS The attribute was set. + @retval EFI_DEVICE_ERROR The device had an error and + could not complete the request. + @retval EFI_UNSUPPORTED The attribute requested is not defined. + +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerSetAttribute ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Attribute + ) +{ + EFI_STATUS Status; + + CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; + + ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This); + + // + // Forward the request to the original ConOut + // + Status = ConsoleInfo->OldConOut->SetAttribute (ConsoleInfo->OldConOut, Attribute); + + // + // Record console output history + // + if (!EFI_ERROR (Status)) { + ConsoleInfo->HistoryMode.Attribute = (INT32)Attribute; + } + + return Status; +} + +/** + Clears the output device(s) display to the currently selected background + color. + + @param[in] This Protocol instance pointer. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and + could not complete the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode. +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerClearScreen ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This + ) +{ + EFI_STATUS Status; + CHAR16 *Screen; + INT32 *Attributes; + UINTN Row; + UINTN Column; + CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; + + if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) { + return (EFI_UNSUPPORTED); + } + + ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This); + + // + // Forward the request to the original ConOut + // + Status = ConsoleInfo->OldConOut->ClearScreen (ConsoleInfo->OldConOut); + + // + // Record console output history + // + if (!EFI_ERROR (Status)) { + Screen = &ConsoleInfo->Buffer[(ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->CurrentStartRow]; + Attributes = &ConsoleInfo->Attributes[ConsoleInfo->ColsPerScreen * ConsoleInfo->CurrentStartRow]; + for ( Row = ConsoleInfo->OriginalStartRow + ; Row < (ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) + ; Row++ + ) + { + for ( Column = 0 + ; Column < ConsoleInfo->ColsPerScreen + ; Column++, + Screen++, + Attributes++ + ) + { + *Screen = L' '; + *Attributes = ConsoleInfo->OldConOut->Mode->Attribute; + } + + // + // Skip the NULL on each column end in text buffer only + // + Screen += 2; + } + + ConsoleInfo->HistoryMode.CursorColumn = 0; + ConsoleInfo->HistoryMode.CursorRow = 0; + } + + return Status; +} + +/** + Sets the current coordinates of the cursor position + + @param[in] This Protocol instance pointer. + @param[in] Column Column to put the cursor in. Must be between zero and Column returned from QueryMode + @param[in] Row Row to put the cursor in. Must be between zero and Row returned from QueryMode + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and + could not complete the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode, or the + cursor position is invalid for the current mode. +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerSetCursorPosition ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Column, + IN UINTN Row + ) +{ + EFI_STATUS Status; + CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; + + if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) { + return (EFI_UNSUPPORTED); + } + + ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This); + // + // Forward the request to the original ConOut + // + Status = ConsoleInfo->OldConOut->SetCursorPosition ( + ConsoleInfo->OldConOut, + Column, + Row + ); + + // + // Record console output history + // + if (!EFI_ERROR (Status)) { + ConsoleInfo->HistoryMode.CursorColumn = (INT32)Column; + ConsoleInfo->HistoryMode.CursorRow = (INT32)(ConsoleInfo->OriginalStartRow + Row); + } + + return Status; +} + +/** + Makes the cursor visible or invisible + + @param[in] This Protocol instance pointer. + @param[in] Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is + set to be invisible. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the + request, or the device does not support changing + the cursor mode. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode. +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerEnableCursor ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN Visible + ) +{ + EFI_STATUS Status; + + CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; + + ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS (This); + // + // Forward the request to the original ConOut + // + Status = ConsoleInfo->OldConOut->EnableCursor (ConsoleInfo->OldConOut, Visible); + + // + // Record console output history + // + if (!EFI_ERROR (Status)) { + ConsoleInfo->HistoryMode.CursorVisible = Visible; + } + + return Status; +} + +/** + Function to update and verify that the current buffers are correct. + + @param[in] ConsoleInfo The pointer to the instance of the console logger information. + + This will be used when a mode has changed or a reset occurred to verify all + history buffers. +**/ +EFI_STATUS +ConsoleLoggerResetBuffers ( + IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ) +{ + EFI_STATUS Status; + + if (ConsoleInfo->Buffer != NULL) { + FreePool (ConsoleInfo->Buffer); + ConsoleInfo->Buffer = NULL; + ConsoleInfo->BufferSize = 0; + } + + if (ConsoleInfo->Attributes != NULL) { + FreePool (ConsoleInfo->Attributes); + ConsoleInfo->Attributes = NULL; + ConsoleInfo->AttribSize = 0; + } + + Status = gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &ConsoleInfo->ColsPerScreen, &ConsoleInfo->RowsPerScreen); + if (EFI_ERROR (Status)) { + return (Status); + } + + ConsoleInfo->BufferSize = (ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount * sizeof (ConsoleInfo->Buffer[0]); + ConsoleInfo->AttribSize = ConsoleInfo->ColsPerScreen * ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount * sizeof (ConsoleInfo->Attributes[0]); + + ConsoleInfo->Buffer = (CHAR16 *)AllocateZeroPool (ConsoleInfo->BufferSize); + + if (ConsoleInfo->Buffer == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + ConsoleInfo->Attributes = (INT32 *)AllocateZeroPool (ConsoleInfo->AttribSize); + if (ConsoleInfo->Attributes == NULL) { + FreePool (ConsoleInfo->Buffer); + ConsoleInfo->Buffer = NULL; + return (EFI_OUT_OF_RESOURCES); + } + + CopyMem (&ConsoleInfo->HistoryMode, ConsoleInfo->OldConOut->Mode, sizeof (EFI_SIMPLE_TEXT_OUTPUT_MODE)); + + return (EFI_SUCCESS); +} diff --git a/ShellPkg/Application/Shell/ConsoleLogger.h b/ShellPkg/Application/Shell/ConsoleLogger.h index a9e017fcd6..9cb6c88daa 100644 --- a/ShellPkg/Application/Shell/ConsoleLogger.h +++ b/ShellPkg/Application/Shell/ConsoleLogger.h @@ -1,316 +1,316 @@ -/** @file
- Provides interface to shell console logger.
-
- Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-**/
-
-#ifndef _CONSOLE_LOGGER_HEADER_
-#define _CONSOLE_LOGGER_HEADER_
-
-#include "Shell.h"
-
-#define CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('c', 'o', 'P', 'D')
-
-typedef struct _CONSOLE_LOGGER_PRIVATE_DATA {
- UINTN Signature;
- EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL OurConOut; ///< the protocol we installed onto the system table
- EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OldConOut; ///< old protocol to reinstall upon exiting
- EFI_HANDLE OldConHandle; ///< old protocol handle
- UINTN ScreenCount; ///< How many screens worth of data to save
- CHAR16 *Buffer; ///< Buffer to save data
- UINTN BufferSize; ///< size of buffer in bytes
-
- // start row is the top of the screen
- UINTN OriginalStartRow; ///< What the originally visible start row was
- UINTN CurrentStartRow; ///< what the currently visible start row is
-
- UINTN RowsPerScreen; ///< how many rows the screen can display
- UINTN ColsPerScreen; ///< how many columns the screen can display
-
- INT32 *Attributes; ///< Buffer for Attribute to be saved for each character
- UINTN AttribSize; ///< Size of Attributes in bytes
-
- EFI_SIMPLE_TEXT_OUTPUT_MODE HistoryMode; ///< mode of the history log
- BOOLEAN Enabled; ///< Set to FALSE when a break is requested.
- UINTN RowCounter; ///< Initial row of each print job.
-} CONSOLE_LOGGER_PRIVATE_DATA;
-
-#define CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(a) CR (a, CONSOLE_LOGGER_PRIVATE_DATA, OurConOut, CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE)
-
-/**
- Install our intermediate ConOut into the system table to
- keep a log of all the info that is displayed to the user.
-
- @param[in] ScreensToSave Sets how many screen-worths of data to save.
- @param[out] ConsoleInfo The object to pass into later functions.
-
- @retval EFI_SUCCESS The operation was successful.
- @return other The operation failed.
-
- @sa ConsoleLoggerResetBuffers
- @sa InstallProtocolInterface
-**/
-EFI_STATUS
-ConsoleLoggerInstall (
- IN CONST UINTN ScreensToSave,
- OUT CONSOLE_LOGGER_PRIVATE_DATA **ConsoleInfo
- );
-
-/**
- Return the system to the state it was before InstallConsoleLogger
- was installed.
-
- @param[in, out] ConsoleInfo The object from the install function.
-
- @retval EFI_SUCCESS The operation was successful
- @return other The operation failed. This was from UninstallProtocolInterface.
-**/
-EFI_STATUS
-ConsoleLoggerUninstall (
- IN OUT CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
- );
-
-/**
- Displays previously logged output back to the screen.
-
- This will scroll the screen forwards and backwards through the log of previous
- output. If Rows is 0 then the size of 1/2 the screen will be scrolled. If Rows
- is (UINTN)(-1) then the size of the screen will be scrolled.
-
- @param[in] Forward If TRUE then the log will be displayed forwards (scroll to newer).
- If FALSE then the log will be displayed backwards (scroll to older).
- @param[in] Rows Determines how many rows the log should scroll.
- @param[in] ConsoleInfo The pointer to the instance of the console logger information.
-**/
-EFI_STATUS
-ConsoleLoggerDisplayHistory (
- IN CONST BOOLEAN Forward,
- IN CONST UINTN Rows,
- IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
- );
-
-/**
- Function to return to normal output whent he scrolling is complete.
- @param[in] ConsoleInfo The pointer to the instance of the console logger information.
-
- @retval EFI_SUCCESS The operation was successful.
- @return other The operation failed. See UpdateDisplayFromHistory.
-
- @sa UpdateDisplayFromHistory
-**/
-EFI_STATUS
-ConsoleLoggerStopHistory (
- IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
- );
-
-/**
- Updates the hidden ConOut to be displaying the correct stuff.
- @param[in] ConsoleInfo The pointer to the instance of the console logger information.
-
- @retval EFI_SUCCESS The operation was successful.
- @return other The operation failed.
-**/
-EFI_STATUS
-UpdateDisplayFromHistory (
- IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
- );
-
-/**
- Reset the text output device hardware and optionally run diagnostics
-
- @param This Pointer to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
- @param ExtendedVerification Indicates that a more extensive test may be performed
-
- @retval EFI_SUCCESS The text output device was reset.
- @retval EFI_DEVICE_ERROR The text output device is not functioning correctly and
- could not be reset.
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerReset (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN BOOLEAN ExtendedVerification
- );
-
-/**
- Write a Unicode string to the output device.
-
- @param[in] This Protocol instance pointer.
- @param[in] WString The NULL-terminated Unicode string to be displayed on the output
- device(s). All output devices must also support the Unicode
- drawing defined in this file.
- @retval EFI_SUCCESS The string was output to the device.
- @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
- the text.
- @retval EFI_UNSUPPORTED The output device's mode is not currently in a
- defined text mode.
- @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
- characters in the Unicode string could not be
- rendered and were skipped.
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerOutputString (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN CHAR16 *WString
- );
-
-/**
- Verifies that all characters in a Unicode string can be output to the
- target device.
-
- @param[in] This Protocol instance pointer.
- @param[in] WString The NULL-terminated Unicode string to be examined for the output
- device(s).
-
- @retval EFI_SUCCESS The device(s) are capable of rendering the output string.
- @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be
- rendered by one or more of the output devices mapped
- by the EFI handle.
-
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerTestString (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN CHAR16 *WString
- );
-
-/**
- Returns information for an available text mode that the output device(s)
- supports.
-
- @param[in] This Protocol instance pointer.
- @param[in] ModeNumber The mode number to return information on.
- @param[out] Columns Upon return, the number of columns in the selected geometry
- @param[out] Rows Upon return, the number of rows in the selected geometry
-
- @retval EFI_SUCCESS The requested mode information was returned.
- @retval EFI_DEVICE_ERROR The device had an error and could not
- complete the request.
- @retval EFI_UNSUPPORTED The mode number was not valid.
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerQueryMode (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN UINTN ModeNumber,
- OUT UINTN *Columns,
- OUT UINTN *Rows
- );
-
-/**
- Sets the output device(s) to a specified mode.
-
- @param[in] This Protocol instance pointer.
- @param[in] ModeNumber The mode number to set.
-
-
- @retval EFI_SUCCESS The requested text mode was set.
- @retval EFI_DEVICE_ERROR The device had an error and
- could not complete the request.
- @retval EFI_UNSUPPORTED The mode number was not valid.
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerSetMode (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN UINTN ModeNumber
- );
-
-/**
- Sets the background and foreground colors for the OutputString () and
- ClearScreen () functions.
-
- @param[in] This Protocol instance pointer.
- @param[in] Attribute The attribute to set. Bits 0..3 are the foreground color, and
- bits 4..6 are the background color. All other bits are undefined
- and must be zero. The valid Attributes are defined in this file.
-
- @retval EFI_SUCCESS The attribute was set.
- @retval EFI_DEVICE_ERROR The device had an error and
- could not complete the request.
- @retval EFI_UNSUPPORTED The attribute requested is not defined.
-
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerSetAttribute (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN UINTN Attribute
- );
-
-/**
- Clears the output device(s) display to the currently selected background
- color.
-
- @param[in] This Protocol instance pointer.
-
- @retval EFI_SUCCESS The operation completed successfully.
- @retval EFI_DEVICE_ERROR The device had an error and
- could not complete the request.
- @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerClearScreen (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
- );
-
-/**
- Sets the current coordinates of the cursor position.
-
- @param[in] This Protocol instance pointer.
- @param[in] Column Column to put the cursor in. Must be between zero and Column returned from QueryMode
- @param[in] Row Row to put the cursor in. Must be between zero and Row returned from QueryMode
-
- @retval EFI_SUCCESS The operation completed successfully.
- @retval EFI_DEVICE_ERROR The device had an error and
- could not complete the request.
- @retval EFI_UNSUPPORTED The output device is not in a valid text mode, or the
- cursor position is invalid for the current mode.
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerSetCursorPosition (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN UINTN Column,
- IN UINTN Row
- );
-
-/**
- Makes the cursor visible or invisible
-
- @param[in] This Protocol instance pointer.
- @param[in] Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is
- set to be invisible.
-
- @retval EFI_SUCCESS The operation completed successfully.
- @retval EFI_DEVICE_ERROR The device had an error and could not complete the
- request, or the device does not support changing
- the cursor mode.
- @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
-
-**/
-EFI_STATUS
-EFIAPI
-ConsoleLoggerEnableCursor (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN BOOLEAN Visible
- );
-
-/**
- Function to update and verify that the current buffers are correct.
-
- @param[in] ConsoleInfo The pointer to the instance of the console logger information.
-
- This will be used when a mode has changed or a reset occurred to verify all
- history buffers.
-**/
-EFI_STATUS
-ConsoleLoggerResetBuffers (
- IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
- );
-
-#endif //_CONSOLE_LOGGER_HEADER_
+/** @file + Provides interface to shell console logger. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _CONSOLE_LOGGER_HEADER_ +#define _CONSOLE_LOGGER_HEADER_ + +#include "Shell.h" + +#define CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('c', 'o', 'P', 'D') + +typedef struct _CONSOLE_LOGGER_PRIVATE_DATA { + UINTN Signature; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL OurConOut; ///< the protocol we installed onto the system table + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OldConOut; ///< old protocol to reinstall upon exiting + EFI_HANDLE OldConHandle; ///< old protocol handle + UINTN ScreenCount; ///< How many screens worth of data to save + CHAR16 *Buffer; ///< Buffer to save data + UINTN BufferSize; ///< size of buffer in bytes + + // start row is the top of the screen + UINTN OriginalStartRow; ///< What the originally visible start row was + UINTN CurrentStartRow; ///< what the currently visible start row is + + UINTN RowsPerScreen; ///< how many rows the screen can display + UINTN ColsPerScreen; ///< how many columns the screen can display + + INT32 *Attributes; ///< Buffer for Attribute to be saved for each character + UINTN AttribSize; ///< Size of Attributes in bytes + + EFI_SIMPLE_TEXT_OUTPUT_MODE HistoryMode; ///< mode of the history log + BOOLEAN Enabled; ///< Set to FALSE when a break is requested. + UINTN RowCounter; ///< Initial row of each print job. +} CONSOLE_LOGGER_PRIVATE_DATA; + +#define CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(a) CR (a, CONSOLE_LOGGER_PRIVATE_DATA, OurConOut, CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE) + +/** + Install our intermediate ConOut into the system table to + keep a log of all the info that is displayed to the user. + + @param[in] ScreensToSave Sets how many screen-worths of data to save. + @param[out] ConsoleInfo The object to pass into later functions. + + @retval EFI_SUCCESS The operation was successful. + @return other The operation failed. + + @sa ConsoleLoggerResetBuffers + @sa InstallProtocolInterface +**/ +EFI_STATUS +ConsoleLoggerInstall ( + IN CONST UINTN ScreensToSave, + OUT CONSOLE_LOGGER_PRIVATE_DATA **ConsoleInfo + ); + +/** + Return the system to the state it was before InstallConsoleLogger + was installed. + + @param[in, out] ConsoleInfo The object from the install function. + + @retval EFI_SUCCESS The operation was successful + @return other The operation failed. This was from UninstallProtocolInterface. +**/ +EFI_STATUS +ConsoleLoggerUninstall ( + IN OUT CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ); + +/** + Displays previously logged output back to the screen. + + This will scroll the screen forwards and backwards through the log of previous + output. If Rows is 0 then the size of 1/2 the screen will be scrolled. If Rows + is (UINTN)(-1) then the size of the screen will be scrolled. + + @param[in] Forward If TRUE then the log will be displayed forwards (scroll to newer). + If FALSE then the log will be displayed backwards (scroll to older). + @param[in] Rows Determines how many rows the log should scroll. + @param[in] ConsoleInfo The pointer to the instance of the console logger information. +**/ +EFI_STATUS +ConsoleLoggerDisplayHistory ( + IN CONST BOOLEAN Forward, + IN CONST UINTN Rows, + IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ); + +/** + Function to return to normal output whent he scrolling is complete. + @param[in] ConsoleInfo The pointer to the instance of the console logger information. + + @retval EFI_SUCCESS The operation was successful. + @return other The operation failed. See UpdateDisplayFromHistory. + + @sa UpdateDisplayFromHistory +**/ +EFI_STATUS +ConsoleLoggerStopHistory ( + IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ); + +/** + Updates the hidden ConOut to be displaying the correct stuff. + @param[in] ConsoleInfo The pointer to the instance of the console logger information. + + @retval EFI_SUCCESS The operation was successful. + @return other The operation failed. +**/ +EFI_STATUS +UpdateDisplayFromHistory ( + IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ); + +/** + Reset the text output device hardware and optionally run diagnostics + + @param This Pointer to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL + @param ExtendedVerification Indicates that a more extensive test may be performed + + @retval EFI_SUCCESS The text output device was reset. + @retval EFI_DEVICE_ERROR The text output device is not functioning correctly and + could not be reset. +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerReset ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Write a Unicode string to the output device. + + @param[in] This Protocol instance pointer. + @param[in] WString The NULL-terminated Unicode string to be displayed on the output + device(s). All output devices must also support the Unicode + drawing defined in this file. + @retval EFI_SUCCESS The string was output to the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting to output + the text. + @retval EFI_UNSUPPORTED The output device's mode is not currently in a + defined text mode. + @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the + characters in the Unicode string could not be + rendered and were skipped. +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerOutputString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ); + +/** + Verifies that all characters in a Unicode string can be output to the + target device. + + @param[in] This Protocol instance pointer. + @param[in] WString The NULL-terminated Unicode string to be examined for the output + device(s). + + @retval EFI_SUCCESS The device(s) are capable of rendering the output string. + @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be + rendered by one or more of the output devices mapped + by the EFI handle. + +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerTestString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ); + +/** + Returns information for an available text mode that the output device(s) + supports. + + @param[in] This Protocol instance pointer. + @param[in] ModeNumber The mode number to return information on. + @param[out] Columns Upon return, the number of columns in the selected geometry + @param[out] Rows Upon return, the number of rows in the selected geometry + + @retval EFI_SUCCESS The requested mode information was returned. + @retval EFI_DEVICE_ERROR The device had an error and could not + complete the request. + @retval EFI_UNSUPPORTED The mode number was not valid. +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerQueryMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber, + OUT UINTN *Columns, + OUT UINTN *Rows + ); + +/** + Sets the output device(s) to a specified mode. + + @param[in] This Protocol instance pointer. + @param[in] ModeNumber The mode number to set. + + + @retval EFI_SUCCESS The requested text mode was set. + @retval EFI_DEVICE_ERROR The device had an error and + could not complete the request. + @retval EFI_UNSUPPORTED The mode number was not valid. +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerSetMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber + ); + +/** + Sets the background and foreground colors for the OutputString () and + ClearScreen () functions. + + @param[in] This Protocol instance pointer. + @param[in] Attribute The attribute to set. Bits 0..3 are the foreground color, and + bits 4..6 are the background color. All other bits are undefined + and must be zero. The valid Attributes are defined in this file. + + @retval EFI_SUCCESS The attribute was set. + @retval EFI_DEVICE_ERROR The device had an error and + could not complete the request. + @retval EFI_UNSUPPORTED The attribute requested is not defined. + +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerSetAttribute ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Attribute + ); + +/** + Clears the output device(s) display to the currently selected background + color. + + @param[in] This Protocol instance pointer. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and + could not complete the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode. +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerClearScreen ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This + ); + +/** + Sets the current coordinates of the cursor position. + + @param[in] This Protocol instance pointer. + @param[in] Column Column to put the cursor in. Must be between zero and Column returned from QueryMode + @param[in] Row Row to put the cursor in. Must be between zero and Row returned from QueryMode + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and + could not complete the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode, or the + cursor position is invalid for the current mode. +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerSetCursorPosition ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Column, + IN UINTN Row + ); + +/** + Makes the cursor visible or invisible + + @param[in] This Protocol instance pointer. + @param[in] Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is + set to be invisible. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the + request, or the device does not support changing + the cursor mode. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode. + +**/ +EFI_STATUS +EFIAPI +ConsoleLoggerEnableCursor ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN Visible + ); + +/** + Function to update and verify that the current buffers are correct. + + @param[in] ConsoleInfo The pointer to the instance of the console logger information. + + This will be used when a mode has changed or a reset occurred to verify all + history buffers. +**/ +EFI_STATUS +ConsoleLoggerResetBuffers ( + IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ); + +#endif //_CONSOLE_LOGGER_HEADER_ diff --git a/ShellPkg/Application/Shell/ConsoleWrappers.c b/ShellPkg/Application/Shell/ConsoleWrappers.c index dbffae30cd..b06dacf8a8 100644 --- a/ShellPkg/Application/Shell/ConsoleWrappers.c +++ b/ShellPkg/Application/Shell/ConsoleWrappers.c @@ -1,524 +1,524 @@ -/** @file
- Function definitions for shell simple text in and out on top of file handles.
-
- (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR>
- Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "Shell.h"
-
-extern BOOLEAN AsciiRedirection;
-
-typedef struct {
- EFI_SIMPLE_TEXT_INPUT_PROTOCOL SimpleTextIn;
- SHELL_FILE_HANDLE FileHandle;
- EFI_HANDLE TheHandle;
- UINT64 RemainingBytesOfInputFile;
-} SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL;
-
-typedef struct {
- EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL SimpleTextOut;
- SHELL_FILE_HANDLE FileHandle;
- EFI_HANDLE TheHandle;
- EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OriginalSimpleTextOut;
-} SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;
-
-/**
- Event notification function for EFI_SIMPLE_TEXT_INPUT_PROTOCOL.WaitForKey event
- Signal the event if there is key available
-
- @param Event Indicates the event that invoke this function.
- @param Context Indicates the calling context.
-
-**/
-VOID
-EFIAPI
-ConInWaitForKey (
- IN EFI_EVENT Event,
- IN VOID *Context
- )
-{
- gBS->SignalEvent (Event);
-}
-
-/**
- Reset function for the fake simple text input.
-
- @param[in] This A pointer to the SimpleTextIn structure.
- @param[in] ExtendedVerification TRUE for extra validation, FALSE otherwise.
-
- @retval EFI_SUCCESS The reset was successful.
-**/
-EFI_STATUS
-EFIAPI
-FileBasedSimpleTextInReset (
- IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
- IN BOOLEAN ExtendedVerification
- )
-{
- return (EFI_SUCCESS);
-}
-
-/**
- ReadKeyStroke function for the fake simple text input.
-
- @param[in] This A pointer to the SimpleTextIn structure.
- @param[in, out] Key A pointer to the Key structure to fill.
-
- @retval EFI_SUCCESS The read was successful.
- @retval EFI_UNSUPPORTED The device does not support the ability to read
- keystroke data.
-**/
-EFI_STATUS
-EFIAPI
-FileBasedSimpleTextInReadKeyStroke (
- IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
- IN OUT EFI_INPUT_KEY *Key
- )
-{
- UINTN Size;
- UINTN CharSize;
-
- //
- // Verify the parameters
- //
- if ((Key == NULL) || (This == NULL)) {
- return (EFI_INVALID_PARAMETER);
- }
-
- //
- // Check if we have any characters left in the stream.
- //
- if (((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->RemainingBytesOfInputFile == 0) {
- return (EFI_NOT_READY);
- }
-
- Size = sizeof (CHAR16);
-
- if (!AsciiRedirection) {
- CharSize = sizeof (CHAR16);
- } else {
- CharSize = sizeof (CHAR8);
- }
-
- //
- // Decrement the amount of free space by Size or set to zero (for odd length files)
- //
- if (((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->RemainingBytesOfInputFile > CharSize) {
- ((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->RemainingBytesOfInputFile -= CharSize;
- } else {
- ((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->RemainingBytesOfInputFile = 0;
- }
-
- Key->ScanCode = 0;
- return (ShellInfoObject.NewEfiShellProtocol->ReadFile (
- ((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->FileHandle,
- &Size,
- &Key->UnicodeChar
- ));
-}
-
-/**
- Function to create a EFI_SIMPLE_TEXT_INPUT_PROTOCOL on top of a
- SHELL_FILE_HANDLE to support redirecting input from a file.
-
- @param[in] FileHandleToUse The pointer to the SHELL_FILE_HANDLE to use.
- @param[in] HandleLocation The pointer of a location to copy handle with protocol to.
-
- @retval NULL There was insufficient memory available.
- @return A pointer to the allocated protocol structure;
-**/
-EFI_SIMPLE_TEXT_INPUT_PROTOCOL *
-CreateSimpleTextInOnFile (
- IN SHELL_FILE_HANDLE FileHandleToUse,
- IN EFI_HANDLE *HandleLocation
- )
-{
- SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ProtocolToReturn;
- EFI_STATUS Status;
- UINT64 CurrentPosition;
- UINT64 FileSize;
-
- if ((HandleLocation == NULL) || (FileHandleToUse == NULL)) {
- return (NULL);
- }
-
- ProtocolToReturn = AllocateZeroPool (sizeof (SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL));
- if (ProtocolToReturn == NULL) {
- return (NULL);
- }
-
- ShellGetFileSize (FileHandleToUse, &FileSize);
- ShellGetFilePosition (FileHandleToUse, &CurrentPosition);
-
- //
- // Initialize the protocol members
- //
- ProtocolToReturn->RemainingBytesOfInputFile = FileSize - CurrentPosition;
- ProtocolToReturn->FileHandle = FileHandleToUse;
- ProtocolToReturn->SimpleTextIn.Reset = FileBasedSimpleTextInReset;
- ProtocolToReturn->SimpleTextIn.ReadKeyStroke = FileBasedSimpleTextInReadKeyStroke;
-
- Status = gBS->CreateEvent (
- EVT_NOTIFY_WAIT,
- TPL_NOTIFY,
- ConInWaitForKey,
- &ProtocolToReturn->SimpleTextIn,
- &ProtocolToReturn->SimpleTextIn.WaitForKey
- );
-
- if (EFI_ERROR (Status)) {
- FreePool (ProtocolToReturn);
- return (NULL);
- }
-
- /// @todo possibly also install SimpleTextInputEx on the handle at this point.
- Status = gBS->InstallProtocolInterface (
- &(ProtocolToReturn->TheHandle),
- &gEfiSimpleTextInProtocolGuid,
- EFI_NATIVE_INTERFACE,
- &(ProtocolToReturn->SimpleTextIn)
- );
- if (!EFI_ERROR (Status)) {
- *HandleLocation = ProtocolToReturn->TheHandle;
- return ((EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)ProtocolToReturn);
- } else {
- FreePool (ProtocolToReturn);
- return (NULL);
- }
-}
-
-/**
- Function to close a EFI_SIMPLE_TEXT_INPUT_PROTOCOL on top of a
- SHELL_FILE_HANDLE to support redirecting input from a file.
-
- @param[in] SimpleTextIn The pointer to the SimpleTextIn to close.
-
- @retval EFI_SUCCESS The object was closed.
-**/
-EFI_STATUS
-CloseSimpleTextInOnFile (
- IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *SimpleTextIn
- )
-{
- EFI_STATUS Status;
- EFI_STATUS Status1;
-
- if (SimpleTextIn == NULL) {
- return (EFI_INVALID_PARAMETER);
- }
-
- Status = gBS->CloseEvent (((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)SimpleTextIn)->SimpleTextIn.WaitForKey);
-
- Status1 = gBS->UninstallProtocolInterface (
- ((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)SimpleTextIn)->TheHandle,
- &gEfiSimpleTextInProtocolGuid,
- &(((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)SimpleTextIn)->SimpleTextIn)
- );
-
- FreePool (SimpleTextIn);
- if (!EFI_ERROR (Status)) {
- return (Status1);
- } else {
- return (Status);
- }
-}
-
-/**
- Reset the text output device hardware and optionally run diagnostics.
-
- @param This pointer to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
- @param ExtendedVerification Indicates that a more extensive test may be performed
-
- @retval EFI_SUCCESS The text output device was reset.
-**/
-EFI_STATUS
-EFIAPI
-FileBasedSimpleTextOutReset (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN BOOLEAN ExtendedVerification
- )
-{
- return (EFI_SUCCESS);
-}
-
-/**
- Verifies that all characters in a Unicode string can be output to the
- target device.
-
- @param[in] This Protocol instance pointer.
- @param[in] WString The NULL-terminated Unicode string to be examined.
-
- @retval EFI_SUCCESS The device(s) are capable of rendering the output string.
-**/
-EFI_STATUS
-EFIAPI
-FileBasedSimpleTextOutTestString (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN CHAR16 *WString
- )
-{
- return (EFI_SUCCESS);
-}
-
-/**
- Returns information for an available text mode that the output device(s)
- supports.
-
- @param[in] This Protocol instance pointer.
- @param[in] ModeNumber The mode number to return information on.
- @param[out] Columns Upon return, the number of columns in the selected geometry
- @param[out] Rows Upon return, the number of rows in the selected geometry
-
- @retval EFI_UNSUPPORTED The mode number was not valid.
-**/
-EFI_STATUS
-EFIAPI
-FileBasedSimpleTextOutQueryMode (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN UINTN ModeNumber,
- OUT UINTN *Columns,
- OUT UINTN *Rows
- )
-{
- EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *PassThruProtocol;
-
- PassThruProtocol = ((SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)This)->OriginalSimpleTextOut;
-
- // Pass the QueryMode call thru to the original SimpleTextOutProtocol
- return (PassThruProtocol->QueryMode (
- PassThruProtocol,
- ModeNumber,
- Columns,
- Rows
- ));
-}
-
-/**
- Sets the output device(s) to a specified mode.
-
- @param[in] This Protocol instance pointer.
- @param[in] ModeNumber The mode number to set.
-
- @retval EFI_UNSUPPORTED The mode number was not valid.
-**/
-EFI_STATUS
-EFIAPI
-FileBasedSimpleTextOutSetMode (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN UINTN ModeNumber
- )
-{
- return (EFI_UNSUPPORTED);
-}
-
-/**
- Sets the background and foreground colors for the OutputString () and
- ClearScreen () functions.
-
- @param[in] This Protocol instance pointer.
- @param[in] Attribute The attribute to set. Bits 0..3 are the foreground color, and
- bits 4..6 are the background color. All other bits are undefined
- and must be zero. The valid Attributes are defined in this file.
-
- @retval EFI_SUCCESS The attribute was set.
-**/
-EFI_STATUS
-EFIAPI
-FileBasedSimpleTextOutSetAttribute (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN UINTN Attribute
- )
-{
- return (EFI_SUCCESS);
-}
-
-/**
- Clears the output device(s) display to the currently selected background
- color.
-
- @param[in] This Protocol instance pointer.
-
- @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
-**/
-EFI_STATUS
-EFIAPI
-FileBasedSimpleTextOutClearScreen (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
- )
-{
- return (EFI_SUCCESS);
-}
-
-/**
- Sets the current coordinates of the cursor position
-
- @param[in] This Protocol instance pointer.
- @param[in] Column Column to put the cursor in. Must be between zero and Column returned from QueryMode
- @param[in] Row Row to put the cursor in. Must be between zero and Row returned from QueryMode
-
- @retval EFI_SUCCESS The operation completed successfully.
-**/
-EFI_STATUS
-EFIAPI
-FileBasedSimpleTextOutSetCursorPosition (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN UINTN Column,
- IN UINTN Row
- )
-{
- return (EFI_SUCCESS);
-}
-
-/**
- Makes the cursor visible or invisible
-
- @param[in] This Protocol instance pointer.
- @param[in] Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is
- set to be invisible.
-
- @retval EFI_SUCCESS The operation completed successfully.
-**/
-EFI_STATUS
-EFIAPI
-FileBasedSimpleTextOutEnableCursor (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN BOOLEAN Visible
- )
-{
- return (EFI_SUCCESS);
-}
-
-/**
- Write a Unicode string to the output device.
-
- @param[in] This Protocol instance pointer.
- @param[in] WString The NULL-terminated Unicode string to be displayed on the output
- device(s). All output devices must also support the Unicode
- drawing defined in this file.
- @retval EFI_SUCCESS The string was output to the device.
- @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
- the text.
- @retval EFI_UNSUPPORTED The output device's mode is not currently in a
- defined text mode.
- @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
- characters in the Unicode string could not be
- rendered and were skipped.
-**/
-EFI_STATUS
-EFIAPI
-FileBasedSimpleTextOutOutputString (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
- IN CHAR16 *WString
- )
-{
- UINTN Size;
-
- Size = StrLen (WString) * sizeof (CHAR16);
- return (ShellInfoObject.NewEfiShellProtocol->WriteFile (
- ((SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)This)->FileHandle,
- &Size,
- WString
- ));
-}
-
-/**
- Function to create a EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on top of a
- SHELL_FILE_HANDLE to support redirecting output from a file.
-
- @param[in] FileHandleToUse The pointer to the SHELL_FILE_HANDLE to use.
- @param[in] HandleLocation The pointer of a location to copy handle with protocol to.
- @param[in] OriginalProtocol The pointer to the original output protocol for pass thru of functions.
-
- @retval NULL There was insufficient memory available.
- @return A pointer to the allocated protocol structure;
-**/
-EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *
-CreateSimpleTextOutOnFile (
- IN SHELL_FILE_HANDLE FileHandleToUse,
- IN EFI_HANDLE *HandleLocation,
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OriginalProtocol
- )
-{
- SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ProtocolToReturn;
- EFI_STATUS Status;
-
- if ((HandleLocation == NULL) || (FileHandleToUse == NULL)) {
- return (NULL);
- }
-
- ProtocolToReturn = AllocateZeroPool (sizeof (SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL));
- if (ProtocolToReturn == NULL) {
- return (NULL);
- }
-
- ProtocolToReturn->FileHandle = FileHandleToUse;
- ProtocolToReturn->OriginalSimpleTextOut = OriginalProtocol;
- ProtocolToReturn->SimpleTextOut.Reset = FileBasedSimpleTextOutReset;
- ProtocolToReturn->SimpleTextOut.TestString = FileBasedSimpleTextOutTestString;
- ProtocolToReturn->SimpleTextOut.QueryMode = FileBasedSimpleTextOutQueryMode;
- ProtocolToReturn->SimpleTextOut.SetMode = FileBasedSimpleTextOutSetMode;
- ProtocolToReturn->SimpleTextOut.SetAttribute = FileBasedSimpleTextOutSetAttribute;
- ProtocolToReturn->SimpleTextOut.ClearScreen = FileBasedSimpleTextOutClearScreen;
- ProtocolToReturn->SimpleTextOut.SetCursorPosition = FileBasedSimpleTextOutSetCursorPosition;
- ProtocolToReturn->SimpleTextOut.EnableCursor = FileBasedSimpleTextOutEnableCursor;
- ProtocolToReturn->SimpleTextOut.OutputString = FileBasedSimpleTextOutOutputString;
- ProtocolToReturn->SimpleTextOut.Mode = AllocateZeroPool (sizeof (EFI_SIMPLE_TEXT_OUTPUT_MODE));
- if (ProtocolToReturn->SimpleTextOut.Mode == NULL) {
- FreePool (ProtocolToReturn);
- return (NULL);
- }
-
- ProtocolToReturn->SimpleTextOut.Mode->MaxMode = OriginalProtocol->Mode->MaxMode;
- ProtocolToReturn->SimpleTextOut.Mode->Mode = OriginalProtocol->Mode->Mode;
- ProtocolToReturn->SimpleTextOut.Mode->Attribute = OriginalProtocol->Mode->Attribute;
- ProtocolToReturn->SimpleTextOut.Mode->CursorColumn = OriginalProtocol->Mode->CursorColumn;
- ProtocolToReturn->SimpleTextOut.Mode->CursorRow = OriginalProtocol->Mode->CursorRow;
- ProtocolToReturn->SimpleTextOut.Mode->CursorVisible = OriginalProtocol->Mode->CursorVisible;
-
- Status = gBS->InstallProtocolInterface (
- &(ProtocolToReturn->TheHandle),
- &gEfiSimpleTextOutProtocolGuid,
- EFI_NATIVE_INTERFACE,
- &(ProtocolToReturn->SimpleTextOut)
- );
- if (!EFI_ERROR (Status)) {
- *HandleLocation = ProtocolToReturn->TheHandle;
- return ((EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)ProtocolToReturn);
- } else {
- SHELL_FREE_NON_NULL (ProtocolToReturn->SimpleTextOut.Mode);
- SHELL_FREE_NON_NULL (ProtocolToReturn);
- return (NULL);
- }
-}
-
-/**
- Function to close a EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on top of a
- SHELL_FILE_HANDLE to support redirecting output from a file.
-
- @param[in] SimpleTextOut The pointer to the SimpleTextOUT to close.
-
- @retval EFI_SUCCESS The object was closed.
-**/
-EFI_STATUS
-CloseSimpleTextOutOnFile (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOut
- )
-{
- EFI_STATUS Status;
-
- if (SimpleTextOut == NULL) {
- return (EFI_INVALID_PARAMETER);
- }
-
- Status = gBS->UninstallProtocolInterface (
- ((SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)SimpleTextOut)->TheHandle,
- &gEfiSimpleTextOutProtocolGuid,
- &(((SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)SimpleTextOut)->SimpleTextOut)
- );
- FreePool (SimpleTextOut->Mode);
- FreePool (SimpleTextOut);
- return (Status);
-}
+/** @file + Function definitions for shell simple text in and out on top of file handles. + + (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR> + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Shell.h" + +extern BOOLEAN AsciiRedirection; + +typedef struct { + EFI_SIMPLE_TEXT_INPUT_PROTOCOL SimpleTextIn; + SHELL_FILE_HANDLE FileHandle; + EFI_HANDLE TheHandle; + UINT64 RemainingBytesOfInputFile; +} SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL; + +typedef struct { + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL SimpleTextOut; + SHELL_FILE_HANDLE FileHandle; + EFI_HANDLE TheHandle; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OriginalSimpleTextOut; +} SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL; + +/** + Event notification function for EFI_SIMPLE_TEXT_INPUT_PROTOCOL.WaitForKey event + Signal the event if there is key available + + @param Event Indicates the event that invoke this function. + @param Context Indicates the calling context. + +**/ +VOID +EFIAPI +ConInWaitForKey ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + gBS->SignalEvent (Event); +} + +/** + Reset function for the fake simple text input. + + @param[in] This A pointer to the SimpleTextIn structure. + @param[in] ExtendedVerification TRUE for extra validation, FALSE otherwise. + + @retval EFI_SUCCESS The reset was successful. +**/ +EFI_STATUS +EFIAPI +FileBasedSimpleTextInReset ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + return (EFI_SUCCESS); +} + +/** + ReadKeyStroke function for the fake simple text input. + + @param[in] This A pointer to the SimpleTextIn structure. + @param[in, out] Key A pointer to the Key structure to fill. + + @retval EFI_SUCCESS The read was successful. + @retval EFI_UNSUPPORTED The device does not support the ability to read + keystroke data. +**/ +EFI_STATUS +EFIAPI +FileBasedSimpleTextInReadKeyStroke ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN OUT EFI_INPUT_KEY *Key + ) +{ + UINTN Size; + UINTN CharSize; + + // + // Verify the parameters + // + if ((Key == NULL) || (This == NULL)) { + return (EFI_INVALID_PARAMETER); + } + + // + // Check if we have any characters left in the stream. + // + if (((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->RemainingBytesOfInputFile == 0) { + return (EFI_NOT_READY); + } + + Size = sizeof (CHAR16); + + if (!AsciiRedirection) { + CharSize = sizeof (CHAR16); + } else { + CharSize = sizeof (CHAR8); + } + + // + // Decrement the amount of free space by Size or set to zero (for odd length files) + // + if (((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->RemainingBytesOfInputFile > CharSize) { + ((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->RemainingBytesOfInputFile -= CharSize; + } else { + ((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->RemainingBytesOfInputFile = 0; + } + + Key->ScanCode = 0; + return (ShellInfoObject.NewEfiShellProtocol->ReadFile ( + ((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->FileHandle, + &Size, + &Key->UnicodeChar + )); +} + +/** + Function to create a EFI_SIMPLE_TEXT_INPUT_PROTOCOL on top of a + SHELL_FILE_HANDLE to support redirecting input from a file. + + @param[in] FileHandleToUse The pointer to the SHELL_FILE_HANDLE to use. + @param[in] HandleLocation The pointer of a location to copy handle with protocol to. + + @retval NULL There was insufficient memory available. + @return A pointer to the allocated protocol structure; +**/ +EFI_SIMPLE_TEXT_INPUT_PROTOCOL * +CreateSimpleTextInOnFile ( + IN SHELL_FILE_HANDLE FileHandleToUse, + IN EFI_HANDLE *HandleLocation + ) +{ + SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ProtocolToReturn; + EFI_STATUS Status; + UINT64 CurrentPosition; + UINT64 FileSize; + + if ((HandleLocation == NULL) || (FileHandleToUse == NULL)) { + return (NULL); + } + + ProtocolToReturn = AllocateZeroPool (sizeof (SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL)); + if (ProtocolToReturn == NULL) { + return (NULL); + } + + ShellGetFileSize (FileHandleToUse, &FileSize); + ShellGetFilePosition (FileHandleToUse, &CurrentPosition); + + // + // Initialize the protocol members + // + ProtocolToReturn->RemainingBytesOfInputFile = FileSize - CurrentPosition; + ProtocolToReturn->FileHandle = FileHandleToUse; + ProtocolToReturn->SimpleTextIn.Reset = FileBasedSimpleTextInReset; + ProtocolToReturn->SimpleTextIn.ReadKeyStroke = FileBasedSimpleTextInReadKeyStroke; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + ConInWaitForKey, + &ProtocolToReturn->SimpleTextIn, + &ProtocolToReturn->SimpleTextIn.WaitForKey + ); + + if (EFI_ERROR (Status)) { + FreePool (ProtocolToReturn); + return (NULL); + } + + /// @todo possibly also install SimpleTextInputEx on the handle at this point. + Status = gBS->InstallProtocolInterface ( + &(ProtocolToReturn->TheHandle), + &gEfiSimpleTextInProtocolGuid, + EFI_NATIVE_INTERFACE, + &(ProtocolToReturn->SimpleTextIn) + ); + if (!EFI_ERROR (Status)) { + *HandleLocation = ProtocolToReturn->TheHandle; + return ((EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)ProtocolToReturn); + } else { + FreePool (ProtocolToReturn); + return (NULL); + } +} + +/** + Function to close a EFI_SIMPLE_TEXT_INPUT_PROTOCOL on top of a + SHELL_FILE_HANDLE to support redirecting input from a file. + + @param[in] SimpleTextIn The pointer to the SimpleTextIn to close. + + @retval EFI_SUCCESS The object was closed. +**/ +EFI_STATUS +CloseSimpleTextInOnFile ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *SimpleTextIn + ) +{ + EFI_STATUS Status; + EFI_STATUS Status1; + + if (SimpleTextIn == NULL) { + return (EFI_INVALID_PARAMETER); + } + + Status = gBS->CloseEvent (((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)SimpleTextIn)->SimpleTextIn.WaitForKey); + + Status1 = gBS->UninstallProtocolInterface ( + ((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)SimpleTextIn)->TheHandle, + &gEfiSimpleTextInProtocolGuid, + &(((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)SimpleTextIn)->SimpleTextIn) + ); + + FreePool (SimpleTextIn); + if (!EFI_ERROR (Status)) { + return (Status1); + } else { + return (Status); + } +} + +/** + Reset the text output device hardware and optionally run diagnostics. + + @param This pointer to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL + @param ExtendedVerification Indicates that a more extensive test may be performed + + @retval EFI_SUCCESS The text output device was reset. +**/ +EFI_STATUS +EFIAPI +FileBasedSimpleTextOutReset ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + return (EFI_SUCCESS); +} + +/** + Verifies that all characters in a Unicode string can be output to the + target device. + + @param[in] This Protocol instance pointer. + @param[in] WString The NULL-terminated Unicode string to be examined. + + @retval EFI_SUCCESS The device(s) are capable of rendering the output string. +**/ +EFI_STATUS +EFIAPI +FileBasedSimpleTextOutTestString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ) +{ + return (EFI_SUCCESS); +} + +/** + Returns information for an available text mode that the output device(s) + supports. + + @param[in] This Protocol instance pointer. + @param[in] ModeNumber The mode number to return information on. + @param[out] Columns Upon return, the number of columns in the selected geometry + @param[out] Rows Upon return, the number of rows in the selected geometry + + @retval EFI_UNSUPPORTED The mode number was not valid. +**/ +EFI_STATUS +EFIAPI +FileBasedSimpleTextOutQueryMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber, + OUT UINTN *Columns, + OUT UINTN *Rows + ) +{ + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *PassThruProtocol; + + PassThruProtocol = ((SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)This)->OriginalSimpleTextOut; + + // Pass the QueryMode call thru to the original SimpleTextOutProtocol + return (PassThruProtocol->QueryMode ( + PassThruProtocol, + ModeNumber, + Columns, + Rows + )); +} + +/** + Sets the output device(s) to a specified mode. + + @param[in] This Protocol instance pointer. + @param[in] ModeNumber The mode number to set. + + @retval EFI_UNSUPPORTED The mode number was not valid. +**/ +EFI_STATUS +EFIAPI +FileBasedSimpleTextOutSetMode ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber + ) +{ + return (EFI_UNSUPPORTED); +} + +/** + Sets the background and foreground colors for the OutputString () and + ClearScreen () functions. + + @param[in] This Protocol instance pointer. + @param[in] Attribute The attribute to set. Bits 0..3 are the foreground color, and + bits 4..6 are the background color. All other bits are undefined + and must be zero. The valid Attributes are defined in this file. + + @retval EFI_SUCCESS The attribute was set. +**/ +EFI_STATUS +EFIAPI +FileBasedSimpleTextOutSetAttribute ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Attribute + ) +{ + return (EFI_SUCCESS); +} + +/** + Clears the output device(s) display to the currently selected background + color. + + @param[in] This Protocol instance pointer. + + @retval EFI_UNSUPPORTED The output device is not in a valid text mode. +**/ +EFI_STATUS +EFIAPI +FileBasedSimpleTextOutClearScreen ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This + ) +{ + return (EFI_SUCCESS); +} + +/** + Sets the current coordinates of the cursor position + + @param[in] This Protocol instance pointer. + @param[in] Column Column to put the cursor in. Must be between zero and Column returned from QueryMode + @param[in] Row Row to put the cursor in. Must be between zero and Row returned from QueryMode + + @retval EFI_SUCCESS The operation completed successfully. +**/ +EFI_STATUS +EFIAPI +FileBasedSimpleTextOutSetCursorPosition ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Column, + IN UINTN Row + ) +{ + return (EFI_SUCCESS); +} + +/** + Makes the cursor visible or invisible + + @param[in] This Protocol instance pointer. + @param[in] Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is + set to be invisible. + + @retval EFI_SUCCESS The operation completed successfully. +**/ +EFI_STATUS +EFIAPI +FileBasedSimpleTextOutEnableCursor ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN Visible + ) +{ + return (EFI_SUCCESS); +} + +/** + Write a Unicode string to the output device. + + @param[in] This Protocol instance pointer. + @param[in] WString The NULL-terminated Unicode string to be displayed on the output + device(s). All output devices must also support the Unicode + drawing defined in this file. + @retval EFI_SUCCESS The string was output to the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting to output + the text. + @retval EFI_UNSUPPORTED The output device's mode is not currently in a + defined text mode. + @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the + characters in the Unicode string could not be + rendered and were skipped. +**/ +EFI_STATUS +EFIAPI +FileBasedSimpleTextOutOutputString ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *WString + ) +{ + UINTN Size; + + Size = StrLen (WString) * sizeof (CHAR16); + return (ShellInfoObject.NewEfiShellProtocol->WriteFile ( + ((SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)This)->FileHandle, + &Size, + WString + )); +} + +/** + Function to create a EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on top of a + SHELL_FILE_HANDLE to support redirecting output from a file. + + @param[in] FileHandleToUse The pointer to the SHELL_FILE_HANDLE to use. + @param[in] HandleLocation The pointer of a location to copy handle with protocol to. + @param[in] OriginalProtocol The pointer to the original output protocol for pass thru of functions. + + @retval NULL There was insufficient memory available. + @return A pointer to the allocated protocol structure; +**/ +EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL * +CreateSimpleTextOutOnFile ( + IN SHELL_FILE_HANDLE FileHandleToUse, + IN EFI_HANDLE *HandleLocation, + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OriginalProtocol + ) +{ + SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ProtocolToReturn; + EFI_STATUS Status; + + if ((HandleLocation == NULL) || (FileHandleToUse == NULL)) { + return (NULL); + } + + ProtocolToReturn = AllocateZeroPool (sizeof (SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL)); + if (ProtocolToReturn == NULL) { + return (NULL); + } + + ProtocolToReturn->FileHandle = FileHandleToUse; + ProtocolToReturn->OriginalSimpleTextOut = OriginalProtocol; + ProtocolToReturn->SimpleTextOut.Reset = FileBasedSimpleTextOutReset; + ProtocolToReturn->SimpleTextOut.TestString = FileBasedSimpleTextOutTestString; + ProtocolToReturn->SimpleTextOut.QueryMode = FileBasedSimpleTextOutQueryMode; + ProtocolToReturn->SimpleTextOut.SetMode = FileBasedSimpleTextOutSetMode; + ProtocolToReturn->SimpleTextOut.SetAttribute = FileBasedSimpleTextOutSetAttribute; + ProtocolToReturn->SimpleTextOut.ClearScreen = FileBasedSimpleTextOutClearScreen; + ProtocolToReturn->SimpleTextOut.SetCursorPosition = FileBasedSimpleTextOutSetCursorPosition; + ProtocolToReturn->SimpleTextOut.EnableCursor = FileBasedSimpleTextOutEnableCursor; + ProtocolToReturn->SimpleTextOut.OutputString = FileBasedSimpleTextOutOutputString; + ProtocolToReturn->SimpleTextOut.Mode = AllocateZeroPool (sizeof (EFI_SIMPLE_TEXT_OUTPUT_MODE)); + if (ProtocolToReturn->SimpleTextOut.Mode == NULL) { + FreePool (ProtocolToReturn); + return (NULL); + } + + ProtocolToReturn->SimpleTextOut.Mode->MaxMode = OriginalProtocol->Mode->MaxMode; + ProtocolToReturn->SimpleTextOut.Mode->Mode = OriginalProtocol->Mode->Mode; + ProtocolToReturn->SimpleTextOut.Mode->Attribute = OriginalProtocol->Mode->Attribute; + ProtocolToReturn->SimpleTextOut.Mode->CursorColumn = OriginalProtocol->Mode->CursorColumn; + ProtocolToReturn->SimpleTextOut.Mode->CursorRow = OriginalProtocol->Mode->CursorRow; + ProtocolToReturn->SimpleTextOut.Mode->CursorVisible = OriginalProtocol->Mode->CursorVisible; + + Status = gBS->InstallProtocolInterface ( + &(ProtocolToReturn->TheHandle), + &gEfiSimpleTextOutProtocolGuid, + EFI_NATIVE_INTERFACE, + &(ProtocolToReturn->SimpleTextOut) + ); + if (!EFI_ERROR (Status)) { + *HandleLocation = ProtocolToReturn->TheHandle; + return ((EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)ProtocolToReturn); + } else { + SHELL_FREE_NON_NULL (ProtocolToReturn->SimpleTextOut.Mode); + SHELL_FREE_NON_NULL (ProtocolToReturn); + return (NULL); + } +} + +/** + Function to close a EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on top of a + SHELL_FILE_HANDLE to support redirecting output from a file. + + @param[in] SimpleTextOut The pointer to the SimpleTextOUT to close. + + @retval EFI_SUCCESS The object was closed. +**/ +EFI_STATUS +CloseSimpleTextOutOnFile ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOut + ) +{ + EFI_STATUS Status; + + if (SimpleTextOut == NULL) { + return (EFI_INVALID_PARAMETER); + } + + Status = gBS->UninstallProtocolInterface ( + ((SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)SimpleTextOut)->TheHandle, + &gEfiSimpleTextOutProtocolGuid, + &(((SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)SimpleTextOut)->SimpleTextOut) + ); + FreePool (SimpleTextOut->Mode); + FreePool (SimpleTextOut); + return (Status); +} diff --git a/ShellPkg/Application/Shell/ConsoleWrappers.h b/ShellPkg/Application/Shell/ConsoleWrappers.h index 0edbad8e6b..b6d591b895 100644 --- a/ShellPkg/Application/Shell/ConsoleWrappers.h +++ b/ShellPkg/Application/Shell/ConsoleWrappers.h @@ -1,73 +1,73 @@ -/** @file
- Function definitions for shell simple text in and out on top of file handles.
-
- (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR>
- Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#ifndef _SHELL_CONSOLE_WRAPPERS_HEADER_
-#define _SHELL_CONSOLE_WRAPPERS_HEADER_
-
-/**
- Function to create a EFI_SIMPLE_TEXT_INPUT_PROTOCOL on top of a
- SHELL_FILE_HANDLE to support redirecting input from a file.
-
- @param[in] FileHandleToUse The pointer to the SHELL_FILE_HANDLE to use.
- @param[in] HandleLocation The pointer of a location to copy handle with protocol to.
-
- @retval NULL There was insufficient memory available.
- @return A pointer to the allocated protocol structure;
-**/
-EFI_SIMPLE_TEXT_INPUT_PROTOCOL *
-CreateSimpleTextInOnFile (
- IN SHELL_FILE_HANDLE FileHandleToUse,
- IN EFI_HANDLE *HandleLocation
- );
-
-/**
- Function to close a EFI_SIMPLE_TEXT_INPUT_PROTOCOL on top of a
- SHELL_FILE_HANDLE to support redirecting input from a file.
-
- @param[in] SimpleTextIn The pointer to the SimpleTextIn to close.
-
- @retval EFI_SUCCESS The object was closed.
-**/
-EFI_STATUS
-CloseSimpleTextInOnFile (
- IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *SimpleTextIn
- );
-
-/**
- Function to create a EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on top of a
- SHELL_FILE_HANDLE to support redirecting output from a file.
-
- @param[in] FileHandleToUse The pointer to the SHELL_FILE_HANDLE to use.
- @param[in] HandleLocation The pointer of a location to copy handle with protocol to.
- @param[in] OriginalProtocol The pointer to the original output protocol for pass thru of functions.
-
- @retval NULL There was insufficient memory available.
- @return A pointer to the allocated protocol structure;
-**/
-EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *
-CreateSimpleTextOutOnFile (
- IN SHELL_FILE_HANDLE FileHandleToUse,
- IN EFI_HANDLE *HandleLocation,
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OriginalProtocol
- );
-
-/**
- Function to close a EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on top of a
- SHELL_FILE_HANDLE to support redirecting output from a file.
-
- @param[in] SimpleTextOut The pointer to the SimpleTextOUT to close.
-
- @retval EFI_SUCCESS The object was closed.
-**/
-EFI_STATUS
-CloseSimpleTextOutOnFile (
- IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOut
- );
-
-#endif //_SHELL_CONSOLE_WRAPPERS_HEADER_
+/** @file + Function definitions for shell simple text in and out on top of file handles. + + (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR> + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SHELL_CONSOLE_WRAPPERS_HEADER_ +#define _SHELL_CONSOLE_WRAPPERS_HEADER_ + +/** + Function to create a EFI_SIMPLE_TEXT_INPUT_PROTOCOL on top of a + SHELL_FILE_HANDLE to support redirecting input from a file. + + @param[in] FileHandleToUse The pointer to the SHELL_FILE_HANDLE to use. + @param[in] HandleLocation The pointer of a location to copy handle with protocol to. + + @retval NULL There was insufficient memory available. + @return A pointer to the allocated protocol structure; +**/ +EFI_SIMPLE_TEXT_INPUT_PROTOCOL * +CreateSimpleTextInOnFile ( + IN SHELL_FILE_HANDLE FileHandleToUse, + IN EFI_HANDLE *HandleLocation + ); + +/** + Function to close a EFI_SIMPLE_TEXT_INPUT_PROTOCOL on top of a + SHELL_FILE_HANDLE to support redirecting input from a file. + + @param[in] SimpleTextIn The pointer to the SimpleTextIn to close. + + @retval EFI_SUCCESS The object was closed. +**/ +EFI_STATUS +CloseSimpleTextInOnFile ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *SimpleTextIn + ); + +/** + Function to create a EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on top of a + SHELL_FILE_HANDLE to support redirecting output from a file. + + @param[in] FileHandleToUse The pointer to the SHELL_FILE_HANDLE to use. + @param[in] HandleLocation The pointer of a location to copy handle with protocol to. + @param[in] OriginalProtocol The pointer to the original output protocol for pass thru of functions. + + @retval NULL There was insufficient memory available. + @return A pointer to the allocated protocol structure; +**/ +EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL * +CreateSimpleTextOutOnFile ( + IN SHELL_FILE_HANDLE FileHandleToUse, + IN EFI_HANDLE *HandleLocation, + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OriginalProtocol + ); + +/** + Function to close a EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on top of a + SHELL_FILE_HANDLE to support redirecting output from a file. + + @param[in] SimpleTextOut The pointer to the SimpleTextOUT to close. + + @retval EFI_SUCCESS The object was closed. +**/ +EFI_STATUS +CloseSimpleTextOutOnFile ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOut + ); + +#endif //_SHELL_CONSOLE_WRAPPERS_HEADER_ diff --git a/ShellPkg/Application/Shell/FileHandleInternal.h b/ShellPkg/Application/Shell/FileHandleInternal.h index 2ad2bd098f..ad32b0cb90 100644 --- a/ShellPkg/Application/Shell/FileHandleInternal.h +++ b/ShellPkg/Application/Shell/FileHandleInternal.h @@ -1,58 +1,58 @@ -/** @file
- internal worker functions for FileHandleWrappers to use
-
- Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#ifndef _FILE_HANDLE_INTERNAL_HEADER_
-#define _FILE_HANDLE_INTERNAL_HEADER_
-
-/**
- Move the cursor position one character backward.
-
- @param[in] LineLength Length of a line. Get it by calling QueryMode
- @param[in, out] Column Current column of the cursor position
- @param[in, out] Row Current row of the cursor position
-**/
-VOID
-MoveCursorBackward (
- IN UINTN LineLength,
- IN OUT UINTN *Column,
- IN OUT UINTN *Row
- );
-
-/**
- Move the cursor position one character forward.
-
- @param[in] LineLength Length of a line.
- @param[in] TotalRow Total row of a screen
- @param[in, out] Column Current column of the cursor position
- @param[in, out] Row Current row of the cursor position
-**/
-VOID
-MoveCursorForward (
- IN UINTN LineLength,
- IN UINTN TotalRow,
- IN OUT UINTN *Column,
- IN OUT UINTN *Row
- );
-
-/**
- Prints out each previously typed command in the command list history log.
-
- When each screen is full it will pause for a key before continuing.
-
- @param[in] TotalCols How many columns are on the screen
- @param[in] TotalRows How many rows are on the screen
- @param[in] StartColumn which column to start at
-**/
-VOID
-PrintCommandHistory (
- IN CONST UINTN TotalCols,
- IN CONST UINTN TotalRows,
- IN CONST UINTN StartColumn
- );
-
-#endif //_FILE_HANDLE_INTERNAL_HEADER_
+/** @file + internal worker functions for FileHandleWrappers to use + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _FILE_HANDLE_INTERNAL_HEADER_ +#define _FILE_HANDLE_INTERNAL_HEADER_ + +/** + Move the cursor position one character backward. + + @param[in] LineLength Length of a line. Get it by calling QueryMode + @param[in, out] Column Current column of the cursor position + @param[in, out] Row Current row of the cursor position +**/ +VOID +MoveCursorBackward ( + IN UINTN LineLength, + IN OUT UINTN *Column, + IN OUT UINTN *Row + ); + +/** + Move the cursor position one character forward. + + @param[in] LineLength Length of a line. + @param[in] TotalRow Total row of a screen + @param[in, out] Column Current column of the cursor position + @param[in, out] Row Current row of the cursor position +**/ +VOID +MoveCursorForward ( + IN UINTN LineLength, + IN UINTN TotalRow, + IN OUT UINTN *Column, + IN OUT UINTN *Row + ); + +/** + Prints out each previously typed command in the command list history log. + + When each screen is full it will pause for a key before continuing. + + @param[in] TotalCols How many columns are on the screen + @param[in] TotalRows How many rows are on the screen + @param[in] StartColumn which column to start at +**/ +VOID +PrintCommandHistory ( + IN CONST UINTN TotalCols, + IN CONST UINTN TotalRows, + IN CONST UINTN StartColumn + ); + +#endif //_FILE_HANDLE_INTERNAL_HEADER_ diff --git a/ShellPkg/Application/Shell/FileHandleWrappers.c b/ShellPkg/Application/Shell/FileHandleWrappers.c index 2ba9b3401a..ed1bcecb7b 100644 --- a/ShellPkg/Application/Shell/FileHandleWrappers.c +++ b/ShellPkg/Application/Shell/FileHandleWrappers.c @@ -1,2194 +1,2194 @@ -/** @file
- EFI_FILE_PROTOCOL wrappers for other items (Like Environment Variables,
- StdIn, StdOut, StdErr, etc...).
-
- Copyright 2016 Dell Inc.
- Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
- (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "Shell.h"
-#include "FileHandleInternal.h"
-
-#define MEM_WRITE_REALLOC_OVERHEAD 1024
-
-/**
- File style interface for console (Open).
-
- @param[in] This Ignored.
- @param[out] NewHandle Ignored.
- @param[in] FileName Ignored.
- @param[in] OpenMode Ignored.
- @param[in] Attributes Ignored.
-
- @retval EFI_NOT_FOUND
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceOpenNotFound (
- IN EFI_FILE_PROTOCOL *This,
- OUT EFI_FILE_PROTOCOL **NewHandle,
- IN CHAR16 *FileName,
- IN UINT64 OpenMode,
- IN UINT64 Attributes
- )
-{
- return (EFI_NOT_FOUND);
-}
-
-/**
- File style interface for console (Close, Delete, & Flush)
-
- @param[in] This Ignored.
-
- @retval EFI_SUCCESS
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceNopGeneric (
- IN EFI_FILE_PROTOCOL *This
- )
-{
- return (EFI_SUCCESS);
-}
-
-/**
- File style interface for console (GetPosition).
-
- @param[in] This Ignored.
- @param[out] Position Ignored.
-
- @retval EFI_UNSUPPORTED
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceNopGetPosition (
- IN EFI_FILE_PROTOCOL *This,
- OUT UINT64 *Position
- )
-{
- return (EFI_UNSUPPORTED);
-}
-
-/**
- File style interface for console (SetPosition).
-
- @param[in] This Ignored.
- @param[in] Position Ignored.
-
- @retval EFI_UNSUPPORTED
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceNopSetPosition (
- IN EFI_FILE_PROTOCOL *This,
- IN UINT64 Position
- )
-{
- return (EFI_UNSUPPORTED);
-}
-
-/**
- File style interface for console (GetInfo).
-
- @param[in] This Ignored.
- @param[in] InformationType Ignored.
- @param[in, out] BufferSize Ignored.
- @param[out] Buffer Ignored.
-
- @retval EFI_UNSUPPORTED
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceNopGetInfo (
- IN EFI_FILE_PROTOCOL *This,
- IN EFI_GUID *InformationType,
- IN OUT UINTN *BufferSize,
- OUT VOID *Buffer
- )
-{
- return (EFI_UNSUPPORTED);
-}
-
-/**
- File style interface for console (SetInfo).
-
- @param[in] This Ignored.
- @param[in] InformationType Ignored.
- @param[in] BufferSize Ignored.
- @param[in] Buffer Ignored.
-
- @retval EFI_UNSUPPORTED
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceNopSetInfo (
- IN EFI_FILE_PROTOCOL *This,
- IN EFI_GUID *InformationType,
- IN UINTN BufferSize,
- IN VOID *Buffer
- )
-{
- return (EFI_UNSUPPORTED);
-}
-
-/**
- File style interface for StdOut (Write).
-
- Writes data to the screen.
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
- @param[in, out] BufferSize Size in bytes of Buffer.
- @param[in] Buffer The pointer to the buffer to write.
-
- @retval EFI_UNSUPPORTED No output console is supported.
- @return A return value from gST->ConOut->OutputString.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceStdOutWrite (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- IN VOID *Buffer
- )
-{
- if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
- return (EFI_UNSUPPORTED);
- }
-
- if (*((CHAR16 *)Buffer) == gUnicodeFileTag) {
- return (gST->ConOut->OutputString (gST->ConOut, (CHAR16 *)Buffer + 1));
- }
-
- return (gST->ConOut->OutputString (gST->ConOut, Buffer));
-}
-
-/**
- File style interface for StdIn (Write).
-
- @param[in] This Ignored.
- @param[in, out] BufferSize Ignored.
- @param[in] Buffer Ignored.
-
- @retval EFI_UNSUPPORTED
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceStdInWrite (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- IN VOID *Buffer
- )
-{
- return (EFI_UNSUPPORTED);
-}
-
-/**
- File style interface for console StdErr (Write).
-
- Writes error to the error output.
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
- @param[in, out] BufferSize Size in bytes of Buffer.
- @param[in] Buffer The pointer to the buffer to write.
-
- @return A return value from gST->StdErr->OutputString.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceStdErrWrite (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- IN VOID *Buffer
- )
-{
- return (gST->StdErr->OutputString (gST->StdErr, Buffer));
-}
-
-/**
- File style interface for console StdOut (Read).
-
- @param[in] This Ignored.
- @param[in, out] BufferSize Ignored.
- @param[out] Buffer Ignored.
-
- @retval EFI_UNSUPPORTED
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceStdOutRead (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- OUT VOID *Buffer
- )
-{
- return (EFI_UNSUPPORTED);
-}
-
-/**
- File style interface for console StdErr (Read).
-
- @param[in] This Ignored.
- @param[in, out] BufferSize Ignored.
- @param[out] Buffer Ignored.
-
- @retval EFI_UNSUPPORTED Always.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceStdErrRead (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- OUT VOID *Buffer
- )
-{
- return (EFI_UNSUPPORTED);
-}
-
-/**
- File style interface for NUL file (Read).
-
- @param[in] This Ignored.
- @param[in, out] BufferSize Poiner to 0 upon return.
- @param[out] Buffer Ignored.
-
- @retval EFI_SUCCESS Always.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceNulRead (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- OUT VOID *Buffer
- )
-{
- *BufferSize = 0;
- return (EFI_SUCCESS);
-}
-
-/**
- File style interface for NUL file (Write).
-
- @param[in] This Ignored.
- @param[in, out] BufferSize Ignored.
- @param[in] Buffer Ignored.
-
- @retval EFI_SUCCESS
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceNulWrite (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- IN VOID *Buffer
- )
-{
- return (EFI_SUCCESS);
-}
-
-/**
- Create the TAB completion list.
-
- @param[in] InputString The command line to expand.
- @param[in] StringLen Length of the command line.
- @param[in] BufferSize Buffer size.
- @param[in, out] TabCompletionList Return the TAB completion list.
- @param[in, out] TabUpdatePos Return the TAB update position.
-**/
-EFI_STATUS
-CreateTabCompletionList (
- IN CONST CHAR16 *InputString,
- IN CONST UINTN StringLen,
- IN CONST UINTN BufferSize,
- IN OUT EFI_SHELL_FILE_INFO **TabCompletionList,
- IN OUT UINTN *TabUpdatePos
- )
-{
- BOOLEAN InQuotation;
- UINTN TabPos;
- UINTN Index;
- CONST CHAR16 *Cwd;
- EFI_STATUS Status;
- CHAR16 *TabStr;
- EFI_SHELL_FILE_INFO *FileList;
- EFI_SHELL_FILE_INFO *FileInfo;
- EFI_SHELL_FILE_INFO *TempFileInfo;
-
- //
- // Allocate buffers
- //
- TabStr = AllocateZeroPool (BufferSize);
- if (TabStr == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- //
- // handle auto complete of file and directory names...
- // E.g.: cd fs0:\EFI\Bo<TAB>
- // ^ ^
- // TabPos TabUpdatePos
- //
- TabPos = 0;
- *TabUpdatePos = 0;
- FileList = NULL;
- InQuotation = FALSE;
- for (Index = 0; Index < StringLen; Index++) {
- switch (InputString[Index]) {
- case L'\"':
- InQuotation = (BOOLEAN)(!InQuotation);
- break;
-
- case L' ':
- if (!InQuotation) {
- TabPos = Index + 1;
- *TabUpdatePos = TabPos;
- }
-
- break;
-
- case L':':
- //
- // handle the case "fs0:<TAB>"
- // Update the TabUpdatePos as well.
- //
- case L'\\':
- *TabUpdatePos = Index + 1;
- break;
-
- default:
- break;
- }
- }
-
- if (StrStr (InputString + TabPos, L":") == NULL) {
- //
- // If file path doesn't contain ":", ...
- //
- Cwd = ShellInfoObject.NewEfiShellProtocol->GetCurDir (NULL);
- if (Cwd != NULL) {
- if (InputString[TabPos] != L'\\') {
- //
- // and it doesn't begin with "\\", it's a path relative to current directory.
- // TabStr = "<cwd>\\"
- //
- StrnCpyS (TabStr, BufferSize / sizeof (CHAR16), Cwd, (BufferSize) / sizeof (CHAR16) - 1);
- StrCatS (TabStr, (BufferSize) / sizeof (CHAR16), L"\\");
- } else {
- //
- // and it begins with "\\", it's a path pointing to root directory of current map.
- // TabStr = "fsx:"
- //
- Index = StrStr (Cwd, L":") - Cwd + 1;
- StrnCpyS (TabStr, BufferSize / sizeof (CHAR16), Cwd, Index);
- }
- }
- }
-
- StrnCatS (TabStr, (BufferSize) / sizeof (CHAR16), InputString + TabPos, StringLen - TabPos);
- StrnCatS (TabStr, (BufferSize) / sizeof (CHAR16), L"*", (BufferSize) / sizeof (CHAR16) - 1 - StrLen (TabStr));
- Status = ShellInfoObject.NewEfiShellProtocol->FindFiles (TabStr, &FileList);
-
- //
- // Filter out the non-directory for "CD" command
- // Filter "." and ".." for all
- //
- if (!EFI_ERROR (Status) && (FileList != NULL)) {
- //
- // Skip the spaces in the beginning
- //
- while (*InputString == L' ') {
- InputString++;
- }
-
- for (FileInfo = (EFI_SHELL_FILE_INFO *)GetFirstNode (&FileList->Link); !IsNull (&FileList->Link, &FileInfo->Link); ) {
- if (((StrCmp (FileInfo->FileName, L".") == 0) || (StrCmp (FileInfo->FileName, L"..") == 0)) ||
- ((((InputString[0] == L'c') || (InputString[0] == L'C')) && ((InputString[1] == L'd') || (InputString[1] == L'D'))) &&
- (ShellIsDirectory (FileInfo->FullName) != EFI_SUCCESS)))
- {
- TempFileInfo = FileInfo;
- FileInfo = (EFI_SHELL_FILE_INFO *)RemoveEntryList (&FileInfo->Link);
- InternalFreeShellFileInfoNode (TempFileInfo);
- } else {
- FileInfo = (EFI_SHELL_FILE_INFO *)GetNextNode (&FileList->Link, &FileInfo->Link);
- }
- }
- }
-
- if ((FileList != NULL) && !IsListEmpty (&FileList->Link)) {
- Status = EFI_SUCCESS;
- } else {
- ShellInfoObject.NewEfiShellProtocol->FreeFileList (&FileList);
- Status = EFI_NOT_FOUND;
- }
-
- FreePool (TabStr);
-
- *TabCompletionList = FileList;
- return Status;
-}
-
-/**
- File style interface for console (Read).
-
- This will return a single line of input from the console.
-
- @param This A pointer to the EFI_FILE_PROTOCOL instance that is the
- file handle to read data from. Not used.
- @param BufferSize On input, the size of the Buffer. On output, the amount
- of data returned in Buffer. In both cases, the size is
- measured in bytes.
- @param Buffer The buffer into which the data is read.
-
-
- @retval EFI_SUCCESS The data was read.
- @retval EFI_NO_MEDIA The device has no medium.
- @retval EFI_DEVICE_ERROR The device reported an error.
- @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted file.
- @retval EFI_DEVICE_ERROR On entry, the current file position is beyond the end of the file.
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
- @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory
- entry. BufferSize has been updated with the size
- needed to complete the request.
- @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceStdInRead (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- OUT VOID *Buffer
- )
-{
- CHAR16 *CurrentString;
- BOOLEAN Done;
- UINTN TabUpdatePos; // Start index of the string updated by TAB stroke
- UINTN Column; // Column of current cursor
- UINTN Row; // Row of current cursor
- UINTN StartColumn; // Column at the beginning of the line
- UINTN Update; // Line index for update
- UINTN Delete; // Num of chars to delete from console after update
- UINTN StringLen; // Total length of the line
- UINTN StringCurPos; // Line index corresponding to the cursor
- UINTN MaxStr; // Maximum possible line length
- UINTN TotalColumn; // Num of columns in the console
- UINTN TotalRow; // Num of rows in the console
- UINTN SkipLength;
- UINTN OutputLength; // Length of the update string
- UINTN TailRow; // Row of end of line
- UINTN TailColumn; // Column of end of line
- EFI_INPUT_KEY Key;
-
- BUFFER_LIST *LinePos;
- BUFFER_LIST *NewPos;
- BOOLEAN InScrolling;
- EFI_STATUS Status;
- BOOLEAN InTabScrolling; // Whether in TAB-completion state
- EFI_SHELL_FILE_INFO *TabCompleteList;
- EFI_SHELL_FILE_INFO *TabCurrent;
- UINTN EventIndex;
- CHAR16 *TabOutputStr;
-
- //
- // If buffer is not large enough to hold a CHAR16, return minimum buffer size
- //
- if (*BufferSize < sizeof (CHAR16) * 2) {
- *BufferSize = sizeof (CHAR16) * 2;
- return (EFI_BUFFER_TOO_SMALL);
- }
-
- Done = FALSE;
- CurrentString = Buffer;
- StringLen = 0;
- StringCurPos = 0;
- OutputLength = 0;
- Update = 0;
- Delete = 0;
- LinePos = NewPos = (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory);
- InScrolling = FALSE;
- InTabScrolling = FALSE;
- Status = EFI_SUCCESS;
- TabOutputStr = NULL;
- TabUpdatePos = 0;
- TabCompleteList = NULL;
- TabCurrent = NULL;
-
- //
- // Get the screen setting and the current cursor location
- //
- Column = StartColumn = gST->ConOut->Mode->CursorColumn;
- Row = gST->ConOut->Mode->CursorRow;
- gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &TotalColumn, &TotalRow);
-
- //
- // Limit the line length to the buffer size or the minimum size of the
- // screen. (The smaller takes effect)
- //
- MaxStr = TotalColumn * (TotalRow - 1) - StartColumn;
- if (MaxStr > *BufferSize / sizeof (CHAR16)) {
- MaxStr = *BufferSize / sizeof (CHAR16);
- }
-
- ZeroMem (CurrentString, MaxStr * sizeof (CHAR16));
- do {
- //
- // Read a key
- //
- gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
- Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
- if (EFI_ERROR (Status)) {
- if (Status == EFI_NOT_READY) {
- continue;
- }
-
- ZeroMem (CurrentString, MaxStr * sizeof (CHAR16));
- StringLen = 0;
- break;
- }
-
- //
- // Press PageUp or PageDown to scroll the history screen up or down.
- // Press any other key to quit scrolling.
- //
- if ((Key.UnicodeChar == 0) && ((Key.ScanCode == SCAN_PAGE_UP) || (Key.ScanCode == SCAN_PAGE_DOWN))) {
- if (Key.ScanCode == SCAN_PAGE_UP) {
- ConsoleLoggerDisplayHistory (FALSE, 0, ShellInfoObject.ConsoleInfo);
- } else if (Key.ScanCode == SCAN_PAGE_DOWN) {
- ConsoleLoggerDisplayHistory (TRUE, 0, ShellInfoObject.ConsoleInfo);
- }
-
- InScrolling = TRUE;
- } else {
- if (InScrolling) {
- ConsoleLoggerStopHistory (ShellInfoObject.ConsoleInfo);
- InScrolling = FALSE;
- }
- }
-
- //
- // If we are quitting TAB scrolling...
- //
- if (InTabScrolling && (Key.UnicodeChar != CHAR_TAB)) {
- if (TabCompleteList != NULL) {
- ShellInfoObject.NewEfiShellProtocol->FreeFileList (&TabCompleteList);
- DEBUG_CODE (
- TabCompleteList = NULL;
- );
- }
-
- InTabScrolling = FALSE;
- }
-
- switch (Key.UnicodeChar) {
- case CHAR_CARRIAGE_RETURN:
- //
- // All done, print a newline at the end of the string
- //
- TailRow = Row + (StringLen - StringCurPos + Column) / TotalColumn;
- TailColumn = (StringLen - StringCurPos + Column) % TotalColumn;
- ShellPrintEx ((INT32)TailColumn, (INT32)TailRow, L"%N\n");
- Done = TRUE;
- break;
-
- case CHAR_BACKSPACE:
- if (StringCurPos != 0) {
- //
- // If not move back beyond string beginning, move all characters behind
- // the current position one character forward
- //
- StringCurPos--;
- Update = StringCurPos;
- Delete = 1;
- CopyMem (CurrentString + StringCurPos, CurrentString + StringCurPos + 1, sizeof (CHAR16) * (StringLen - StringCurPos));
-
- //
- // Adjust the current column and row
- //
- MoveCursorBackward (TotalColumn, &Column, &Row);
- }
-
- break;
-
- case CHAR_TAB:
- if (!InTabScrolling) {
- TabCurrent = NULL;
- //
- // Initialize a tab complete operation.
- //
- Status = CreateTabCompletionList (CurrentString, StringLen, *BufferSize, &TabCompleteList, &TabUpdatePos);
- if (!EFI_ERROR (Status)) {
- InTabScrolling = TRUE;
- }
-
- //
- // We do not set up the replacement.
- // The next section will do that.
- //
- }
-
- if (InTabScrolling) {
- //
- // We are in a tab complete operation.
- // set up the next replacement.
- //
- ASSERT (TabCompleteList != NULL);
- if (TabCurrent == NULL) {
- TabCurrent = (EFI_SHELL_FILE_INFO *)GetFirstNode (&TabCompleteList->Link);
- } else {
- TabCurrent = (EFI_SHELL_FILE_INFO *)GetNextNode (&TabCompleteList->Link, &TabCurrent->Link);
- }
-
- //
- // Skip over the empty list beginning node
- //
- if (IsNull (&TabCompleteList->Link, &TabCurrent->Link)) {
- TabCurrent = (EFI_SHELL_FILE_INFO *)GetNextNode (&TabCompleteList->Link, &TabCurrent->Link);
- }
- }
-
- break;
-
- default:
- if (Key.UnicodeChar >= ' ') {
- //
- // If we are at the buffer's end, drop the key
- //
- if ((StringLen == MaxStr - 1) && (ShellInfoObject.ViewingSettings.InsertMode || (StringCurPos == StringLen))) {
- break;
- }
-
- //
- // If in insert mode, make space by moving each other character 1
- // space higher in the array
- //
- if (ShellInfoObject.ViewingSettings.InsertMode) {
- CopyMem (CurrentString + StringCurPos + 1, CurrentString + StringCurPos, (StringLen - StringCurPos)*sizeof (CurrentString[0]));
- }
-
- CurrentString[StringCurPos] = Key.UnicodeChar;
- Update = StringCurPos;
-
- StringCurPos += 1;
- OutputLength = 1;
- }
-
- break;
-
- case 0:
- switch (Key.ScanCode) {
- case SCAN_DELETE:
- //
- // Move characters behind current position one character forward
- //
- if (StringLen != 0) {
- Update = StringCurPos;
- Delete = 1;
- CopyMem (CurrentString + StringCurPos, CurrentString + StringCurPos + 1, sizeof (CHAR16) * (StringLen - StringCurPos));
- }
-
- break;
-
- case SCAN_UP:
- //
- // Prepare to print the previous command
- //
- NewPos = (BUFFER_LIST *)GetPreviousNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link);
- if (IsNull (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link)) {
- NewPos = (BUFFER_LIST *)GetPreviousNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link);
- }
-
- break;
-
- case SCAN_DOWN:
- //
- // Prepare to print the next command
- //
- NewPos = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link);
- if (NewPos == (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory)) {
- NewPos = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link);
- }
-
- break;
-
- case SCAN_LEFT:
- //
- // Adjust current cursor position
- //
- if (StringCurPos != 0) {
- --StringCurPos;
- MoveCursorBackward (TotalColumn, &Column, &Row);
- }
-
- break;
-
- case SCAN_RIGHT:
- //
- // Adjust current cursor position
- //
- if (StringCurPos < StringLen) {
- ++StringCurPos;
- MoveCursorForward (TotalColumn, TotalRow, &Column, &Row);
- }
-
- break;
-
- case SCAN_HOME:
- //
- // Move current cursor position to the beginning of the command line
- //
- Row -= (StringCurPos + StartColumn) / TotalColumn;
- Column = StartColumn;
- StringCurPos = 0;
- break;
-
- case SCAN_END:
- //
- // Move current cursor position to the end of the command line
- //
- TailRow = Row + (StringLen - StringCurPos + Column) / TotalColumn;
- TailColumn = (StringLen - StringCurPos + Column) % TotalColumn;
- Row = TailRow;
- Column = TailColumn;
- StringCurPos = StringLen;
- break;
-
- case SCAN_ESC:
- //
- // Prepare to clear the current command line
- //
- CurrentString[0] = 0;
- Update = 0;
- Delete = StringLen;
- Row -= (StringCurPos + StartColumn) / TotalColumn;
- Column = StartColumn;
- OutputLength = 0;
- break;
-
- case SCAN_INSERT:
- //
- // Toggle the SEnvInsertMode flag
- //
- ShellInfoObject.ViewingSettings.InsertMode = (BOOLEAN) !ShellInfoObject.ViewingSettings.InsertMode;
- break;
-
- case SCAN_F7:
- //
- // Print command history
- //
- PrintCommandHistory (TotalColumn, TotalRow, 4);
- *CurrentString = CHAR_NULL;
- Done = TRUE;
- break;
- }
- }
-
- if (Done) {
- break;
- }
-
- //
- // If we are in auto-complete mode, we are preparing to print
- // the next file or directory name
- //
- if (InTabScrolling) {
- TabOutputStr = AllocateZeroPool (*BufferSize);
- if (TabOutputStr == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- }
- }
-
- if (InTabScrolling && (TabOutputStr != NULL)) {
- //
- // Adjust the column and row to the start of TAB-completion string.
- //
- Column = (StartColumn + TabUpdatePos) % TotalColumn;
- Row -= (StartColumn + StringCurPos) / TotalColumn - (StartColumn + TabUpdatePos) / TotalColumn;
- OutputLength = StrLen (TabCurrent->FileName);
- //
- // if the output string contains blank space, quotation marks L'\"'
- // should be added to the output.
- //
- if (StrStr (TabCurrent->FileName, L" ") != NULL) {
- TabOutputStr[0] = L'\"';
- CopyMem (TabOutputStr + 1, TabCurrent->FileName, OutputLength * sizeof (CHAR16));
- TabOutputStr[OutputLength + 1] = L'\"';
- TabOutputStr[OutputLength + 2] = CHAR_NULL;
- } else {
- CopyMem (TabOutputStr, TabCurrent->FileName, OutputLength * sizeof (CHAR16));
- TabOutputStr[OutputLength] = CHAR_NULL;
- }
-
- OutputLength = StrLen (TabOutputStr) < MaxStr - 1 ? StrLen (TabOutputStr) : MaxStr - 1;
- CopyMem (CurrentString + TabUpdatePos, TabOutputStr, OutputLength * sizeof (CHAR16));
- CurrentString[TabUpdatePos + OutputLength] = CHAR_NULL;
- StringCurPos = TabUpdatePos + OutputLength;
- Update = TabUpdatePos;
- if (StringLen > TabUpdatePos + OutputLength) {
- Delete = StringLen - TabUpdatePos - OutputLength;
- }
-
- FreePool (TabOutputStr);
- }
-
- //
- // If we have a new position, we are preparing to print a previous or
- // next command.
- //
- if (NewPos != (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory)) {
- Column = StartColumn;
- Row -= (StringCurPos + StartColumn) / TotalColumn;
-
- LinePos = NewPos;
- NewPos = (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory);
-
- OutputLength = StrLen (LinePos->Buffer) < MaxStr - 1 ? StrLen (LinePos->Buffer) : MaxStr - 1;
- CopyMem (CurrentString, LinePos->Buffer, OutputLength * sizeof (CHAR16));
- CurrentString[OutputLength] = CHAR_NULL;
-
- StringCurPos = OutputLength;
-
- //
- // Draw new input string
- //
- Update = 0;
- if (StringLen > OutputLength) {
- //
- // If old string was longer, blank its tail
- //
- Delete = StringLen - OutputLength;
- }
- }
-
- //
- // If we need to update the output do so now
- //
- if (Update != (UINTN)-1) {
- ShellPrintEx ((INT32)Column, (INT32)Row, L"%s%.*s", CurrentString + Update, Delete, L"");
- StringLen = StrLen (CurrentString);
-
- if (Delete != 0) {
- SetMem (CurrentString + StringLen, Delete * sizeof (CHAR16), CHAR_NULL);
- }
-
- if (StringCurPos > StringLen) {
- StringCurPos = StringLen;
- }
-
- Update = (UINTN)-1;
-
- //
- // After using print to reflect newly updates, if we're not using
- // BACKSPACE and DELETE, we need to move the cursor position forward,
- // so adjust row and column here.
- //
- if ((Key.UnicodeChar != CHAR_BACKSPACE) && !((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_DELETE))) {
- //
- // Calculate row and column of the tail of current string
- //
- TailRow = Row + (StringLen - StringCurPos + Column + OutputLength) / TotalColumn;
- TailColumn = (StringLen - StringCurPos + Column + OutputLength) % TotalColumn;
-
- //
- // If the tail of string reaches screen end, screen rolls up, so if
- // Row does not equal TailRow, Row should be decremented
- //
- // (if we are recalling commands using UPPER and DOWN key, and if the
- // old command is too long to fit the screen, TailColumn must be 79.
- //
- if ((TailColumn == 0) && (TailRow >= TotalRow) && (Row != TailRow)) {
- Row--;
- }
-
- //
- // Calculate the cursor position after current operation. If cursor
- // reaches line end, update both row and column, otherwise, only
- // column will be changed.
- //
- if (Column + OutputLength >= TotalColumn) {
- SkipLength = OutputLength - (TotalColumn - Column);
-
- Row += SkipLength / TotalColumn + 1;
- if (Row > TotalRow - 1) {
- Row = TotalRow - 1;
- }
-
- Column = SkipLength % TotalColumn;
- } else {
- Column += OutputLength;
- }
- }
-
- Delete = 0;
- }
-
- //
- // Set the cursor position for this key
- //
- gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row);
- } while (!Done);
-
- if ((CurrentString != NULL) && (StrLen (CurrentString) > 0)) {
- //
- // add the line to the history buffer
- //
- AddLineToCommandHistory (CurrentString);
- }
-
- //
- // Return the data to the caller
- //
- *BufferSize = StringLen * sizeof (CHAR16);
-
- //
- // if this was used it should be deallocated by now...
- // prevent memory leaks...
- //
- if (TabCompleteList != NULL) {
- ShellInfoObject.NewEfiShellProtocol->FreeFileList (&TabCompleteList);
- }
-
- ASSERT (TabCompleteList == NULL);
-
- return Status;
-}
-
-//
-// FILE style interfaces for StdIn/StdOut/StdErr
-//
-EFI_FILE_PROTOCOL FileInterfaceStdIn = {
- EFI_FILE_REVISION,
- FileInterfaceOpenNotFound,
- FileInterfaceNopGeneric,
- FileInterfaceNopGeneric,
- FileInterfaceStdInRead,
- FileInterfaceStdInWrite,
- FileInterfaceNopGetPosition,
- FileInterfaceNopSetPosition,
- FileInterfaceNopGetInfo,
- FileInterfaceNopSetInfo,
- FileInterfaceNopGeneric
-};
-
-EFI_FILE_PROTOCOL FileInterfaceStdOut = {
- EFI_FILE_REVISION,
- FileInterfaceOpenNotFound,
- FileInterfaceNopGeneric,
- FileInterfaceNopGeneric,
- FileInterfaceStdOutRead,
- FileInterfaceStdOutWrite,
- FileInterfaceNopGetPosition,
- FileInterfaceNopSetPosition,
- FileInterfaceNopGetInfo,
- FileInterfaceNopSetInfo,
- FileInterfaceNopGeneric
-};
-
-EFI_FILE_PROTOCOL FileInterfaceStdErr = {
- EFI_FILE_REVISION,
- FileInterfaceOpenNotFound,
- FileInterfaceNopGeneric,
- FileInterfaceNopGeneric,
- FileInterfaceStdErrRead,
- FileInterfaceStdErrWrite,
- FileInterfaceNopGetPosition,
- FileInterfaceNopSetPosition,
- FileInterfaceNopGetInfo,
- FileInterfaceNopSetInfo,
- FileInterfaceNopGeneric
-};
-
-EFI_FILE_PROTOCOL FileInterfaceNulFile = {
- EFI_FILE_REVISION,
- FileInterfaceOpenNotFound,
- FileInterfaceNopGeneric,
- FileInterfaceNopGeneric,
- FileInterfaceNulRead,
- FileInterfaceNulWrite,
- FileInterfaceNopGetPosition,
- FileInterfaceNopSetPosition,
- FileInterfaceNopGetInfo,
- FileInterfaceNopSetInfo,
- FileInterfaceNopGeneric
-};
-
-//
-// This is identical to EFI_FILE_PROTOCOL except for the additional member
-// for the name.
-//
-
-typedef struct {
- UINT64 Revision;
- EFI_FILE_OPEN Open;
- EFI_FILE_CLOSE Close;
- EFI_FILE_DELETE Delete;
- EFI_FILE_READ Read;
- EFI_FILE_WRITE Write;
- EFI_FILE_GET_POSITION GetPosition;
- EFI_FILE_SET_POSITION SetPosition;
- EFI_FILE_GET_INFO GetInfo;
- EFI_FILE_SET_INFO SetInfo;
- EFI_FILE_FLUSH Flush;
- CHAR16 Name[1];
-} EFI_FILE_PROTOCOL_ENVIRONMENT;
-// ANSI compliance helper to get size of the struct.
-#define SIZE_OF_EFI_FILE_PROTOCOL_ENVIRONMENT EFI_FIELD_OFFSET (EFI_FILE_PROTOCOL_ENVIRONMENT, Name)
-
-/**
- File style interface for Environment Variable (Close).
-
- Frees the memory for this object.
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
-
- @retval EFI_SUCCESS
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceEnvClose (
- IN EFI_FILE_PROTOCOL *This
- )
-{
- VOID *NewBuffer;
- UINTN NewSize;
- EFI_STATUS Status;
- BOOLEAN Volatile;
- UINTN TotalSize;
-
- //
- // Most if not all UEFI commands will have an '\r\n' at the end of any output.
- // Since the output was redirected to a variable, it does not make sense to
- // keep this. So, before closing, strip the trailing '\r\n' from the variable
- // if it exists.
- //
- NewBuffer = NULL;
- NewSize = 0;
- TotalSize = 0;
-
- Status = IsVolatileEnv (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &Volatile);
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer);
- if (Status == EFI_BUFFER_TOO_SMALL) {
- TotalSize = NewSize + sizeof (CHAR16);
- NewBuffer = AllocateZeroPool (TotalSize);
- if (NewBuffer == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer);
- }
-
- if (!EFI_ERROR (Status) && (NewBuffer != NULL)) {
- if (TotalSize / sizeof (CHAR16) >= 3) {
- if ((((CHAR16 *)NewBuffer)[TotalSize / sizeof (CHAR16) - 2] == CHAR_LINEFEED) &&
- (((CHAR16 *)NewBuffer)[TotalSize / sizeof (CHAR16) - 3] == CHAR_CARRIAGE_RETURN)
- )
- {
- ((CHAR16 *)NewBuffer)[TotalSize / sizeof (CHAR16) - 3] = CHAR_NULL;
- //
- // If the NewBuffer end with \r\n\0, We will replace '\r' by '\0' and then update TotalSize.
- //
- TotalSize -= sizeof (CHAR16) * 2;
- }
-
- if (Volatile) {
- Status = SHELL_SET_ENVIRONMENT_VARIABLE_V (
- ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
- TotalSize - sizeof (CHAR16),
- NewBuffer
- );
-
- if (!EFI_ERROR (Status)) {
- Status = ShellAddEnvVarToList (
- ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
- NewBuffer,
- TotalSize,
- EFI_VARIABLE_BOOTSERVICE_ACCESS
- );
- }
- } else {
- Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV (
- ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
- TotalSize - sizeof (CHAR16),
- NewBuffer
- );
-
- if (!EFI_ERROR (Status)) {
- Status = ShellAddEnvVarToList (
- ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
- NewBuffer,
- TotalSize,
- EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS
- );
- }
- }
- }
- }
-
- SHELL_FREE_NON_NULL (NewBuffer);
- FreePool ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This);
- return (Status);
-}
-
-/**
- File style interface for Environment Variable (Delete).
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
-
- @retval The return value from FileInterfaceEnvClose().
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceEnvDelete (
- IN EFI_FILE_PROTOCOL *This
- )
-{
- SHELL_DELETE_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name);
- return (FileInterfaceEnvClose (This));
-}
-
-/**
- File style interface for Environment Variable (Read).
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
- @param[in, out] BufferSize Size in bytes of Buffer.
- @param[out] Buffer The pointer to the buffer to fill.
-
- @retval EFI_SUCCESS The data was read.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceEnvRead (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- OUT VOID *Buffer
- )
-{
- EFI_STATUS Status;
-
- *BufferSize = *BufferSize / sizeof (CHAR16) * sizeof (CHAR16);
- if (*BufferSize != 0) {
- //
- // Make sure the first unicode character is \xFEFF
- //
- *(CHAR16 *)Buffer = gUnicodeFileTag;
- Buffer = (CHAR16 *)Buffer + 1;
- *BufferSize -= sizeof (gUnicodeFileTag);
- }
-
- Status = SHELL_GET_ENVIRONMENT_VARIABLE (
- ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
- BufferSize,
- Buffer
- );
- if (!EFI_ERROR (Status) || (Status == EFI_BUFFER_TOO_SMALL)) {
- //
- // BufferSize is valid and needs update when Status is Success or BufferTooSmall.
- //
- *BufferSize += sizeof (gUnicodeFileTag);
- }
-
- return Status;
-}
-
-/**
- File style interface for Volatile Environment Variable (Write).
- This function also caches the environment variable into gShellEnvVarList.
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
- @param[in, out] BufferSize Size in bytes of Buffer.
- @param[in] Buffer The pointer to the buffer to write.
-
- @retval EFI_SUCCESS The data was successfully write to variable.
- @retval SHELL_OUT_OF_RESOURCES A memory allocation failed.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceEnvVolWrite (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- IN VOID *Buffer
- )
-{
- VOID *NewBuffer;
- UINTN NewSize;
- EFI_STATUS Status;
- UINTN TotalSize;
-
- NewBuffer = NULL;
- NewSize = 0;
- TotalSize = 0;
-
- Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer);
- if (Status == EFI_BUFFER_TOO_SMALL) {
- TotalSize = NewSize + *BufferSize + sizeof (CHAR16);
- } else if (Status == EFI_NOT_FOUND) {
- TotalSize = *BufferSize + sizeof (CHAR16);
- } else {
- return Status;
- }
-
- NewBuffer = AllocateZeroPool (TotalSize);
- if (NewBuffer == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- if (Status == EFI_BUFFER_TOO_SMALL) {
- Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer);
- }
-
- if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
- FreePool (NewBuffer);
- return Status;
- }
-
- CopyMem ((UINT8 *)NewBuffer + NewSize, Buffer, *BufferSize);
- Status = ShellAddEnvVarToList (
- ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
- NewBuffer,
- TotalSize,
- EFI_VARIABLE_BOOTSERVICE_ACCESS
- );
- if (EFI_ERROR (Status)) {
- FreePool (NewBuffer);
- return Status;
- }
-
- Status = SHELL_SET_ENVIRONMENT_VARIABLE_V (
- ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
- TotalSize - sizeof (CHAR16),
- NewBuffer
- );
- if (EFI_ERROR (Status)) {
- ShellRemvoeEnvVarFromList (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name);
- }
-
- FreePool (NewBuffer);
- return Status;
-}
-
-/**
- File style interface for Non Volatile Environment Variable (Write).
- This function also caches the environment variable into gShellEnvVarList.
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
- @param[in, out] BufferSize Size in bytes of Buffer.
- @param[in] Buffer The pointer to the buffer to write.
-
- @retval EFI_SUCCESS The data was successfully write to variable.
- @retval SHELL_OUT_OF_RESOURCES A memory allocation failed.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceEnvNonVolWrite (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- IN VOID *Buffer
- )
-{
- VOID *NewBuffer;
- UINTN NewSize;
- EFI_STATUS Status;
- UINTN TotalSize;
-
- NewBuffer = NULL;
- NewSize = 0;
- TotalSize = 0;
-
- Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer);
- if (Status == EFI_BUFFER_TOO_SMALL) {
- TotalSize = NewSize + *BufferSize + sizeof (CHAR16);
- } else if (Status == EFI_NOT_FOUND) {
- TotalSize = *BufferSize + sizeof (CHAR16);
- } else {
- return Status;
- }
-
- NewBuffer = AllocateZeroPool (TotalSize);
- if (NewBuffer == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- if (Status == EFI_BUFFER_TOO_SMALL) {
- Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer);
- }
-
- if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
- FreePool (NewBuffer);
- return Status;
- }
-
- CopyMem ((UINT8 *)NewBuffer + NewSize, Buffer, *BufferSize);
- Status = ShellAddEnvVarToList (
- ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
- NewBuffer,
- TotalSize,
- EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS
- );
- if (EFI_ERROR (Status)) {
- FreePool (NewBuffer);
- return Status;
- }
-
- Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV (
- ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
- TotalSize - sizeof (CHAR16),
- NewBuffer
- );
- if (EFI_ERROR (Status)) {
- ShellRemvoeEnvVarFromList (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name);
- }
-
- FreePool (NewBuffer);
- return Status;
-}
-
-/**
- Creates a EFI_FILE_PROTOCOL (almost) object for using to access
- environment variables through file operations.
-
- @param EnvName The name of the Environment Variable to be operated on.
-
- @retval NULL Memory could not be allocated.
- @return other a pointer to an EFI_FILE_PROTOCOL structure
-**/
-EFI_FILE_PROTOCOL *
-CreateFileInterfaceEnv (
- IN CONST CHAR16 *EnvName
- )
-{
- EFI_STATUS Status;
- EFI_FILE_PROTOCOL_ENVIRONMENT *EnvFileInterface;
- UINTN EnvNameSize;
- BOOLEAN Volatile;
-
- if (EnvName == NULL) {
- return (NULL);
- }
-
- Status = IsVolatileEnv (EnvName, &Volatile);
- if (EFI_ERROR (Status)) {
- return NULL;
- }
-
- //
- // Get some memory
- //
- EnvNameSize = StrSize (EnvName);
- EnvFileInterface = AllocateZeroPool (sizeof (EFI_FILE_PROTOCOL_ENVIRONMENT)+EnvNameSize);
- if (EnvFileInterface == NULL) {
- return (NULL);
- }
-
- //
- // Assign the generic members
- //
- EnvFileInterface->Revision = EFI_FILE_REVISION;
- EnvFileInterface->Open = FileInterfaceOpenNotFound;
- EnvFileInterface->Close = FileInterfaceEnvClose;
- EnvFileInterface->GetPosition = FileInterfaceNopGetPosition;
- EnvFileInterface->SetPosition = FileInterfaceNopSetPosition;
- EnvFileInterface->GetInfo = FileInterfaceNopGetInfo;
- EnvFileInterface->SetInfo = FileInterfaceNopSetInfo;
- EnvFileInterface->Flush = FileInterfaceNopGeneric;
- EnvFileInterface->Delete = FileInterfaceEnvDelete;
- EnvFileInterface->Read = FileInterfaceEnvRead;
-
- CopyMem (EnvFileInterface->Name, EnvName, EnvNameSize);
-
- //
- // Assign the different members for Volatile and Non-Volatile variables
- //
- if (Volatile) {
- EnvFileInterface->Write = FileInterfaceEnvVolWrite;
- } else {
- EnvFileInterface->Write = FileInterfaceEnvNonVolWrite;
- }
-
- return ((EFI_FILE_PROTOCOL *)EnvFileInterface);
-}
-
-/**
- Move the cursor position one character backward.
-
- @param[in] LineLength Length of a line. Get it by calling QueryMode
- @param[in, out] Column Current column of the cursor position
- @param[in, out] Row Current row of the cursor position
-**/
-VOID
-MoveCursorBackward (
- IN UINTN LineLength,
- IN OUT UINTN *Column,
- IN OUT UINTN *Row
- )
-{
- //
- // If current column is 0, move to the last column of the previous line,
- // otherwise, just decrement column.
- //
- if (*Column == 0) {
- *Column = LineLength - 1;
- if (*Row > 0) {
- (*Row)--;
- }
-
- return;
- }
-
- (*Column)--;
-}
-
-/**
- Move the cursor position one character forward.
-
- @param[in] LineLength Length of a line.
- @param[in] TotalRow Total row of a screen
- @param[in, out] Column Current column of the cursor position
- @param[in, out] Row Current row of the cursor position
-**/
-VOID
-MoveCursorForward (
- IN UINTN LineLength,
- IN UINTN TotalRow,
- IN OUT UINTN *Column,
- IN OUT UINTN *Row
- )
-{
- //
- // Increment Column.
- // If this puts column past the end of the line, move to first column
- // of the next row.
- //
- (*Column)++;
- if (*Column >= LineLength) {
- (*Column) = 0;
- if ((*Row) < TotalRow - 1) {
- (*Row)++;
- }
- }
-}
-
-/**
- Prints out each previously typed command in the command list history log.
-
- When each screen is full it will pause for a key before continuing.
-
- @param[in] TotalCols How many columns are on the screen
- @param[in] TotalRows How many rows are on the screen
- @param[in] StartColumn which column to start at
-**/
-VOID
-PrintCommandHistory (
- IN CONST UINTN TotalCols,
- IN CONST UINTN TotalRows,
- IN CONST UINTN StartColumn
- )
-{
- BUFFER_LIST *Node;
- UINTN Index;
- UINTN LineNumber;
- UINTN LineCount;
-
- ShellPrintEx (-1, -1, L"\n");
- Index = 0;
- LineNumber = 0;
- //
- // go through history list...
- //
- for ( Node = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link)
- ; !IsNull (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link)
- ; Node = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link)
- )
- {
- Index++;
- LineCount = ((StrLen (Node->Buffer) + StartColumn + 1) / TotalCols) + 1;
-
- if (LineNumber + LineCount >= TotalRows) {
- ShellPromptForResponseHii (
- ShellPromptResponseTypeEnterContinue,
- STRING_TOKEN (STR_SHELL_ENTER_TO_CONT),
- ShellInfoObject.HiiHandle,
- NULL
- );
- LineNumber = 0;
- }
-
- ShellPrintEx (-1, -1, L"%2d. %s\n", Index, Node->Buffer);
- LineNumber += LineCount;
- }
-}
-
-//
-// This is identical to EFI_FILE_PROTOCOL except for the additional members
-// for the buffer, size, and position.
-//
-
-typedef struct {
- UINT64 Revision;
- EFI_FILE_OPEN Open;
- EFI_FILE_CLOSE Close;
- EFI_FILE_DELETE Delete;
- EFI_FILE_READ Read;
- EFI_FILE_WRITE Write;
- EFI_FILE_GET_POSITION GetPosition;
- EFI_FILE_SET_POSITION SetPosition;
- EFI_FILE_GET_INFO GetInfo;
- EFI_FILE_SET_INFO SetInfo;
- EFI_FILE_FLUSH Flush;
- VOID *Buffer;
- UINT64 Position;
- UINT64 BufferSize;
- BOOLEAN Unicode;
- UINT64 FileSize;
-} EFI_FILE_PROTOCOL_MEM;
-
-/**
- File style interface for Mem (SetPosition).
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
- @param[out] Position The position to set.
-
- @retval EFI_SUCCESS The position was successfully changed.
- @retval EFI_INVALID_PARAMETER The Position was invalid.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceMemSetPosition (
- IN EFI_FILE_PROTOCOL *This,
- OUT UINT64 Position
- )
-{
- if (Position <= ((EFI_FILE_PROTOCOL_MEM *)This)->FileSize) {
- ((EFI_FILE_PROTOCOL_MEM *)This)->Position = Position;
- return (EFI_SUCCESS);
- } else {
- return (EFI_INVALID_PARAMETER);
- }
-}
-
-/**
- File style interface for Mem (GetPosition).
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
- @param[out] Position The pointer to the position.
-
- @retval EFI_SUCCESS The position was retrieved.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceMemGetPosition (
- IN EFI_FILE_PROTOCOL *This,
- OUT UINT64 *Position
- )
-{
- *Position = ((EFI_FILE_PROTOCOL_MEM *)This)->Position;
- return (EFI_SUCCESS);
-}
-
-/**
- File style interface for Mem (GetInfo).
-
- @param This Protocol instance pointer.
- @param InformationType Type of information to return in Buffer.
- @param BufferSize On input size of buffer, on output amount of data in buffer.
- @param Buffer The buffer to return data.
-
- @retval EFI_SUCCESS Data was returned.
- @retval EFI_UNSUPPORT InformationType is not supported.
- @retval EFI_NO_MEDIA The device has no media.
- @retval EFI_DEVICE_ERROR The device reported an error.
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
- @retval EFI_WRITE_PROTECTED The device is write protected.
- @retval EFI_ACCESS_DENIED The file was open for read only.
- @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in BufferSize.
-
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceMemGetInfo (
- IN EFI_FILE_PROTOCOL *This,
- IN EFI_GUID *InformationType,
- IN OUT UINTN *BufferSize,
- OUT VOID *Buffer
- )
-{
- EFI_FILE_INFO *FileInfo;
-
- if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
- if (*BufferSize < sizeof (EFI_FILE_INFO)) {
- *BufferSize = sizeof (EFI_FILE_INFO);
- return EFI_BUFFER_TOO_SMALL;
- }
-
- if (Buffer == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- FileInfo = (EFI_FILE_INFO *)Buffer;
- FileInfo->Size = sizeof (*FileInfo);
- ZeroMem (FileInfo, sizeof (*FileInfo));
- FileInfo->FileSize = ((EFI_FILE_PROTOCOL_MEM *)This)->FileSize;
- FileInfo->PhysicalSize = FileInfo->FileSize;
- return EFI_SUCCESS;
- }
-
- return EFI_UNSUPPORTED;
-}
-
-/**
- File style interface for Mem (Write).
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
- @param[in, out] BufferSize Size in bytes of Buffer.
- @param[in] Buffer The pointer to the buffer to write.
-
- @retval EFI_OUT_OF_RESOURCES The operation failed due to lack of resources.
- @retval EFI_SUCCESS The data was written.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceMemWrite (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- IN VOID *Buffer
- )
-{
- CHAR8 *AsciiBuffer;
- EFI_FILE_PROTOCOL_MEM *MemFile;
-
- MemFile = (EFI_FILE_PROTOCOL_MEM *)This;
- if (MemFile->Unicode) {
- //
- // Unicode
- //
- if ((UINTN)(MemFile->Position + (*BufferSize)) > (UINTN)(MemFile->BufferSize)) {
- MemFile->Buffer = ReallocatePool ((UINTN)(MemFile->BufferSize), (UINTN)(MemFile->BufferSize) + (*BufferSize) + MEM_WRITE_REALLOC_OVERHEAD, MemFile->Buffer);
- if (MemFile->Buffer == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- MemFile->BufferSize += (*BufferSize) + MEM_WRITE_REALLOC_OVERHEAD;
- }
-
- CopyMem (((UINT8 *)MemFile->Buffer) + MemFile->Position, Buffer, *BufferSize);
- MemFile->Position += (*BufferSize);
- MemFile->FileSize = MemFile->Position;
- return (EFI_SUCCESS);
- } else {
- //
- // Ascii
- //
- AsciiBuffer = AllocateZeroPool (*BufferSize);
- if (AsciiBuffer == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- AsciiSPrint (AsciiBuffer, *BufferSize, "%S", Buffer);
- if ((UINTN)(MemFile->Position + AsciiStrSize (AsciiBuffer)) > (UINTN)(MemFile->BufferSize)) {
- MemFile->Buffer = ReallocatePool ((UINTN)(MemFile->BufferSize), (UINTN)(MemFile->BufferSize) + AsciiStrSize (AsciiBuffer) + MEM_WRITE_REALLOC_OVERHEAD, MemFile->Buffer);
- if (MemFile->Buffer == NULL) {
- FreePool (AsciiBuffer);
- return EFI_OUT_OF_RESOURCES;
- }
-
- MemFile->BufferSize += AsciiStrSize (AsciiBuffer) + MEM_WRITE_REALLOC_OVERHEAD;
- }
-
- CopyMem (((UINT8 *)MemFile->Buffer) + MemFile->Position, AsciiBuffer, AsciiStrSize (AsciiBuffer));
- MemFile->Position += (*BufferSize / sizeof (CHAR16));
- MemFile->FileSize = MemFile->Position;
- FreePool (AsciiBuffer);
- return (EFI_SUCCESS);
- }
-}
-
-/**
- File style interface for Mem (Read).
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
- @param[in, out] BufferSize Size in bytes of Buffer.
- @param[in] Buffer The pointer to the buffer to fill.
-
- @retval EFI_SUCCESS The data was read.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceMemRead (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- IN VOID *Buffer
- )
-{
- EFI_FILE_PROTOCOL_MEM *MemFile;
-
- MemFile = (EFI_FILE_PROTOCOL_MEM *)This;
- if (*BufferSize > (UINTN)((MemFile->FileSize) - (UINTN)(MemFile->Position))) {
- (*BufferSize) = (UINTN)((MemFile->FileSize) - (UINTN)(MemFile->Position));
- }
-
- CopyMem (Buffer, ((UINT8 *)MemFile->Buffer) + MemFile->Position, (*BufferSize));
- MemFile->Position = MemFile->Position + (*BufferSize);
- return (EFI_SUCCESS);
-}
-
-/**
- File style interface for Mem (Close).
-
- Frees all memory associated with this object.
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
-
- @retval EFI_SUCCESS The 'file' was closed.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceMemClose (
- IN EFI_FILE_PROTOCOL *This
- )
-{
- SHELL_FREE_NON_NULL (((EFI_FILE_PROTOCOL_MEM *)This)->Buffer);
- SHELL_FREE_NON_NULL (This);
- return (EFI_SUCCESS);
-}
-
-/**
- Creates a EFI_FILE_PROTOCOL (almost) object for using to access
- a file entirely in memory through file operations.
-
- @param[in] Unicode Boolean value with TRUE for Unicode and FALSE for Ascii.
-
- @retval NULL Memory could not be allocated.
- @return other A pointer to an EFI_FILE_PROTOCOL structure.
-**/
-EFI_FILE_PROTOCOL *
-CreateFileInterfaceMem (
- IN CONST BOOLEAN Unicode
- )
-{
- EFI_FILE_PROTOCOL_MEM *FileInterface;
-
- //
- // Get some memory
- //
- FileInterface = AllocateZeroPool (sizeof (EFI_FILE_PROTOCOL_MEM));
- if (FileInterface == NULL) {
- return (NULL);
- }
-
- //
- // Assign the generic members
- //
- FileInterface->Revision = EFI_FILE_REVISION;
- FileInterface->Open = FileInterfaceOpenNotFound;
- FileInterface->Close = FileInterfaceMemClose;
- FileInterface->GetPosition = FileInterfaceMemGetPosition;
- FileInterface->SetPosition = FileInterfaceMemSetPosition;
- FileInterface->GetInfo = FileInterfaceMemGetInfo;
- FileInterface->SetInfo = FileInterfaceNopSetInfo;
- FileInterface->Flush = FileInterfaceNopGeneric;
- FileInterface->Delete = FileInterfaceNopGeneric;
- FileInterface->Read = FileInterfaceMemRead;
- FileInterface->Write = FileInterfaceMemWrite;
- FileInterface->Unicode = Unicode;
-
- ASSERT (FileInterface->Buffer == NULL);
- ASSERT (FileInterface->BufferSize == 0);
- ASSERT (FileInterface->Position == 0);
-
- if (Unicode) {
- FileInterface->Buffer = AllocateZeroPool (sizeof (gUnicodeFileTag));
- if (FileInterface->Buffer == NULL) {
- FreePool (FileInterface);
- return NULL;
- }
-
- *((CHAR16 *)(FileInterface->Buffer)) = EFI_UNICODE_BYTE_ORDER_MARK;
- FileInterface->BufferSize = 2;
- FileInterface->Position = 2;
- }
-
- return ((EFI_FILE_PROTOCOL *)FileInterface);
-}
-
-typedef struct {
- UINT64 Revision;
- EFI_FILE_OPEN Open;
- EFI_FILE_CLOSE Close;
- EFI_FILE_DELETE Delete;
- EFI_FILE_READ Read;
- EFI_FILE_WRITE Write;
- EFI_FILE_GET_POSITION GetPosition;
- EFI_FILE_SET_POSITION SetPosition;
- EFI_FILE_GET_INFO GetInfo;
- EFI_FILE_SET_INFO SetInfo;
- EFI_FILE_FLUSH Flush;
- BOOLEAN Unicode;
- EFI_FILE_PROTOCOL *Orig;
-} EFI_FILE_PROTOCOL_FILE;
-
-/**
- Set a files current position
-
- @param This Protocol instance pointer.
- @param Position Byte position from the start of the file.
-
- @retval EFI_SUCCESS Data was written.
- @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open.
-
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceFileSetPosition (
- IN EFI_FILE_PROTOCOL *This,
- IN UINT64 Position
- )
-{
- return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->SetPosition (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, Position);
-}
-
-/**
- Get a file's current position
-
- @param This Protocol instance pointer.
- @param Position Byte position from the start of the file.
-
- @retval EFI_SUCCESS Data was written.
- @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open..
-
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceFileGetPosition (
- IN EFI_FILE_PROTOCOL *This,
- OUT UINT64 *Position
- )
-{
- return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->GetPosition (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, Position);
-}
-
-/**
- Get information about a file.
-
- @param This Protocol instance pointer.
- @param InformationType Type of information to return in Buffer.
- @param BufferSize On input size of buffer, on output amount of data in buffer.
- @param Buffer The buffer to return data.
-
- @retval EFI_SUCCESS Data was returned.
- @retval EFI_UNSUPPORT InformationType is not supported.
- @retval EFI_NO_MEDIA The device has no media.
- @retval EFI_DEVICE_ERROR The device reported an error.
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
- @retval EFI_WRITE_PROTECTED The device is write protected.
- @retval EFI_ACCESS_DENIED The file was open for read only.
- @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in BufferSize.
-
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceFileGetInfo (
- IN EFI_FILE_PROTOCOL *This,
- IN EFI_GUID *InformationType,
- IN OUT UINTN *BufferSize,
- OUT VOID *Buffer
- )
-{
- return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->GetInfo (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, InformationType, BufferSize, Buffer);
-}
-
-/**
- Set information about a file
-
- @param This Protocol instance pointer.
- @param InformationType Type of information in Buffer.
- @param BufferSize Size of buffer.
- @param Buffer The data to write.
-
- @retval EFI_SUCCESS Data was returned.
- @retval EFI_UNSUPPORT InformationType is not supported.
- @retval EFI_NO_MEDIA The device has no media.
- @retval EFI_DEVICE_ERROR The device reported an error.
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
- @retval EFI_WRITE_PROTECTED The device is write protected.
- @retval EFI_ACCESS_DENIED The file was open for read only.
-
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceFileSetInfo (
- IN EFI_FILE_PROTOCOL *This,
- IN EFI_GUID *InformationType,
- IN UINTN BufferSize,
- IN VOID *Buffer
- )
-{
- return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->SetInfo (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, InformationType, BufferSize, Buffer);
-}
-
-/**
- Flush data back for the file handle.
-
- @param This Protocol instance pointer.
-
- @retval EFI_SUCCESS Data was written.
- @retval EFI_UNSUPPORT Writes to Open directory are not supported.
- @retval EFI_NO_MEDIA The device has no media.
- @retval EFI_DEVICE_ERROR The device reported an error.
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
- @retval EFI_WRITE_PROTECTED The device is write protected.
- @retval EFI_ACCESS_DENIED The file was open for read only.
- @retval EFI_VOLUME_FULL The volume is full.
-
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceFileFlush (
- IN EFI_FILE_PROTOCOL *This
- )
-{
- return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Flush (((EFI_FILE_PROTOCOL_FILE *)This)->Orig);
-}
-
-/**
- Read data from the file.
-
- @param This Protocol instance pointer.
- @param BufferSize On input size of buffer, on output amount of data in buffer.
- @param Buffer The buffer in which data is read.
-
- @retval EFI_SUCCESS Data was read.
- @retval EFI_NO_MEDIA The device has no media.
- @retval EFI_DEVICE_ERROR The device reported an error.
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
- @retval EFI_BUFFER_TO_SMALL BufferSize is too small. BufferSize contains required size.
-
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceFileRead (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- OUT VOID *Buffer
- )
-{
- EFI_STATUS Status;
- UINT64 Position;
- CHAR8 *AsciiStrBuffer;
- CHAR16 *UscStrBuffer;
- UINTN Size;
-
- if (((EFI_FILE_PROTOCOL_FILE *)This)->Unicode) {
- //
- // Unicode
- // There might be different file tag for the Unicode file. We cannot unconditionally insert the \xFEFF.
- // So we choose to leave the file content as is.
- //
- return (((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Read (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, BufferSize, Buffer));
- } else {
- //
- // Ascii
- //
- *BufferSize = *BufferSize / sizeof (CHAR16) * sizeof (CHAR16);
- if (*BufferSize == 0) {
- return EFI_SUCCESS;
- }
-
- Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->GetPosition (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, &Position);
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- if (Position == 0) {
- //
- // First two bytes in Buffer is for the Unicode file tag.
- //
- *(CHAR16 *)Buffer = gUnicodeFileTag;
- Buffer = (CHAR16 *)Buffer + 1;
- Size = *BufferSize / sizeof (CHAR16) - 1;
- } else {
- Size = *BufferSize / sizeof (CHAR16);
- }
-
- AsciiStrBuffer = AllocateZeroPool (Size + 1);
- if (AsciiStrBuffer == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- UscStrBuffer = AllocateZeroPool ((Size + 1) * sizeof (CHAR16));
- if (UscStrBuffer == NULL) {
- SHELL_FREE_NON_NULL (AsciiStrBuffer);
- return EFI_OUT_OF_RESOURCES;
- }
-
- Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Read (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, &Size, AsciiStrBuffer);
- if (!EFI_ERROR (Status)) {
- AsciiStrToUnicodeStrS (AsciiStrBuffer, UscStrBuffer, Size + 1);
- *BufferSize = Size * sizeof (CHAR16);
- CopyMem (Buffer, UscStrBuffer, *BufferSize);
- }
-
- SHELL_FREE_NON_NULL (AsciiStrBuffer);
- SHELL_FREE_NON_NULL (UscStrBuffer);
- return Status;
- }
-}
-
-/**
- Opens a new file relative to the source file's location.
-
- @param[in] This The protocol instance pointer.
- @param[out] NewHandle Returns File Handle for FileName.
- @param[in] FileName Null terminated string. "\", ".", and ".." are supported.
- @param[in] OpenMode Open mode for file.
- @param[in] Attributes Only used for EFI_FILE_MODE_CREATE.
-
- @retval EFI_SUCCESS The device was opened.
- @retval EFI_NOT_FOUND The specified file could not be found on the device.
- @retval EFI_NO_MEDIA The device has no media.
- @retval EFI_MEDIA_CHANGED The media has changed.
- @retval EFI_DEVICE_ERROR The device reported an error.
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
- @retval EFI_ACCESS_DENIED The service denied access to the file.
- @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources.
- @retval EFI_VOLUME_FULL The volume is full.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceFileOpen (
- IN EFI_FILE_PROTOCOL *This,
- OUT EFI_FILE_PROTOCOL **NewHandle,
- IN CHAR16 *FileName,
- IN UINT64 OpenMode,
- IN UINT64 Attributes
- )
-{
- return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Open (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, NewHandle, FileName, OpenMode, Attributes);
-}
-
-/**
- Close and delete the file handle.
-
- @param This Protocol instance pointer.
-
- @retval EFI_SUCCESS The device was opened.
- @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted.
-
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceFileDelete (
- IN EFI_FILE_PROTOCOL *This
- )
-{
- EFI_STATUS Status;
-
- Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Delete (((EFI_FILE_PROTOCOL_FILE *)This)->Orig);
- FreePool (This);
- return (Status);
-}
-
-/**
- File style interface for File (Close).
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
-
- @retval EFI_SUCCESS The file was closed.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceFileClose (
- IN EFI_FILE_PROTOCOL *This
- )
-{
- EFI_STATUS Status;
-
- Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Close (((EFI_FILE_PROTOCOL_FILE *)This)->Orig);
- FreePool (This);
- return (Status);
-}
-
-/**
- File style interface for File (Write).
-
- If the file was opened with ASCII mode the data will be processed through
- AsciiSPrint before writing.
-
- @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
- @param[in, out] BufferSize Size in bytes of Buffer.
- @param[in] Buffer The pointer to the buffer to write.
-
- @retval EFI_SUCCESS The data was written.
-**/
-EFI_STATUS
-EFIAPI
-FileInterfaceFileWrite (
- IN EFI_FILE_PROTOCOL *This,
- IN OUT UINTN *BufferSize,
- IN VOID *Buffer
- )
-{
- CHAR8 *AsciiBuffer;
- UINTN Size;
- EFI_STATUS Status;
-
- if (((EFI_FILE_PROTOCOL_FILE *)This)->Unicode) {
- //
- // Unicode
- //
- return (((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Write (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, BufferSize, Buffer));
- } else {
- //
- // Ascii
- //
- AsciiBuffer = AllocateZeroPool (*BufferSize);
- AsciiSPrint (AsciiBuffer, *BufferSize, "%S", Buffer);
- Size = AsciiStrSize (AsciiBuffer) - 1; // (we dont need the null terminator)
- Status = (((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Write (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, &Size, AsciiBuffer));
- FreePool (AsciiBuffer);
- return (Status);
- }
-}
-
-/**
- Create a file interface with unicode information.
-
- This will create a new EFI_FILE_PROTOCOL identical to the Templace
- except that the new one has Unicode and Ascii knowledge.
-
- @param[in] Template A pointer to the EFI_FILE_PROTOCOL object.
- @param[in] Unicode TRUE for UCS-2, FALSE for ASCII.
-
- @return a new EFI_FILE_PROTOCOL object to be used instead of the template.
-**/
-EFI_FILE_PROTOCOL *
-CreateFileInterfaceFile (
- IN CONST EFI_FILE_PROTOCOL *Template,
- IN CONST BOOLEAN Unicode
- )
-{
- EFI_FILE_PROTOCOL_FILE *NewOne;
-
- NewOne = AllocateZeroPool (sizeof (EFI_FILE_PROTOCOL_FILE));
- if (NewOne == NULL) {
- return (NULL);
- }
-
- CopyMem (NewOne, Template, sizeof (EFI_FILE_PROTOCOL_FILE));
- NewOne->Orig = (EFI_FILE_PROTOCOL *)Template;
- NewOne->Unicode = Unicode;
- NewOne->Open = FileInterfaceFileOpen;
- NewOne->Close = FileInterfaceFileClose;
- NewOne->Delete = FileInterfaceFileDelete;
- NewOne->Read = FileInterfaceFileRead;
- NewOne->Write = FileInterfaceFileWrite;
- NewOne->GetPosition = FileInterfaceFileGetPosition;
- NewOne->SetPosition = FileInterfaceFileSetPosition;
- NewOne->GetInfo = FileInterfaceFileGetInfo;
- NewOne->SetInfo = FileInterfaceFileSetInfo;
- NewOne->Flush = FileInterfaceFileFlush;
-
- return ((EFI_FILE_PROTOCOL *)NewOne);
-}
+/** @file + EFI_FILE_PROTOCOL wrappers for other items (Like Environment Variables, + StdIn, StdOut, StdErr, etc...). + + Copyright 2016 Dell Inc. + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Shell.h" +#include "FileHandleInternal.h" + +#define MEM_WRITE_REALLOC_OVERHEAD 1024 + +/** + File style interface for console (Open). + + @param[in] This Ignored. + @param[out] NewHandle Ignored. + @param[in] FileName Ignored. + @param[in] OpenMode Ignored. + @param[in] Attributes Ignored. + + @retval EFI_NOT_FOUND +**/ +EFI_STATUS +EFIAPI +FileInterfaceOpenNotFound ( + IN EFI_FILE_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +{ + return (EFI_NOT_FOUND); +} + +/** + File style interface for console (Close, Delete, & Flush) + + @param[in] This Ignored. + + @retval EFI_SUCCESS +**/ +EFI_STATUS +EFIAPI +FileInterfaceNopGeneric ( + IN EFI_FILE_PROTOCOL *This + ) +{ + return (EFI_SUCCESS); +} + +/** + File style interface for console (GetPosition). + + @param[in] This Ignored. + @param[out] Position Ignored. + + @retval EFI_UNSUPPORTED +**/ +EFI_STATUS +EFIAPI +FileInterfaceNopGetPosition ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ) +{ + return (EFI_UNSUPPORTED); +} + +/** + File style interface for console (SetPosition). + + @param[in] This Ignored. + @param[in] Position Ignored. + + @retval EFI_UNSUPPORTED +**/ +EFI_STATUS +EFIAPI +FileInterfaceNopSetPosition ( + IN EFI_FILE_PROTOCOL *This, + IN UINT64 Position + ) +{ + return (EFI_UNSUPPORTED); +} + +/** + File style interface for console (GetInfo). + + @param[in] This Ignored. + @param[in] InformationType Ignored. + @param[in, out] BufferSize Ignored. + @param[out] Buffer Ignored. + + @retval EFI_UNSUPPORTED +**/ +EFI_STATUS +EFIAPI +FileInterfaceNopGetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + return (EFI_UNSUPPORTED); +} + +/** + File style interface for console (SetInfo). + + @param[in] This Ignored. + @param[in] InformationType Ignored. + @param[in] BufferSize Ignored. + @param[in] Buffer Ignored. + + @retval EFI_UNSUPPORTED +**/ +EFI_STATUS +EFIAPI +FileInterfaceNopSetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + return (EFI_UNSUPPORTED); +} + +/** + File style interface for StdOut (Write). + + Writes data to the screen. + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + @param[in, out] BufferSize Size in bytes of Buffer. + @param[in] Buffer The pointer to the buffer to write. + + @retval EFI_UNSUPPORTED No output console is supported. + @return A return value from gST->ConOut->OutputString. +**/ +EFI_STATUS +EFIAPI +FileInterfaceStdOutWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) { + return (EFI_UNSUPPORTED); + } + + if (*((CHAR16 *)Buffer) == gUnicodeFileTag) { + return (gST->ConOut->OutputString (gST->ConOut, (CHAR16 *)Buffer + 1)); + } + + return (gST->ConOut->OutputString (gST->ConOut, Buffer)); +} + +/** + File style interface for StdIn (Write). + + @param[in] This Ignored. + @param[in, out] BufferSize Ignored. + @param[in] Buffer Ignored. + + @retval EFI_UNSUPPORTED +**/ +EFI_STATUS +EFIAPI +FileInterfaceStdInWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + return (EFI_UNSUPPORTED); +} + +/** + File style interface for console StdErr (Write). + + Writes error to the error output. + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + @param[in, out] BufferSize Size in bytes of Buffer. + @param[in] Buffer The pointer to the buffer to write. + + @return A return value from gST->StdErr->OutputString. +**/ +EFI_STATUS +EFIAPI +FileInterfaceStdErrWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + return (gST->StdErr->OutputString (gST->StdErr, Buffer)); +} + +/** + File style interface for console StdOut (Read). + + @param[in] This Ignored. + @param[in, out] BufferSize Ignored. + @param[out] Buffer Ignored. + + @retval EFI_UNSUPPORTED +**/ +EFI_STATUS +EFIAPI +FileInterfaceStdOutRead ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + return (EFI_UNSUPPORTED); +} + +/** + File style interface for console StdErr (Read). + + @param[in] This Ignored. + @param[in, out] BufferSize Ignored. + @param[out] Buffer Ignored. + + @retval EFI_UNSUPPORTED Always. +**/ +EFI_STATUS +EFIAPI +FileInterfaceStdErrRead ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + return (EFI_UNSUPPORTED); +} + +/** + File style interface for NUL file (Read). + + @param[in] This Ignored. + @param[in, out] BufferSize Poiner to 0 upon return. + @param[out] Buffer Ignored. + + @retval EFI_SUCCESS Always. +**/ +EFI_STATUS +EFIAPI +FileInterfaceNulRead ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + *BufferSize = 0; + return (EFI_SUCCESS); +} + +/** + File style interface for NUL file (Write). + + @param[in] This Ignored. + @param[in, out] BufferSize Ignored. + @param[in] Buffer Ignored. + + @retval EFI_SUCCESS +**/ +EFI_STATUS +EFIAPI +FileInterfaceNulWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + return (EFI_SUCCESS); +} + +/** + Create the TAB completion list. + + @param[in] InputString The command line to expand. + @param[in] StringLen Length of the command line. + @param[in] BufferSize Buffer size. + @param[in, out] TabCompletionList Return the TAB completion list. + @param[in, out] TabUpdatePos Return the TAB update position. +**/ +EFI_STATUS +CreateTabCompletionList ( + IN CONST CHAR16 *InputString, + IN CONST UINTN StringLen, + IN CONST UINTN BufferSize, + IN OUT EFI_SHELL_FILE_INFO **TabCompletionList, + IN OUT UINTN *TabUpdatePos + ) +{ + BOOLEAN InQuotation; + UINTN TabPos; + UINTN Index; + CONST CHAR16 *Cwd; + EFI_STATUS Status; + CHAR16 *TabStr; + EFI_SHELL_FILE_INFO *FileList; + EFI_SHELL_FILE_INFO *FileInfo; + EFI_SHELL_FILE_INFO *TempFileInfo; + + // + // Allocate buffers + // + TabStr = AllocateZeroPool (BufferSize); + if (TabStr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // handle auto complete of file and directory names... + // E.g.: cd fs0:\EFI\Bo<TAB> + // ^ ^ + // TabPos TabUpdatePos + // + TabPos = 0; + *TabUpdatePos = 0; + FileList = NULL; + InQuotation = FALSE; + for (Index = 0; Index < StringLen; Index++) { + switch (InputString[Index]) { + case L'\"': + InQuotation = (BOOLEAN)(!InQuotation); + break; + + case L' ': + if (!InQuotation) { + TabPos = Index + 1; + *TabUpdatePos = TabPos; + } + + break; + + case L':': + // + // handle the case "fs0:<TAB>" + // Update the TabUpdatePos as well. + // + case L'\\': + *TabUpdatePos = Index + 1; + break; + + default: + break; + } + } + + if (StrStr (InputString + TabPos, L":") == NULL) { + // + // If file path doesn't contain ":", ... + // + Cwd = ShellInfoObject.NewEfiShellProtocol->GetCurDir (NULL); + if (Cwd != NULL) { + if (InputString[TabPos] != L'\\') { + // + // and it doesn't begin with "\\", it's a path relative to current directory. + // TabStr = "<cwd>\\" + // + StrnCpyS (TabStr, BufferSize / sizeof (CHAR16), Cwd, (BufferSize) / sizeof (CHAR16) - 1); + StrCatS (TabStr, (BufferSize) / sizeof (CHAR16), L"\\"); + } else { + // + // and it begins with "\\", it's a path pointing to root directory of current map. + // TabStr = "fsx:" + // + Index = StrStr (Cwd, L":") - Cwd + 1; + StrnCpyS (TabStr, BufferSize / sizeof (CHAR16), Cwd, Index); + } + } + } + + StrnCatS (TabStr, (BufferSize) / sizeof (CHAR16), InputString + TabPos, StringLen - TabPos); + StrnCatS (TabStr, (BufferSize) / sizeof (CHAR16), L"*", (BufferSize) / sizeof (CHAR16) - 1 - StrLen (TabStr)); + Status = ShellInfoObject.NewEfiShellProtocol->FindFiles (TabStr, &FileList); + + // + // Filter out the non-directory for "CD" command + // Filter "." and ".." for all + // + if (!EFI_ERROR (Status) && (FileList != NULL)) { + // + // Skip the spaces in the beginning + // + while (*InputString == L' ') { + InputString++; + } + + for (FileInfo = (EFI_SHELL_FILE_INFO *)GetFirstNode (&FileList->Link); !IsNull (&FileList->Link, &FileInfo->Link); ) { + if (((StrCmp (FileInfo->FileName, L".") == 0) || (StrCmp (FileInfo->FileName, L"..") == 0)) || + ((((InputString[0] == L'c') || (InputString[0] == L'C')) && ((InputString[1] == L'd') || (InputString[1] == L'D'))) && + (ShellIsDirectory (FileInfo->FullName) != EFI_SUCCESS))) + { + TempFileInfo = FileInfo; + FileInfo = (EFI_SHELL_FILE_INFO *)RemoveEntryList (&FileInfo->Link); + InternalFreeShellFileInfoNode (TempFileInfo); + } else { + FileInfo = (EFI_SHELL_FILE_INFO *)GetNextNode (&FileList->Link, &FileInfo->Link); + } + } + } + + if ((FileList != NULL) && !IsListEmpty (&FileList->Link)) { + Status = EFI_SUCCESS; + } else { + ShellInfoObject.NewEfiShellProtocol->FreeFileList (&FileList); + Status = EFI_NOT_FOUND; + } + + FreePool (TabStr); + + *TabCompletionList = FileList; + return Status; +} + +/** + File style interface for console (Read). + + This will return a single line of input from the console. + + @param This A pointer to the EFI_FILE_PROTOCOL instance that is the + file handle to read data from. Not used. + @param BufferSize On input, the size of the Buffer. On output, the amount + of data returned in Buffer. In both cases, the size is + measured in bytes. + @param Buffer The buffer into which the data is read. + + + @retval EFI_SUCCESS The data was read. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted file. + @retval EFI_DEVICE_ERROR On entry, the current file position is beyond the end of the file. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory + entry. BufferSize has been updated with the size + needed to complete the request. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +EFIAPI +FileInterfaceStdInRead ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + CHAR16 *CurrentString; + BOOLEAN Done; + UINTN TabUpdatePos; // Start index of the string updated by TAB stroke + UINTN Column; // Column of current cursor + UINTN Row; // Row of current cursor + UINTN StartColumn; // Column at the beginning of the line + UINTN Update; // Line index for update + UINTN Delete; // Num of chars to delete from console after update + UINTN StringLen; // Total length of the line + UINTN StringCurPos; // Line index corresponding to the cursor + UINTN MaxStr; // Maximum possible line length + UINTN TotalColumn; // Num of columns in the console + UINTN TotalRow; // Num of rows in the console + UINTN SkipLength; + UINTN OutputLength; // Length of the update string + UINTN TailRow; // Row of end of line + UINTN TailColumn; // Column of end of line + EFI_INPUT_KEY Key; + + BUFFER_LIST *LinePos; + BUFFER_LIST *NewPos; + BOOLEAN InScrolling; + EFI_STATUS Status; + BOOLEAN InTabScrolling; // Whether in TAB-completion state + EFI_SHELL_FILE_INFO *TabCompleteList; + EFI_SHELL_FILE_INFO *TabCurrent; + UINTN EventIndex; + CHAR16 *TabOutputStr; + + // + // If buffer is not large enough to hold a CHAR16, return minimum buffer size + // + if (*BufferSize < sizeof (CHAR16) * 2) { + *BufferSize = sizeof (CHAR16) * 2; + return (EFI_BUFFER_TOO_SMALL); + } + + Done = FALSE; + CurrentString = Buffer; + StringLen = 0; + StringCurPos = 0; + OutputLength = 0; + Update = 0; + Delete = 0; + LinePos = NewPos = (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory); + InScrolling = FALSE; + InTabScrolling = FALSE; + Status = EFI_SUCCESS; + TabOutputStr = NULL; + TabUpdatePos = 0; + TabCompleteList = NULL; + TabCurrent = NULL; + + // + // Get the screen setting and the current cursor location + // + Column = StartColumn = gST->ConOut->Mode->CursorColumn; + Row = gST->ConOut->Mode->CursorRow; + gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &TotalColumn, &TotalRow); + + // + // Limit the line length to the buffer size or the minimum size of the + // screen. (The smaller takes effect) + // + MaxStr = TotalColumn * (TotalRow - 1) - StartColumn; + if (MaxStr > *BufferSize / sizeof (CHAR16)) { + MaxStr = *BufferSize / sizeof (CHAR16); + } + + ZeroMem (CurrentString, MaxStr * sizeof (CHAR16)); + do { + // + // Read a key + // + gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex); + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_READY) { + continue; + } + + ZeroMem (CurrentString, MaxStr * sizeof (CHAR16)); + StringLen = 0; + break; + } + + // + // Press PageUp or PageDown to scroll the history screen up or down. + // Press any other key to quit scrolling. + // + if ((Key.UnicodeChar == 0) && ((Key.ScanCode == SCAN_PAGE_UP) || (Key.ScanCode == SCAN_PAGE_DOWN))) { + if (Key.ScanCode == SCAN_PAGE_UP) { + ConsoleLoggerDisplayHistory (FALSE, 0, ShellInfoObject.ConsoleInfo); + } else if (Key.ScanCode == SCAN_PAGE_DOWN) { + ConsoleLoggerDisplayHistory (TRUE, 0, ShellInfoObject.ConsoleInfo); + } + + InScrolling = TRUE; + } else { + if (InScrolling) { + ConsoleLoggerStopHistory (ShellInfoObject.ConsoleInfo); + InScrolling = FALSE; + } + } + + // + // If we are quitting TAB scrolling... + // + if (InTabScrolling && (Key.UnicodeChar != CHAR_TAB)) { + if (TabCompleteList != NULL) { + ShellInfoObject.NewEfiShellProtocol->FreeFileList (&TabCompleteList); + DEBUG_CODE ( + TabCompleteList = NULL; + ); + } + + InTabScrolling = FALSE; + } + + switch (Key.UnicodeChar) { + case CHAR_CARRIAGE_RETURN: + // + // All done, print a newline at the end of the string + // + TailRow = Row + (StringLen - StringCurPos + Column) / TotalColumn; + TailColumn = (StringLen - StringCurPos + Column) % TotalColumn; + ShellPrintEx ((INT32)TailColumn, (INT32)TailRow, L"%N\n"); + Done = TRUE; + break; + + case CHAR_BACKSPACE: + if (StringCurPos != 0) { + // + // If not move back beyond string beginning, move all characters behind + // the current position one character forward + // + StringCurPos--; + Update = StringCurPos; + Delete = 1; + CopyMem (CurrentString + StringCurPos, CurrentString + StringCurPos + 1, sizeof (CHAR16) * (StringLen - StringCurPos)); + + // + // Adjust the current column and row + // + MoveCursorBackward (TotalColumn, &Column, &Row); + } + + break; + + case CHAR_TAB: + if (!InTabScrolling) { + TabCurrent = NULL; + // + // Initialize a tab complete operation. + // + Status = CreateTabCompletionList (CurrentString, StringLen, *BufferSize, &TabCompleteList, &TabUpdatePos); + if (!EFI_ERROR (Status)) { + InTabScrolling = TRUE; + } + + // + // We do not set up the replacement. + // The next section will do that. + // + } + + if (InTabScrolling) { + // + // We are in a tab complete operation. + // set up the next replacement. + // + ASSERT (TabCompleteList != NULL); + if (TabCurrent == NULL) { + TabCurrent = (EFI_SHELL_FILE_INFO *)GetFirstNode (&TabCompleteList->Link); + } else { + TabCurrent = (EFI_SHELL_FILE_INFO *)GetNextNode (&TabCompleteList->Link, &TabCurrent->Link); + } + + // + // Skip over the empty list beginning node + // + if (IsNull (&TabCompleteList->Link, &TabCurrent->Link)) { + TabCurrent = (EFI_SHELL_FILE_INFO *)GetNextNode (&TabCompleteList->Link, &TabCurrent->Link); + } + } + + break; + + default: + if (Key.UnicodeChar >= ' ') { + // + // If we are at the buffer's end, drop the key + // + if ((StringLen == MaxStr - 1) && (ShellInfoObject.ViewingSettings.InsertMode || (StringCurPos == StringLen))) { + break; + } + + // + // If in insert mode, make space by moving each other character 1 + // space higher in the array + // + if (ShellInfoObject.ViewingSettings.InsertMode) { + CopyMem (CurrentString + StringCurPos + 1, CurrentString + StringCurPos, (StringLen - StringCurPos)*sizeof (CurrentString[0])); + } + + CurrentString[StringCurPos] = Key.UnicodeChar; + Update = StringCurPos; + + StringCurPos += 1; + OutputLength = 1; + } + + break; + + case 0: + switch (Key.ScanCode) { + case SCAN_DELETE: + // + // Move characters behind current position one character forward + // + if (StringLen != 0) { + Update = StringCurPos; + Delete = 1; + CopyMem (CurrentString + StringCurPos, CurrentString + StringCurPos + 1, sizeof (CHAR16) * (StringLen - StringCurPos)); + } + + break; + + case SCAN_UP: + // + // Prepare to print the previous command + // + NewPos = (BUFFER_LIST *)GetPreviousNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link); + if (IsNull (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link)) { + NewPos = (BUFFER_LIST *)GetPreviousNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link); + } + + break; + + case SCAN_DOWN: + // + // Prepare to print the next command + // + NewPos = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link); + if (NewPos == (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory)) { + NewPos = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link); + } + + break; + + case SCAN_LEFT: + // + // Adjust current cursor position + // + if (StringCurPos != 0) { + --StringCurPos; + MoveCursorBackward (TotalColumn, &Column, &Row); + } + + break; + + case SCAN_RIGHT: + // + // Adjust current cursor position + // + if (StringCurPos < StringLen) { + ++StringCurPos; + MoveCursorForward (TotalColumn, TotalRow, &Column, &Row); + } + + break; + + case SCAN_HOME: + // + // Move current cursor position to the beginning of the command line + // + Row -= (StringCurPos + StartColumn) / TotalColumn; + Column = StartColumn; + StringCurPos = 0; + break; + + case SCAN_END: + // + // Move current cursor position to the end of the command line + // + TailRow = Row + (StringLen - StringCurPos + Column) / TotalColumn; + TailColumn = (StringLen - StringCurPos + Column) % TotalColumn; + Row = TailRow; + Column = TailColumn; + StringCurPos = StringLen; + break; + + case SCAN_ESC: + // + // Prepare to clear the current command line + // + CurrentString[0] = 0; + Update = 0; + Delete = StringLen; + Row -= (StringCurPos + StartColumn) / TotalColumn; + Column = StartColumn; + OutputLength = 0; + break; + + case SCAN_INSERT: + // + // Toggle the SEnvInsertMode flag + // + ShellInfoObject.ViewingSettings.InsertMode = (BOOLEAN) !ShellInfoObject.ViewingSettings.InsertMode; + break; + + case SCAN_F7: + // + // Print command history + // + PrintCommandHistory (TotalColumn, TotalRow, 4); + *CurrentString = CHAR_NULL; + Done = TRUE; + break; + } + } + + if (Done) { + break; + } + + // + // If we are in auto-complete mode, we are preparing to print + // the next file or directory name + // + if (InTabScrolling) { + TabOutputStr = AllocateZeroPool (*BufferSize); + if (TabOutputStr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } + } + + if (InTabScrolling && (TabOutputStr != NULL)) { + // + // Adjust the column and row to the start of TAB-completion string. + // + Column = (StartColumn + TabUpdatePos) % TotalColumn; + Row -= (StartColumn + StringCurPos) / TotalColumn - (StartColumn + TabUpdatePos) / TotalColumn; + OutputLength = StrLen (TabCurrent->FileName); + // + // if the output string contains blank space, quotation marks L'\"' + // should be added to the output. + // + if (StrStr (TabCurrent->FileName, L" ") != NULL) { + TabOutputStr[0] = L'\"'; + CopyMem (TabOutputStr + 1, TabCurrent->FileName, OutputLength * sizeof (CHAR16)); + TabOutputStr[OutputLength + 1] = L'\"'; + TabOutputStr[OutputLength + 2] = CHAR_NULL; + } else { + CopyMem (TabOutputStr, TabCurrent->FileName, OutputLength * sizeof (CHAR16)); + TabOutputStr[OutputLength] = CHAR_NULL; + } + + OutputLength = StrLen (TabOutputStr) < MaxStr - 1 ? StrLen (TabOutputStr) : MaxStr - 1; + CopyMem (CurrentString + TabUpdatePos, TabOutputStr, OutputLength * sizeof (CHAR16)); + CurrentString[TabUpdatePos + OutputLength] = CHAR_NULL; + StringCurPos = TabUpdatePos + OutputLength; + Update = TabUpdatePos; + if (StringLen > TabUpdatePos + OutputLength) { + Delete = StringLen - TabUpdatePos - OutputLength; + } + + FreePool (TabOutputStr); + } + + // + // If we have a new position, we are preparing to print a previous or + // next command. + // + if (NewPos != (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory)) { + Column = StartColumn; + Row -= (StringCurPos + StartColumn) / TotalColumn; + + LinePos = NewPos; + NewPos = (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory); + + OutputLength = StrLen (LinePos->Buffer) < MaxStr - 1 ? StrLen (LinePos->Buffer) : MaxStr - 1; + CopyMem (CurrentString, LinePos->Buffer, OutputLength * sizeof (CHAR16)); + CurrentString[OutputLength] = CHAR_NULL; + + StringCurPos = OutputLength; + + // + // Draw new input string + // + Update = 0; + if (StringLen > OutputLength) { + // + // If old string was longer, blank its tail + // + Delete = StringLen - OutputLength; + } + } + + // + // If we need to update the output do so now + // + if (Update != (UINTN)-1) { + ShellPrintEx ((INT32)Column, (INT32)Row, L"%s%.*s", CurrentString + Update, Delete, L""); + StringLen = StrLen (CurrentString); + + if (Delete != 0) { + SetMem (CurrentString + StringLen, Delete * sizeof (CHAR16), CHAR_NULL); + } + + if (StringCurPos > StringLen) { + StringCurPos = StringLen; + } + + Update = (UINTN)-1; + + // + // After using print to reflect newly updates, if we're not using + // BACKSPACE and DELETE, we need to move the cursor position forward, + // so adjust row and column here. + // + if ((Key.UnicodeChar != CHAR_BACKSPACE) && !((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_DELETE))) { + // + // Calculate row and column of the tail of current string + // + TailRow = Row + (StringLen - StringCurPos + Column + OutputLength) / TotalColumn; + TailColumn = (StringLen - StringCurPos + Column + OutputLength) % TotalColumn; + + // + // If the tail of string reaches screen end, screen rolls up, so if + // Row does not equal TailRow, Row should be decremented + // + // (if we are recalling commands using UPPER and DOWN key, and if the + // old command is too long to fit the screen, TailColumn must be 79. + // + if ((TailColumn == 0) && (TailRow >= TotalRow) && (Row != TailRow)) { + Row--; + } + + // + // Calculate the cursor position after current operation. If cursor + // reaches line end, update both row and column, otherwise, only + // column will be changed. + // + if (Column + OutputLength >= TotalColumn) { + SkipLength = OutputLength - (TotalColumn - Column); + + Row += SkipLength / TotalColumn + 1; + if (Row > TotalRow - 1) { + Row = TotalRow - 1; + } + + Column = SkipLength % TotalColumn; + } else { + Column += OutputLength; + } + } + + Delete = 0; + } + + // + // Set the cursor position for this key + // + gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row); + } while (!Done); + + if ((CurrentString != NULL) && (StrLen (CurrentString) > 0)) { + // + // add the line to the history buffer + // + AddLineToCommandHistory (CurrentString); + } + + // + // Return the data to the caller + // + *BufferSize = StringLen * sizeof (CHAR16); + + // + // if this was used it should be deallocated by now... + // prevent memory leaks... + // + if (TabCompleteList != NULL) { + ShellInfoObject.NewEfiShellProtocol->FreeFileList (&TabCompleteList); + } + + ASSERT (TabCompleteList == NULL); + + return Status; +} + +// +// FILE style interfaces for StdIn/StdOut/StdErr +// +EFI_FILE_PROTOCOL FileInterfaceStdIn = { + EFI_FILE_REVISION, + FileInterfaceOpenNotFound, + FileInterfaceNopGeneric, + FileInterfaceNopGeneric, + FileInterfaceStdInRead, + FileInterfaceStdInWrite, + FileInterfaceNopGetPosition, + FileInterfaceNopSetPosition, + FileInterfaceNopGetInfo, + FileInterfaceNopSetInfo, + FileInterfaceNopGeneric +}; + +EFI_FILE_PROTOCOL FileInterfaceStdOut = { + EFI_FILE_REVISION, + FileInterfaceOpenNotFound, + FileInterfaceNopGeneric, + FileInterfaceNopGeneric, + FileInterfaceStdOutRead, + FileInterfaceStdOutWrite, + FileInterfaceNopGetPosition, + FileInterfaceNopSetPosition, + FileInterfaceNopGetInfo, + FileInterfaceNopSetInfo, + FileInterfaceNopGeneric +}; + +EFI_FILE_PROTOCOL FileInterfaceStdErr = { + EFI_FILE_REVISION, + FileInterfaceOpenNotFound, + FileInterfaceNopGeneric, + FileInterfaceNopGeneric, + FileInterfaceStdErrRead, + FileInterfaceStdErrWrite, + FileInterfaceNopGetPosition, + FileInterfaceNopSetPosition, + FileInterfaceNopGetInfo, + FileInterfaceNopSetInfo, + FileInterfaceNopGeneric +}; + +EFI_FILE_PROTOCOL FileInterfaceNulFile = { + EFI_FILE_REVISION, + FileInterfaceOpenNotFound, + FileInterfaceNopGeneric, + FileInterfaceNopGeneric, + FileInterfaceNulRead, + FileInterfaceNulWrite, + FileInterfaceNopGetPosition, + FileInterfaceNopSetPosition, + FileInterfaceNopGetInfo, + FileInterfaceNopSetInfo, + FileInterfaceNopGeneric +}; + +// +// This is identical to EFI_FILE_PROTOCOL except for the additional member +// for the name. +// + +typedef struct { + UINT64 Revision; + EFI_FILE_OPEN Open; + EFI_FILE_CLOSE Close; + EFI_FILE_DELETE Delete; + EFI_FILE_READ Read; + EFI_FILE_WRITE Write; + EFI_FILE_GET_POSITION GetPosition; + EFI_FILE_SET_POSITION SetPosition; + EFI_FILE_GET_INFO GetInfo; + EFI_FILE_SET_INFO SetInfo; + EFI_FILE_FLUSH Flush; + CHAR16 Name[1]; +} EFI_FILE_PROTOCOL_ENVIRONMENT; +// ANSI compliance helper to get size of the struct. +#define SIZE_OF_EFI_FILE_PROTOCOL_ENVIRONMENT EFI_FIELD_OFFSET (EFI_FILE_PROTOCOL_ENVIRONMENT, Name) + +/** + File style interface for Environment Variable (Close). + + Frees the memory for this object. + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + + @retval EFI_SUCCESS +**/ +EFI_STATUS +EFIAPI +FileInterfaceEnvClose ( + IN EFI_FILE_PROTOCOL *This + ) +{ + VOID *NewBuffer; + UINTN NewSize; + EFI_STATUS Status; + BOOLEAN Volatile; + UINTN TotalSize; + + // + // Most if not all UEFI commands will have an '\r\n' at the end of any output. + // Since the output was redirected to a variable, it does not make sense to + // keep this. So, before closing, strip the trailing '\r\n' from the variable + // if it exists. + // + NewBuffer = NULL; + NewSize = 0; + TotalSize = 0; + + Status = IsVolatileEnv (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &Volatile); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer); + if (Status == EFI_BUFFER_TOO_SMALL) { + TotalSize = NewSize + sizeof (CHAR16); + NewBuffer = AllocateZeroPool (TotalSize); + if (NewBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer); + } + + if (!EFI_ERROR (Status) && (NewBuffer != NULL)) { + if (TotalSize / sizeof (CHAR16) >= 3) { + if ((((CHAR16 *)NewBuffer)[TotalSize / sizeof (CHAR16) - 2] == CHAR_LINEFEED) && + (((CHAR16 *)NewBuffer)[TotalSize / sizeof (CHAR16) - 3] == CHAR_CARRIAGE_RETURN) + ) + { + ((CHAR16 *)NewBuffer)[TotalSize / sizeof (CHAR16) - 3] = CHAR_NULL; + // + // If the NewBuffer end with \r\n\0, We will replace '\r' by '\0' and then update TotalSize. + // + TotalSize -= sizeof (CHAR16) * 2; + } + + if (Volatile) { + Status = SHELL_SET_ENVIRONMENT_VARIABLE_V ( + ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, + TotalSize - sizeof (CHAR16), + NewBuffer + ); + + if (!EFI_ERROR (Status)) { + Status = ShellAddEnvVarToList ( + ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, + NewBuffer, + TotalSize, + EFI_VARIABLE_BOOTSERVICE_ACCESS + ); + } + } else { + Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV ( + ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, + TotalSize - sizeof (CHAR16), + NewBuffer + ); + + if (!EFI_ERROR (Status)) { + Status = ShellAddEnvVarToList ( + ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, + NewBuffer, + TotalSize, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS + ); + } + } + } + } + + SHELL_FREE_NON_NULL (NewBuffer); + FreePool ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This); + return (Status); +} + +/** + File style interface for Environment Variable (Delete). + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + + @retval The return value from FileInterfaceEnvClose(). +**/ +EFI_STATUS +EFIAPI +FileInterfaceEnvDelete ( + IN EFI_FILE_PROTOCOL *This + ) +{ + SHELL_DELETE_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name); + return (FileInterfaceEnvClose (This)); +} + +/** + File style interface for Environment Variable (Read). + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + @param[in, out] BufferSize Size in bytes of Buffer. + @param[out] Buffer The pointer to the buffer to fill. + + @retval EFI_SUCCESS The data was read. +**/ +EFI_STATUS +EFIAPI +FileInterfaceEnvRead ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + + *BufferSize = *BufferSize / sizeof (CHAR16) * sizeof (CHAR16); + if (*BufferSize != 0) { + // + // Make sure the first unicode character is \xFEFF + // + *(CHAR16 *)Buffer = gUnicodeFileTag; + Buffer = (CHAR16 *)Buffer + 1; + *BufferSize -= sizeof (gUnicodeFileTag); + } + + Status = SHELL_GET_ENVIRONMENT_VARIABLE ( + ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, + BufferSize, + Buffer + ); + if (!EFI_ERROR (Status) || (Status == EFI_BUFFER_TOO_SMALL)) { + // + // BufferSize is valid and needs update when Status is Success or BufferTooSmall. + // + *BufferSize += sizeof (gUnicodeFileTag); + } + + return Status; +} + +/** + File style interface for Volatile Environment Variable (Write). + This function also caches the environment variable into gShellEnvVarList. + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + @param[in, out] BufferSize Size in bytes of Buffer. + @param[in] Buffer The pointer to the buffer to write. + + @retval EFI_SUCCESS The data was successfully write to variable. + @retval SHELL_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +EFIAPI +FileInterfaceEnvVolWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + VOID *NewBuffer; + UINTN NewSize; + EFI_STATUS Status; + UINTN TotalSize; + + NewBuffer = NULL; + NewSize = 0; + TotalSize = 0; + + Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer); + if (Status == EFI_BUFFER_TOO_SMALL) { + TotalSize = NewSize + *BufferSize + sizeof (CHAR16); + } else if (Status == EFI_NOT_FOUND) { + TotalSize = *BufferSize + sizeof (CHAR16); + } else { + return Status; + } + + NewBuffer = AllocateZeroPool (TotalSize); + if (NewBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (Status == EFI_BUFFER_TOO_SMALL) { + Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer); + } + + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + FreePool (NewBuffer); + return Status; + } + + CopyMem ((UINT8 *)NewBuffer + NewSize, Buffer, *BufferSize); + Status = ShellAddEnvVarToList ( + ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, + NewBuffer, + TotalSize, + EFI_VARIABLE_BOOTSERVICE_ACCESS + ); + if (EFI_ERROR (Status)) { + FreePool (NewBuffer); + return Status; + } + + Status = SHELL_SET_ENVIRONMENT_VARIABLE_V ( + ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, + TotalSize - sizeof (CHAR16), + NewBuffer + ); + if (EFI_ERROR (Status)) { + ShellRemvoeEnvVarFromList (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name); + } + + FreePool (NewBuffer); + return Status; +} + +/** + File style interface for Non Volatile Environment Variable (Write). + This function also caches the environment variable into gShellEnvVarList. + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + @param[in, out] BufferSize Size in bytes of Buffer. + @param[in] Buffer The pointer to the buffer to write. + + @retval EFI_SUCCESS The data was successfully write to variable. + @retval SHELL_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +EFIAPI +FileInterfaceEnvNonVolWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + VOID *NewBuffer; + UINTN NewSize; + EFI_STATUS Status; + UINTN TotalSize; + + NewBuffer = NULL; + NewSize = 0; + TotalSize = 0; + + Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer); + if (Status == EFI_BUFFER_TOO_SMALL) { + TotalSize = NewSize + *BufferSize + sizeof (CHAR16); + } else if (Status == EFI_NOT_FOUND) { + TotalSize = *BufferSize + sizeof (CHAR16); + } else { + return Status; + } + + NewBuffer = AllocateZeroPool (TotalSize); + if (NewBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (Status == EFI_BUFFER_TOO_SMALL) { + Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer); + } + + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + FreePool (NewBuffer); + return Status; + } + + CopyMem ((UINT8 *)NewBuffer + NewSize, Buffer, *BufferSize); + Status = ShellAddEnvVarToList ( + ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, + NewBuffer, + TotalSize, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS + ); + if (EFI_ERROR (Status)) { + FreePool (NewBuffer); + return Status; + } + + Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV ( + ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, + TotalSize - sizeof (CHAR16), + NewBuffer + ); + if (EFI_ERROR (Status)) { + ShellRemvoeEnvVarFromList (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name); + } + + FreePool (NewBuffer); + return Status; +} + +/** + Creates a EFI_FILE_PROTOCOL (almost) object for using to access + environment variables through file operations. + + @param EnvName The name of the Environment Variable to be operated on. + + @retval NULL Memory could not be allocated. + @return other a pointer to an EFI_FILE_PROTOCOL structure +**/ +EFI_FILE_PROTOCOL * +CreateFileInterfaceEnv ( + IN CONST CHAR16 *EnvName + ) +{ + EFI_STATUS Status; + EFI_FILE_PROTOCOL_ENVIRONMENT *EnvFileInterface; + UINTN EnvNameSize; + BOOLEAN Volatile; + + if (EnvName == NULL) { + return (NULL); + } + + Status = IsVolatileEnv (EnvName, &Volatile); + if (EFI_ERROR (Status)) { + return NULL; + } + + // + // Get some memory + // + EnvNameSize = StrSize (EnvName); + EnvFileInterface = AllocateZeroPool (sizeof (EFI_FILE_PROTOCOL_ENVIRONMENT)+EnvNameSize); + if (EnvFileInterface == NULL) { + return (NULL); + } + + // + // Assign the generic members + // + EnvFileInterface->Revision = EFI_FILE_REVISION; + EnvFileInterface->Open = FileInterfaceOpenNotFound; + EnvFileInterface->Close = FileInterfaceEnvClose; + EnvFileInterface->GetPosition = FileInterfaceNopGetPosition; + EnvFileInterface->SetPosition = FileInterfaceNopSetPosition; + EnvFileInterface->GetInfo = FileInterfaceNopGetInfo; + EnvFileInterface->SetInfo = FileInterfaceNopSetInfo; + EnvFileInterface->Flush = FileInterfaceNopGeneric; + EnvFileInterface->Delete = FileInterfaceEnvDelete; + EnvFileInterface->Read = FileInterfaceEnvRead; + + CopyMem (EnvFileInterface->Name, EnvName, EnvNameSize); + + // + // Assign the different members for Volatile and Non-Volatile variables + // + if (Volatile) { + EnvFileInterface->Write = FileInterfaceEnvVolWrite; + } else { + EnvFileInterface->Write = FileInterfaceEnvNonVolWrite; + } + + return ((EFI_FILE_PROTOCOL *)EnvFileInterface); +} + +/** + Move the cursor position one character backward. + + @param[in] LineLength Length of a line. Get it by calling QueryMode + @param[in, out] Column Current column of the cursor position + @param[in, out] Row Current row of the cursor position +**/ +VOID +MoveCursorBackward ( + IN UINTN LineLength, + IN OUT UINTN *Column, + IN OUT UINTN *Row + ) +{ + // + // If current column is 0, move to the last column of the previous line, + // otherwise, just decrement column. + // + if (*Column == 0) { + *Column = LineLength - 1; + if (*Row > 0) { + (*Row)--; + } + + return; + } + + (*Column)--; +} + +/** + Move the cursor position one character forward. + + @param[in] LineLength Length of a line. + @param[in] TotalRow Total row of a screen + @param[in, out] Column Current column of the cursor position + @param[in, out] Row Current row of the cursor position +**/ +VOID +MoveCursorForward ( + IN UINTN LineLength, + IN UINTN TotalRow, + IN OUT UINTN *Column, + IN OUT UINTN *Row + ) +{ + // + // Increment Column. + // If this puts column past the end of the line, move to first column + // of the next row. + // + (*Column)++; + if (*Column >= LineLength) { + (*Column) = 0; + if ((*Row) < TotalRow - 1) { + (*Row)++; + } + } +} + +/** + Prints out each previously typed command in the command list history log. + + When each screen is full it will pause for a key before continuing. + + @param[in] TotalCols How many columns are on the screen + @param[in] TotalRows How many rows are on the screen + @param[in] StartColumn which column to start at +**/ +VOID +PrintCommandHistory ( + IN CONST UINTN TotalCols, + IN CONST UINTN TotalRows, + IN CONST UINTN StartColumn + ) +{ + BUFFER_LIST *Node; + UINTN Index; + UINTN LineNumber; + UINTN LineCount; + + ShellPrintEx (-1, -1, L"\n"); + Index = 0; + LineNumber = 0; + // + // go through history list... + // + for ( Node = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link) + ; !IsNull (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link) + ; Node = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link) + ) + { + Index++; + LineCount = ((StrLen (Node->Buffer) + StartColumn + 1) / TotalCols) + 1; + + if (LineNumber + LineCount >= TotalRows) { + ShellPromptForResponseHii ( + ShellPromptResponseTypeEnterContinue, + STRING_TOKEN (STR_SHELL_ENTER_TO_CONT), + ShellInfoObject.HiiHandle, + NULL + ); + LineNumber = 0; + } + + ShellPrintEx (-1, -1, L"%2d. %s\n", Index, Node->Buffer); + LineNumber += LineCount; + } +} + +// +// This is identical to EFI_FILE_PROTOCOL except for the additional members +// for the buffer, size, and position. +// + +typedef struct { + UINT64 Revision; + EFI_FILE_OPEN Open; + EFI_FILE_CLOSE Close; + EFI_FILE_DELETE Delete; + EFI_FILE_READ Read; + EFI_FILE_WRITE Write; + EFI_FILE_GET_POSITION GetPosition; + EFI_FILE_SET_POSITION SetPosition; + EFI_FILE_GET_INFO GetInfo; + EFI_FILE_SET_INFO SetInfo; + EFI_FILE_FLUSH Flush; + VOID *Buffer; + UINT64 Position; + UINT64 BufferSize; + BOOLEAN Unicode; + UINT64 FileSize; +} EFI_FILE_PROTOCOL_MEM; + +/** + File style interface for Mem (SetPosition). + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + @param[out] Position The position to set. + + @retval EFI_SUCCESS The position was successfully changed. + @retval EFI_INVALID_PARAMETER The Position was invalid. +**/ +EFI_STATUS +EFIAPI +FileInterfaceMemSetPosition ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 Position + ) +{ + if (Position <= ((EFI_FILE_PROTOCOL_MEM *)This)->FileSize) { + ((EFI_FILE_PROTOCOL_MEM *)This)->Position = Position; + return (EFI_SUCCESS); + } else { + return (EFI_INVALID_PARAMETER); + } +} + +/** + File style interface for Mem (GetPosition). + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + @param[out] Position The pointer to the position. + + @retval EFI_SUCCESS The position was retrieved. +**/ +EFI_STATUS +EFIAPI +FileInterfaceMemGetPosition ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ) +{ + *Position = ((EFI_FILE_PROTOCOL_MEM *)This)->Position; + return (EFI_SUCCESS); +} + +/** + File style interface for Mem (GetInfo). + + @param This Protocol instance pointer. + @param InformationType Type of information to return in Buffer. + @param BufferSize On input size of buffer, on output amount of data in buffer. + @param Buffer The buffer to return data. + + @retval EFI_SUCCESS Data was returned. + @retval EFI_UNSUPPORT InformationType is not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The device is write protected. + @retval EFI_ACCESS_DENIED The file was open for read only. + @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in BufferSize. + +**/ +EFI_STATUS +EFIAPI +FileInterfaceMemGetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_FILE_INFO *FileInfo; + + if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { + if (*BufferSize < sizeof (EFI_FILE_INFO)) { + *BufferSize = sizeof (EFI_FILE_INFO); + return EFI_BUFFER_TOO_SMALL; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + FileInfo = (EFI_FILE_INFO *)Buffer; + FileInfo->Size = sizeof (*FileInfo); + ZeroMem (FileInfo, sizeof (*FileInfo)); + FileInfo->FileSize = ((EFI_FILE_PROTOCOL_MEM *)This)->FileSize; + FileInfo->PhysicalSize = FileInfo->FileSize; + return EFI_SUCCESS; + } + + return EFI_UNSUPPORTED; +} + +/** + File style interface for Mem (Write). + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + @param[in, out] BufferSize Size in bytes of Buffer. + @param[in] Buffer The pointer to the buffer to write. + + @retval EFI_OUT_OF_RESOURCES The operation failed due to lack of resources. + @retval EFI_SUCCESS The data was written. +**/ +EFI_STATUS +EFIAPI +FileInterfaceMemWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + CHAR8 *AsciiBuffer; + EFI_FILE_PROTOCOL_MEM *MemFile; + + MemFile = (EFI_FILE_PROTOCOL_MEM *)This; + if (MemFile->Unicode) { + // + // Unicode + // + if ((UINTN)(MemFile->Position + (*BufferSize)) > (UINTN)(MemFile->BufferSize)) { + MemFile->Buffer = ReallocatePool ((UINTN)(MemFile->BufferSize), (UINTN)(MemFile->BufferSize) + (*BufferSize) + MEM_WRITE_REALLOC_OVERHEAD, MemFile->Buffer); + if (MemFile->Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + MemFile->BufferSize += (*BufferSize) + MEM_WRITE_REALLOC_OVERHEAD; + } + + CopyMem (((UINT8 *)MemFile->Buffer) + MemFile->Position, Buffer, *BufferSize); + MemFile->Position += (*BufferSize); + MemFile->FileSize = MemFile->Position; + return (EFI_SUCCESS); + } else { + // + // Ascii + // + AsciiBuffer = AllocateZeroPool (*BufferSize); + if (AsciiBuffer == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + AsciiSPrint (AsciiBuffer, *BufferSize, "%S", Buffer); + if ((UINTN)(MemFile->Position + AsciiStrSize (AsciiBuffer)) > (UINTN)(MemFile->BufferSize)) { + MemFile->Buffer = ReallocatePool ((UINTN)(MemFile->BufferSize), (UINTN)(MemFile->BufferSize) + AsciiStrSize (AsciiBuffer) + MEM_WRITE_REALLOC_OVERHEAD, MemFile->Buffer); + if (MemFile->Buffer == NULL) { + FreePool (AsciiBuffer); + return EFI_OUT_OF_RESOURCES; + } + + MemFile->BufferSize += AsciiStrSize (AsciiBuffer) + MEM_WRITE_REALLOC_OVERHEAD; + } + + CopyMem (((UINT8 *)MemFile->Buffer) + MemFile->Position, AsciiBuffer, AsciiStrSize (AsciiBuffer)); + MemFile->Position += (*BufferSize / sizeof (CHAR16)); + MemFile->FileSize = MemFile->Position; + FreePool (AsciiBuffer); + return (EFI_SUCCESS); + } +} + +/** + File style interface for Mem (Read). + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + @param[in, out] BufferSize Size in bytes of Buffer. + @param[in] Buffer The pointer to the buffer to fill. + + @retval EFI_SUCCESS The data was read. +**/ +EFI_STATUS +EFIAPI +FileInterfaceMemRead ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + EFI_FILE_PROTOCOL_MEM *MemFile; + + MemFile = (EFI_FILE_PROTOCOL_MEM *)This; + if (*BufferSize > (UINTN)((MemFile->FileSize) - (UINTN)(MemFile->Position))) { + (*BufferSize) = (UINTN)((MemFile->FileSize) - (UINTN)(MemFile->Position)); + } + + CopyMem (Buffer, ((UINT8 *)MemFile->Buffer) + MemFile->Position, (*BufferSize)); + MemFile->Position = MemFile->Position + (*BufferSize); + return (EFI_SUCCESS); +} + +/** + File style interface for Mem (Close). + + Frees all memory associated with this object. + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + + @retval EFI_SUCCESS The 'file' was closed. +**/ +EFI_STATUS +EFIAPI +FileInterfaceMemClose ( + IN EFI_FILE_PROTOCOL *This + ) +{ + SHELL_FREE_NON_NULL (((EFI_FILE_PROTOCOL_MEM *)This)->Buffer); + SHELL_FREE_NON_NULL (This); + return (EFI_SUCCESS); +} + +/** + Creates a EFI_FILE_PROTOCOL (almost) object for using to access + a file entirely in memory through file operations. + + @param[in] Unicode Boolean value with TRUE for Unicode and FALSE for Ascii. + + @retval NULL Memory could not be allocated. + @return other A pointer to an EFI_FILE_PROTOCOL structure. +**/ +EFI_FILE_PROTOCOL * +CreateFileInterfaceMem ( + IN CONST BOOLEAN Unicode + ) +{ + EFI_FILE_PROTOCOL_MEM *FileInterface; + + // + // Get some memory + // + FileInterface = AllocateZeroPool (sizeof (EFI_FILE_PROTOCOL_MEM)); + if (FileInterface == NULL) { + return (NULL); + } + + // + // Assign the generic members + // + FileInterface->Revision = EFI_FILE_REVISION; + FileInterface->Open = FileInterfaceOpenNotFound; + FileInterface->Close = FileInterfaceMemClose; + FileInterface->GetPosition = FileInterfaceMemGetPosition; + FileInterface->SetPosition = FileInterfaceMemSetPosition; + FileInterface->GetInfo = FileInterfaceMemGetInfo; + FileInterface->SetInfo = FileInterfaceNopSetInfo; + FileInterface->Flush = FileInterfaceNopGeneric; + FileInterface->Delete = FileInterfaceNopGeneric; + FileInterface->Read = FileInterfaceMemRead; + FileInterface->Write = FileInterfaceMemWrite; + FileInterface->Unicode = Unicode; + + ASSERT (FileInterface->Buffer == NULL); + ASSERT (FileInterface->BufferSize == 0); + ASSERT (FileInterface->Position == 0); + + if (Unicode) { + FileInterface->Buffer = AllocateZeroPool (sizeof (gUnicodeFileTag)); + if (FileInterface->Buffer == NULL) { + FreePool (FileInterface); + return NULL; + } + + *((CHAR16 *)(FileInterface->Buffer)) = EFI_UNICODE_BYTE_ORDER_MARK; + FileInterface->BufferSize = 2; + FileInterface->Position = 2; + } + + return ((EFI_FILE_PROTOCOL *)FileInterface); +} + +typedef struct { + UINT64 Revision; + EFI_FILE_OPEN Open; + EFI_FILE_CLOSE Close; + EFI_FILE_DELETE Delete; + EFI_FILE_READ Read; + EFI_FILE_WRITE Write; + EFI_FILE_GET_POSITION GetPosition; + EFI_FILE_SET_POSITION SetPosition; + EFI_FILE_GET_INFO GetInfo; + EFI_FILE_SET_INFO SetInfo; + EFI_FILE_FLUSH Flush; + BOOLEAN Unicode; + EFI_FILE_PROTOCOL *Orig; +} EFI_FILE_PROTOCOL_FILE; + +/** + Set a files current position + + @param This Protocol instance pointer. + @param Position Byte position from the start of the file. + + @retval EFI_SUCCESS Data was written. + @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open. + +**/ +EFI_STATUS +EFIAPI +FileInterfaceFileSetPosition ( + IN EFI_FILE_PROTOCOL *This, + IN UINT64 Position + ) +{ + return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->SetPosition (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, Position); +} + +/** + Get a file's current position + + @param This Protocol instance pointer. + @param Position Byte position from the start of the file. + + @retval EFI_SUCCESS Data was written. + @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open.. + +**/ +EFI_STATUS +EFIAPI +FileInterfaceFileGetPosition ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ) +{ + return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->GetPosition (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, Position); +} + +/** + Get information about a file. + + @param This Protocol instance pointer. + @param InformationType Type of information to return in Buffer. + @param BufferSize On input size of buffer, on output amount of data in buffer. + @param Buffer The buffer to return data. + + @retval EFI_SUCCESS Data was returned. + @retval EFI_UNSUPPORT InformationType is not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The device is write protected. + @retval EFI_ACCESS_DENIED The file was open for read only. + @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in BufferSize. + +**/ +EFI_STATUS +EFIAPI +FileInterfaceFileGetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->GetInfo (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, InformationType, BufferSize, Buffer); +} + +/** + Set information about a file + + @param This Protocol instance pointer. + @param InformationType Type of information in Buffer. + @param BufferSize Size of buffer. + @param Buffer The data to write. + + @retval EFI_SUCCESS Data was returned. + @retval EFI_UNSUPPORT InformationType is not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The device is write protected. + @retval EFI_ACCESS_DENIED The file was open for read only. + +**/ +EFI_STATUS +EFIAPI +FileInterfaceFileSetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->SetInfo (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, InformationType, BufferSize, Buffer); +} + +/** + Flush data back for the file handle. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS Data was written. + @retval EFI_UNSUPPORT Writes to Open directory are not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The device is write protected. + @retval EFI_ACCESS_DENIED The file was open for read only. + @retval EFI_VOLUME_FULL The volume is full. + +**/ +EFI_STATUS +EFIAPI +FileInterfaceFileFlush ( + IN EFI_FILE_PROTOCOL *This + ) +{ + return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Flush (((EFI_FILE_PROTOCOL_FILE *)This)->Orig); +} + +/** + Read data from the file. + + @param This Protocol instance pointer. + @param BufferSize On input size of buffer, on output amount of data in buffer. + @param Buffer The buffer in which data is read. + + @retval EFI_SUCCESS Data was read. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TO_SMALL BufferSize is too small. BufferSize contains required size. + +**/ +EFI_STATUS +EFIAPI +FileInterfaceFileRead ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT64 Position; + CHAR8 *AsciiStrBuffer; + CHAR16 *UscStrBuffer; + UINTN Size; + + if (((EFI_FILE_PROTOCOL_FILE *)This)->Unicode) { + // + // Unicode + // There might be different file tag for the Unicode file. We cannot unconditionally insert the \xFEFF. + // So we choose to leave the file content as is. + // + return (((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Read (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, BufferSize, Buffer)); + } else { + // + // Ascii + // + *BufferSize = *BufferSize / sizeof (CHAR16) * sizeof (CHAR16); + if (*BufferSize == 0) { + return EFI_SUCCESS; + } + + Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->GetPosition (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, &Position); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Position == 0) { + // + // First two bytes in Buffer is for the Unicode file tag. + // + *(CHAR16 *)Buffer = gUnicodeFileTag; + Buffer = (CHAR16 *)Buffer + 1; + Size = *BufferSize / sizeof (CHAR16) - 1; + } else { + Size = *BufferSize / sizeof (CHAR16); + } + + AsciiStrBuffer = AllocateZeroPool (Size + 1); + if (AsciiStrBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UscStrBuffer = AllocateZeroPool ((Size + 1) * sizeof (CHAR16)); + if (UscStrBuffer == NULL) { + SHELL_FREE_NON_NULL (AsciiStrBuffer); + return EFI_OUT_OF_RESOURCES; + } + + Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Read (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, &Size, AsciiStrBuffer); + if (!EFI_ERROR (Status)) { + AsciiStrToUnicodeStrS (AsciiStrBuffer, UscStrBuffer, Size + 1); + *BufferSize = Size * sizeof (CHAR16); + CopyMem (Buffer, UscStrBuffer, *BufferSize); + } + + SHELL_FREE_NON_NULL (AsciiStrBuffer); + SHELL_FREE_NON_NULL (UscStrBuffer); + return Status; + } +} + +/** + Opens a new file relative to the source file's location. + + @param[in] This The protocol instance pointer. + @param[out] NewHandle Returns File Handle for FileName. + @param[in] FileName Null terminated string. "\", ".", and ".." are supported. + @param[in] OpenMode Open mode for file. + @param[in] Attributes Only used for EFI_FILE_MODE_CREATE. + + @retval EFI_SUCCESS The device was opened. + @retval EFI_NOT_FOUND The specified file could not be found on the device. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_MEDIA_CHANGED The media has changed. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources. + @retval EFI_VOLUME_FULL The volume is full. +**/ +EFI_STATUS +EFIAPI +FileInterfaceFileOpen ( + IN EFI_FILE_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +{ + return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Open (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, NewHandle, FileName, OpenMode, Attributes); +} + +/** + Close and delete the file handle. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The device was opened. + @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted. + +**/ +EFI_STATUS +EFIAPI +FileInterfaceFileDelete ( + IN EFI_FILE_PROTOCOL *This + ) +{ + EFI_STATUS Status; + + Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Delete (((EFI_FILE_PROTOCOL_FILE *)This)->Orig); + FreePool (This); + return (Status); +} + +/** + File style interface for File (Close). + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + + @retval EFI_SUCCESS The file was closed. +**/ +EFI_STATUS +EFIAPI +FileInterfaceFileClose ( + IN EFI_FILE_PROTOCOL *This + ) +{ + EFI_STATUS Status; + + Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Close (((EFI_FILE_PROTOCOL_FILE *)This)->Orig); + FreePool (This); + return (Status); +} + +/** + File style interface for File (Write). + + If the file was opened with ASCII mode the data will be processed through + AsciiSPrint before writing. + + @param[in] This The pointer to the EFI_FILE_PROTOCOL object. + @param[in, out] BufferSize Size in bytes of Buffer. + @param[in] Buffer The pointer to the buffer to write. + + @retval EFI_SUCCESS The data was written. +**/ +EFI_STATUS +EFIAPI +FileInterfaceFileWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + CHAR8 *AsciiBuffer; + UINTN Size; + EFI_STATUS Status; + + if (((EFI_FILE_PROTOCOL_FILE *)This)->Unicode) { + // + // Unicode + // + return (((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Write (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, BufferSize, Buffer)); + } else { + // + // Ascii + // + AsciiBuffer = AllocateZeroPool (*BufferSize); + AsciiSPrint (AsciiBuffer, *BufferSize, "%S", Buffer); + Size = AsciiStrSize (AsciiBuffer) - 1; // (we dont need the null terminator) + Status = (((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Write (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, &Size, AsciiBuffer)); + FreePool (AsciiBuffer); + return (Status); + } +} + +/** + Create a file interface with unicode information. + + This will create a new EFI_FILE_PROTOCOL identical to the Templace + except that the new one has Unicode and Ascii knowledge. + + @param[in] Template A pointer to the EFI_FILE_PROTOCOL object. + @param[in] Unicode TRUE for UCS-2, FALSE for ASCII. + + @return a new EFI_FILE_PROTOCOL object to be used instead of the template. +**/ +EFI_FILE_PROTOCOL * +CreateFileInterfaceFile ( + IN CONST EFI_FILE_PROTOCOL *Template, + IN CONST BOOLEAN Unicode + ) +{ + EFI_FILE_PROTOCOL_FILE *NewOne; + + NewOne = AllocateZeroPool (sizeof (EFI_FILE_PROTOCOL_FILE)); + if (NewOne == NULL) { + return (NULL); + } + + CopyMem (NewOne, Template, sizeof (EFI_FILE_PROTOCOL_FILE)); + NewOne->Orig = (EFI_FILE_PROTOCOL *)Template; + NewOne->Unicode = Unicode; + NewOne->Open = FileInterfaceFileOpen; + NewOne->Close = FileInterfaceFileClose; + NewOne->Delete = FileInterfaceFileDelete; + NewOne->Read = FileInterfaceFileRead; + NewOne->Write = FileInterfaceFileWrite; + NewOne->GetPosition = FileInterfaceFileGetPosition; + NewOne->SetPosition = FileInterfaceFileSetPosition; + NewOne->GetInfo = FileInterfaceFileGetInfo; + NewOne->SetInfo = FileInterfaceFileSetInfo; + NewOne->Flush = FileInterfaceFileFlush; + + return ((EFI_FILE_PROTOCOL *)NewOne); +} diff --git a/ShellPkg/Application/Shell/FileHandleWrappers.h b/ShellPkg/Application/Shell/FileHandleWrappers.h index 178b24bd5c..4a184a6567 100644 --- a/ShellPkg/Application/Shell/FileHandleWrappers.h +++ b/ShellPkg/Application/Shell/FileHandleWrappers.h @@ -1,86 +1,86 @@ -/** @file
- EFI_FILE_PROTOCOL wrappers for other items (Like Environment Variables, StdIn, StdOut, StdErr, etc...)
-
- Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#ifndef _SHELL_FILE_HANDLE_WRAPPERS_HEADER_
-#define _SHELL_FILE_HANDLE_WRAPPERS_HEADER_
-
-typedef struct {
- LIST_ENTRY Link;
- CHAR16 *Buffer;
-} SHELL_LINE_LIST;
-
-typedef struct {
- UINTN LogCount;
- SHELL_LINE_LIST *Log;
-} SHELL_LINE_LOG;
-
-///
-/// FILE styte interfaces for StdIn.
-///
-extern EFI_FILE_PROTOCOL FileInterfaceStdIn;
-
-///
-/// FILE styte interfaces for StdOut.
-///
-extern EFI_FILE_PROTOCOL FileInterfaceStdOut;
-
-///
-/// FILE styte interfaces for StdErr.
-///
-extern EFI_FILE_PROTOCOL FileInterfaceStdErr;
-
-///
-/// FILE style interface for NUL file.
-///
-extern EFI_FILE_PROTOCOL FileInterfaceNulFile;
-
-/**
- Creates a EFI_FILE_PROTOCOL (almost) object for using to access
- environment variables through file operations.
-
- @param EnvName The name of the Environment Variable to be operated on.
-
- @retval NULL Memory could not be allocated.
- @return other a pointer to an EFI_FILE_PROTOCOL structure
-**/
-EFI_FILE_PROTOCOL *
-CreateFileInterfaceEnv (
- CONST CHAR16 *EnvName
- );
-
-/**
- Creates a EFI_FILE_PROTOCOL (almost) object for using to access
- a file entirely in memory through file operations.
-
- @param[in] Unicode TRUE if the data is UNICODE, FALSE otherwise.
-
- @retval NULL Memory could not be allocated.
- @return other a pointer to an EFI_FILE_PROTOCOL structure
-**/
-EFI_FILE_PROTOCOL *
-CreateFileInterfaceMem (
- IN CONST BOOLEAN Unicode
- );
-
-/**
- Creates a EFI_FILE_PROTOCOL (almost) object for using to access
- a file entirely with unicode awareness through file operations.
-
- @param[in] Template The pointer to the handle to start with.
- @param[in] Unicode TRUE if the data is UNICODE, FALSE otherwise.
-
- @retval NULL Memory could not be allocated.
- @return other a pointer to an EFI_FILE_PROTOCOL structure
-**/
-EFI_FILE_PROTOCOL *
-CreateFileInterfaceFile (
- IN CONST EFI_FILE_PROTOCOL *Template,
- IN CONST BOOLEAN Unicode
- );
-
-#endif //_SHELL_FILE_HANDLE_WRAPPERS_HEADER_
+/** @file + EFI_FILE_PROTOCOL wrappers for other items (Like Environment Variables, StdIn, StdOut, StdErr, etc...) + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SHELL_FILE_HANDLE_WRAPPERS_HEADER_ +#define _SHELL_FILE_HANDLE_WRAPPERS_HEADER_ + +typedef struct { + LIST_ENTRY Link; + CHAR16 *Buffer; +} SHELL_LINE_LIST; + +typedef struct { + UINTN LogCount; + SHELL_LINE_LIST *Log; +} SHELL_LINE_LOG; + +/// +/// FILE styte interfaces for StdIn. +/// +extern EFI_FILE_PROTOCOL FileInterfaceStdIn; + +/// +/// FILE styte interfaces for StdOut. +/// +extern EFI_FILE_PROTOCOL FileInterfaceStdOut; + +/// +/// FILE styte interfaces for StdErr. +/// +extern EFI_FILE_PROTOCOL FileInterfaceStdErr; + +/// +/// FILE style interface for NUL file. +/// +extern EFI_FILE_PROTOCOL FileInterfaceNulFile; + +/** + Creates a EFI_FILE_PROTOCOL (almost) object for using to access + environment variables through file operations. + + @param EnvName The name of the Environment Variable to be operated on. + + @retval NULL Memory could not be allocated. + @return other a pointer to an EFI_FILE_PROTOCOL structure +**/ +EFI_FILE_PROTOCOL * +CreateFileInterfaceEnv ( + CONST CHAR16 *EnvName + ); + +/** + Creates a EFI_FILE_PROTOCOL (almost) object for using to access + a file entirely in memory through file operations. + + @param[in] Unicode TRUE if the data is UNICODE, FALSE otherwise. + + @retval NULL Memory could not be allocated. + @return other a pointer to an EFI_FILE_PROTOCOL structure +**/ +EFI_FILE_PROTOCOL * +CreateFileInterfaceMem ( + IN CONST BOOLEAN Unicode + ); + +/** + Creates a EFI_FILE_PROTOCOL (almost) object for using to access + a file entirely with unicode awareness through file operations. + + @param[in] Template The pointer to the handle to start with. + @param[in] Unicode TRUE if the data is UNICODE, FALSE otherwise. + + @retval NULL Memory could not be allocated. + @return other a pointer to an EFI_FILE_PROTOCOL structure +**/ +EFI_FILE_PROTOCOL * +CreateFileInterfaceFile ( + IN CONST EFI_FILE_PROTOCOL *Template, + IN CONST BOOLEAN Unicode + ); + +#endif //_SHELL_FILE_HANDLE_WRAPPERS_HEADER_ diff --git a/ShellPkg/Application/Shell/Shell.c b/ShellPkg/Application/Shell/Shell.c index 01b4e37871..15434bc026 100644 --- a/ShellPkg/Application/Shell/Shell.c +++ b/ShellPkg/Application/Shell/Shell.c @@ -1,3350 +1,3350 @@ -/** @file
- This is THE shell (application)
-
- Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
- (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.<BR>
- Copyright 2015-2018 Dell Technologies.<BR>
- Copyright (C) 2023, Apple Inc. All rights reserved.<BR>
-
- SPDX-License-Identifier: BSD-2-Clause-Patent
-**/
-
-#include "Shell.h"
-
-//
-// Initialize the global structure
-//
-SHELL_INFO ShellInfoObject = {
- NULL,
- NULL,
- FALSE,
- FALSE,
- {
- {
- {
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0
- }
- },
- 0,
- NULL,
- NULL
- },
- {
- { NULL,NULL }, NULL
- },
- {
- {
- { NULL,NULL }, NULL
- },
- 0,
- 0,
- TRUE
- },
- NULL,
- 0,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- {
- { NULL,NULL }, NULL, NULL
- },
- {
- { NULL,NULL }, NULL, NULL
- },
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- FALSE
-};
-
-STATIC CONST CHAR16 mScriptExtension[] = L".NSH";
-STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";
-STATIC CONST CHAR16 mStartupScript[] = L"startup.nsh";
-CONST CHAR16 mNoNestingEnvVarName[] = L"nonesting";
-CONST CHAR16 mNoNestingTrue[] = L"True";
-CONST CHAR16 mNoNestingFalse[] = L"False";
-
-/**
- Cleans off leading and trailing spaces and tabs.
-
- @param[in] String pointer to the string to trim them off.
-**/
-EFI_STATUS
-TrimSpaces (
- IN CHAR16 **String
- )
-{
- ASSERT (String != NULL);
- ASSERT (*String != NULL);
- //
- // Remove any spaces and tabs at the beginning of the (*String).
- //
- while (((*String)[0] == L' ') || ((*String)[0] == L'\t')) {
- CopyMem ((*String), (*String)+1, StrSize ((*String)) - sizeof ((*String)[0]));
- }
-
- //
- // Remove any spaces and tabs at the end of the (*String).
- //
- while ((StrLen (*String) > 0) && (((*String)[StrLen ((*String))-1] == L' ') || ((*String)[StrLen ((*String))-1] == L'\t'))) {
- (*String)[StrLen ((*String))-1] = CHAR_NULL;
- }
-
- return (EFI_SUCCESS);
-}
-
-/**
- Parse for the next instance of one string within another string. Can optionally make sure that
- the string was not escaped (^ character) per the shell specification.
-
- @param[in] SourceString The string to search within
- @param[in] FindString The string to look for
- @param[in] CheckForEscapeCharacter TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances
-**/
-CHAR16 *
-FindNextInstance (
- IN CONST CHAR16 *SourceString,
- IN CONST CHAR16 *FindString,
- IN CONST BOOLEAN CheckForEscapeCharacter
- )
-{
- CHAR16 *Temp;
-
- if (SourceString == NULL) {
- return (NULL);
- }
-
- Temp = StrStr (SourceString, FindString);
-
- //
- // If nothing found, or we don't care about escape characters
- //
- if ((Temp == NULL) || !CheckForEscapeCharacter) {
- return (Temp);
- }
-
- //
- // If we found an escaped character, try again on the remainder of the string
- //
- if ((Temp > (SourceString)) && (*(Temp-1) == L'^')) {
- return FindNextInstance (Temp+1, FindString, CheckForEscapeCharacter);
- }
-
- //
- // we found the right character
- //
- return (Temp);
-}
-
-/**
- Check whether the string between a pair of % is a valid environment variable name.
-
- @param[in] BeginPercent pointer to the first percent.
- @param[in] EndPercent pointer to the last percent.
-
- @retval TRUE is a valid environment variable name.
- @retval FALSE is NOT a valid environment variable name.
-**/
-BOOLEAN
-IsValidEnvironmentVariableName (
- IN CONST CHAR16 *BeginPercent,
- IN CONST CHAR16 *EndPercent
- )
-{
- CONST CHAR16 *Walker;
-
- Walker = NULL;
-
- ASSERT (BeginPercent != NULL);
- ASSERT (EndPercent != NULL);
- ASSERT (BeginPercent < EndPercent);
-
- if ((BeginPercent + 1) == EndPercent) {
- return FALSE;
- }
-
- for (Walker = BeginPercent + 1; Walker < EndPercent; Walker++) {
- if (
- ((*Walker >= L'0') && (*Walker <= L'9')) ||
- ((*Walker >= L'A') && (*Walker <= L'Z')) ||
- ((*Walker >= L'a') && (*Walker <= L'z')) ||
- (*Walker == L'_')
- )
- {
- if ((Walker == BeginPercent + 1) && ((*Walker >= L'0') && (*Walker <= L'9'))) {
- return FALSE;
- } else {
- continue;
- }
- } else {
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-/**
- Determine if a command line contains a split operation
-
- @param[in] CmdLine The command line to parse.
-
- @retval TRUE CmdLine has a valid split.
- @retval FALSE CmdLine does not have a valid split.
-**/
-BOOLEAN
-ContainsSplit (
- IN CONST CHAR16 *CmdLine
- )
-{
- CONST CHAR16 *TempSpot;
- CONST CHAR16 *FirstQuote;
- CONST CHAR16 *SecondQuote;
-
- FirstQuote = FindNextInstance (CmdLine, L"\"", TRUE);
- SecondQuote = NULL;
- TempSpot = FindFirstCharacter (CmdLine, L"|", L'^');
-
- if ((FirstQuote == NULL) ||
- (TempSpot == NULL) ||
- (TempSpot == CHAR_NULL) ||
- (FirstQuote > TempSpot)
- )
- {
- return (BOOLEAN)((TempSpot != NULL) && (*TempSpot != CHAR_NULL));
- }
-
- while ((TempSpot != NULL) && (*TempSpot != CHAR_NULL)) {
- if ((FirstQuote == NULL) || (FirstQuote > TempSpot)) {
- break;
- }
-
- SecondQuote = FindNextInstance (FirstQuote + 1, L"\"", TRUE);
- if (SecondQuote == NULL) {
- break;
- }
-
- if (SecondQuote < TempSpot) {
- FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);
- continue;
- } else {
- FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);
- TempSpot = FindFirstCharacter (TempSpot + 1, L"|", L'^');
- continue;
- }
- }
-
- return (BOOLEAN)((TempSpot != NULL) && (*TempSpot != CHAR_NULL));
-}
-
-/**
- Function to start monitoring for CTRL-S using SimpleTextInputEx. This
- feature's enabled state was not known when the shell initially launched.
-
- @retval EFI_SUCCESS The feature is enabled.
- @retval EFI_OUT_OF_RESOURCES There is not enough memory available.
-**/
-EFI_STATUS
-InternalEfiShellStartCtrlSMonitor (
- VOID
- )
-{
- EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
- EFI_KEY_DATA KeyData;
- EFI_STATUS Status;
-
- Status = gBS->OpenProtocol (
- gST->ConsoleInHandle,
- &gEfiSimpleTextInputExProtocolGuid,
- (VOID **)&SimpleEx,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- if (EFI_ERROR (Status)) {
- ShellPrintHiiEx (
- -1,
- -1,
- NULL,
- STRING_TOKEN (STR_SHELL_NO_IN_EX),
- ShellInfoObject.HiiHandle
- );
- return (EFI_SUCCESS);
- }
-
- KeyData.KeyState.KeyToggleState = 0;
- KeyData.Key.ScanCode = 0;
- KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
- KeyData.Key.UnicodeChar = L's';
-
- Status = SimpleEx->RegisterKeyNotify (
- SimpleEx,
- &KeyData,
- NotificationFunction,
- &ShellInfoObject.CtrlSNotifyHandle1
- );
-
- KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
- if (!EFI_ERROR (Status)) {
- Status = SimpleEx->RegisterKeyNotify (
- SimpleEx,
- &KeyData,
- NotificationFunction,
- &ShellInfoObject.CtrlSNotifyHandle2
- );
- }
-
- KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
- KeyData.Key.UnicodeChar = 19;
-
- if (!EFI_ERROR (Status)) {
- Status = SimpleEx->RegisterKeyNotify (
- SimpleEx,
- &KeyData,
- NotificationFunction,
- &ShellInfoObject.CtrlSNotifyHandle3
- );
- }
-
- KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
- if (!EFI_ERROR (Status)) {
- Status = SimpleEx->RegisterKeyNotify (
- SimpleEx,
- &KeyData,
- NotificationFunction,
- &ShellInfoObject.CtrlSNotifyHandle4
- );
- }
-
- return (Status);
-}
-
-/**
- The entry point for the application.
-
- @param[in] ImageHandle The firmware allocated handle for the EFI image.
- @param[in] SystemTable A pointer to the EFI System Table.
-
- @retval EFI_SUCCESS The entry point is executed successfully.
- @retval other Some error occurs when executing this entry point.
-
-**/
-EFI_STATUS
-EFIAPI
-UefiMain (
- IN EFI_HANDLE ImageHandle,
- IN EFI_SYSTEM_TABLE *SystemTable
- )
-{
- EFI_STATUS Status;
- CHAR16 *TempString;
- UINTN Size;
- EFI_HANDLE ConInHandle;
- EFI_SIMPLE_TEXT_INPUT_PROTOCOL *OldConIn;
- SPLIT_LIST *Split;
-
- if (PcdGet8 (PcdShellSupportLevel) > 3) {
- return (EFI_UNSUPPORTED);
- }
-
- //
- // Clear the screen
- //
- Status = gST->ConOut->ClearScreen (gST->ConOut);
- if (EFI_ERROR (Status)) {
- return (Status);
- }
-
- //
- // Populate the global structure from PCDs
- //
- ShellInfoObject.ImageDevPath = NULL;
- ShellInfoObject.FileDevPath = NULL;
- ShellInfoObject.PageBreakEnabled = PcdGetBool (PcdShellPageBreakDefault);
- ShellInfoObject.ViewingSettings.InsertMode = PcdGetBool (PcdShellInsertModeDefault);
- ShellInfoObject.LogScreenCount = PcdGet8 (PcdShellScreenLogCount);
-
- //
- // verify we dont allow for spec violation
- //
- ASSERT (ShellInfoObject.LogScreenCount >= 3);
-
- //
- // Initialize the LIST ENTRY objects...
- //
- InitializeListHead (&ShellInfoObject.BufferToFreeList.Link);
- InitializeListHead (&ShellInfoObject.ViewingSettings.CommandHistory.Link);
- InitializeListHead (&ShellInfoObject.SplitList.Link);
-
- //
- // Check PCDs for optional features that are not implemented yet.
- //
- if ( PcdGetBool (PcdShellSupportOldProtocols)
- || !FeaturePcdGet (PcdShellRequireHiiPlatform)
- || FeaturePcdGet (PcdShellSupportFrameworkHii)
- )
- {
- return (EFI_UNSUPPORTED);
- }
-
- //
- // turn off the watchdog timer
- //
- gBS->SetWatchdogTimer (0, 0, 0, NULL);
-
- //
- // install our console logger. This will keep a log of the output for back-browsing
- //
- Status = ConsoleLoggerInstall (ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);
- if (!EFI_ERROR (Status)) {
- //
- // Enable the cursor to be visible
- //
- gST->ConOut->EnableCursor (gST->ConOut, TRUE);
-
- //
- // If supporting EFI 1.1 we need to install HII protocol
- // only do this if PcdShellRequireHiiPlatform == FALSE
- //
- // remove EFI_UNSUPPORTED check above when complete.
- /// @todo add support for Framework HII
-
- //
- // install our (solitary) HII package
- //
- ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);
- if (ShellInfoObject.HiiHandle == NULL) {
- if (PcdGetBool (PcdShellSupportFrameworkHii)) {
- /// @todo Add our package into Framework HII
- }
-
- if (ShellInfoObject.HiiHandle == NULL) {
- Status = EFI_NOT_STARTED;
- goto FreeResources;
- }
- }
-
- //
- // create and install the EfiShellParametersProtocol
- //
- Status = CreatePopulateInstallShellParametersProtocol (&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);
- ASSERT_EFI_ERROR (Status);
- ASSERT (ShellInfoObject.NewShellParametersProtocol != NULL);
-
- //
- // create and install the EfiShellProtocol
- //
- Status = CreatePopulateInstallShellProtocol (&ShellInfoObject.NewEfiShellProtocol);
- ASSERT_EFI_ERROR (Status);
- ASSERT (ShellInfoObject.NewEfiShellProtocol != NULL);
-
- //
- // Now initialize the shell library (it requires Shell Parameters protocol)
- //
- Status = ShellInitialize ();
- ASSERT_EFI_ERROR (Status);
-
- Status = CommandInit ();
- ASSERT_EFI_ERROR (Status);
-
- Status = ShellInitEnvVarList ();
-
- //
- // Check the command line
- //
- Status = ProcessCommandLine ();
- if (EFI_ERROR (Status)) {
- goto FreeResources;
- }
-
- //
- // If shell support level is >= 1 create the mappings and paths
- //
- if (PcdGet8 (PcdShellSupportLevel) >= 1) {
- Status = ShellCommandCreateInitialMappingsAndPaths ();
- }
-
- //
- // Set the environment variable for nesting support
- //
- Size = 0;
- TempString = NULL;
- if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) {
- //
- // No change. require nesting in Shell Protocol Execute()
- //
- StrnCatGrow (
- &TempString,
- &Size,
- L"False",
- 0
- );
- } else {
- StrnCatGrow (
- &TempString,
- &Size,
- mNoNestingTrue,
- 0
- );
- }
-
- Status = InternalEfiShellSetEnv (mNoNestingEnvVarName, TempString, TRUE);
- SHELL_FREE_NON_NULL (TempString);
- Size = 0;
-
- //
- // save the device path for the loaded image and the device path for the filepath (under loaded image)
- // These are where to look for the startup.nsh file
- //
- Status = GetDevicePathsForImageAndFile (&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);
- ASSERT_EFI_ERROR (Status);
-
- //
- // Display the version
- //
- if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {
- ShellPrintHiiEx (
- 0,
- gST->ConOut->Mode->CursorRow,
- NULL,
- STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL),
- ShellInfoObject.HiiHandle,
- SupportLevel[PcdGet8 (PcdShellSupportLevel)],
- gEfiShellProtocol->MajorVersion,
- gEfiShellProtocol->MinorVersion
- );
-
- ShellPrintHiiEx (
- -1,
- -1,
- NULL,
- STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER),
- ShellInfoObject.HiiHandle,
- (CHAR16 *)PcdGetPtr (PcdShellSupplier)
- );
-
- ShellPrintHiiEx (
- -1,
- -1,
- NULL,
- STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI),
- ShellInfoObject.HiiHandle,
- (gST->Hdr.Revision&0xffff0000)>>16,
- (gST->Hdr.Revision&0x0000ffff),
- gST->FirmwareVendor,
- gST->FirmwareRevision
- );
- }
-
- //
- // Display the mapping
- //
- if ((PcdGet8 (PcdShellSupportLevel) >= 2) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {
- Status = RunCommand (L"map");
- ASSERT_EFI_ERROR (Status);
- }
-
- //
- // init all the built in alias'
- //
- Status = SetBuiltInAlias ();
- ASSERT_EFI_ERROR (Status);
-
- //
- // Initialize environment variables
- //
- if (ShellCommandGetProfileList () != NULL) {
- Status = InternalEfiShellSetEnv (L"profiles", ShellCommandGetProfileList (), TRUE);
- ASSERT_EFI_ERROR (Status);
- }
-
- Size = 100;
- TempString = AllocateZeroPool (Size);
-
- UnicodeSPrint (TempString, Size, L"%d", PcdGet8 (PcdShellSupportLevel));
- Status = InternalEfiShellSetEnv (L"uefishellsupport", TempString, TRUE);
- ASSERT_EFI_ERROR (Status);
-
- UnicodeSPrint (TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);
- Status = InternalEfiShellSetEnv (L"uefishellversion", TempString, TRUE);
- ASSERT_EFI_ERROR (Status);
-
- UnicodeSPrint (TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);
- Status = InternalEfiShellSetEnv (L"uefiversion", TempString, TRUE);
- ASSERT_EFI_ERROR (Status);
-
- FreePool (TempString);
-
- if (!EFI_ERROR (Status)) {
- if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
- //
- // Set up the event for CTRL-C monitoring...
- //
- Status = InernalEfiShellStartMonitor ();
- }
-
- if (!EFI_ERROR (Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
- //
- // Set up the event for CTRL-S monitoring...
- //
- Status = InternalEfiShellStartCtrlSMonitor ();
- }
-
- if (!EFI_ERROR (Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
- //
- // close off the gST->ConIn
- //
- OldConIn = gST->ConIn;
- ConInHandle = gST->ConsoleInHandle;
- gST->ConIn = CreateSimpleTextInOnFile ((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle);
- } else {
- OldConIn = NULL;
- ConInHandle = NULL;
- }
-
- if (!EFI_ERROR (Status) && (PcdGet8 (PcdShellSupportLevel) >= 1)) {
- //
- // process the startup script or launch the called app.
- //
- Status = DoStartupScript (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
- }
-
- if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit () && ((PcdGet8 (PcdShellSupportLevel) >= 3) || PcdGetBool (PcdShellForceConsole)) && !EFI_ERROR (Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
- //
- // begin the UI waiting loop
- //
- do {
- //
- // clean out all the memory allocated for CONST <something> * return values
- // between each shell prompt presentation
- //
- if (!IsListEmpty (&ShellInfoObject.BufferToFreeList.Link)) {
- FreeBufferList (&ShellInfoObject.BufferToFreeList);
- }
-
- //
- // Reset page break back to default.
- //
- ShellInfoObject.PageBreakEnabled = PcdGetBool (PcdShellPageBreakDefault);
- ASSERT (ShellInfoObject.ConsoleInfo != NULL);
- ShellInfoObject.ConsoleInfo->Enabled = TRUE;
- ShellInfoObject.ConsoleInfo->RowCounter = 0;
-
- //
- // Display Prompt
- //
- Status = DoShellPrompt ();
- } while (!ShellCommandGetExit ());
- }
-
- if ((OldConIn != NULL) && (ConInHandle != NULL)) {
- CloseSimpleTextInOnFile (gST->ConIn);
- gST->ConIn = OldConIn;
- gST->ConsoleInHandle = ConInHandle;
- }
- }
- }
-
-FreeResources:
- //
- // uninstall protocols / free memory / etc...
- //
- if (ShellInfoObject.UserBreakTimer != NULL) {
- gBS->CloseEvent (ShellInfoObject.UserBreakTimer);
- DEBUG_CODE (
- ShellInfoObject.UserBreakTimer = NULL;
- );
- }
-
- if (ShellInfoObject.ImageDevPath != NULL) {
- FreePool (ShellInfoObject.ImageDevPath);
- DEBUG_CODE (
- ShellInfoObject.ImageDevPath = NULL;
- );
- }
-
- if (ShellInfoObject.FileDevPath != NULL) {
- FreePool (ShellInfoObject.FileDevPath);
- DEBUG_CODE (
- ShellInfoObject.FileDevPath = NULL;
- );
- }
-
- if (ShellInfoObject.NewShellParametersProtocol != NULL) {
- CleanUpShellParametersProtocol (ShellInfoObject.NewShellParametersProtocol);
- DEBUG_CODE (
- ShellInfoObject.NewShellParametersProtocol = NULL;
- );
- }
-
- if (ShellInfoObject.NewEfiShellProtocol != NULL) {
- if (ShellInfoObject.NewEfiShellProtocol->IsRootShell ()) {
- InternalEfiShellSetEnv (L"cwd", NULL, TRUE);
- }
-
- CleanUpShellEnvironment (ShellInfoObject.NewEfiShellProtocol);
- DEBUG_CODE (
- ShellInfoObject.NewEfiShellProtocol = NULL;
- );
- }
-
- if (!IsListEmpty (&ShellInfoObject.BufferToFreeList.Link)) {
- FreeBufferList (&ShellInfoObject.BufferToFreeList);
- }
-
- if (!IsListEmpty (&ShellInfoObject.SplitList.Link)) {
- ASSERT (FALSE); /// @todo finish this de-allocation (free SplitStdIn/Out when needed).
-
- for ( Split = (SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link)
- ; !IsNull (&ShellInfoObject.SplitList.Link, &Split->Link)
- ; Split = (SPLIT_LIST *)GetNextNode (&ShellInfoObject.SplitList.Link, &Split->Link)
- )
- {
- RemoveEntryList (&Split->Link);
- FreePool (Split);
- }
-
- DEBUG_CODE (
- InitializeListHead (&ShellInfoObject.SplitList.Link);
- );
- }
-
- if (ShellInfoObject.ShellInitSettings.FileName != NULL) {
- FreePool (ShellInfoObject.ShellInitSettings.FileName);
- DEBUG_CODE (
- ShellInfoObject.ShellInitSettings.FileName = NULL;
- );
- }
-
- if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
- FreePool (ShellInfoObject.ShellInitSettings.FileOptions);
- DEBUG_CODE (
- ShellInfoObject.ShellInitSettings.FileOptions = NULL;
- );
- }
-
- if (ShellInfoObject.HiiHandle != NULL) {
- HiiRemovePackages (ShellInfoObject.HiiHandle);
- DEBUG_CODE (
- ShellInfoObject.HiiHandle = NULL;
- );
- }
-
- if (!IsListEmpty (&ShellInfoObject.ViewingSettings.CommandHistory.Link)) {
- FreeBufferList (&ShellInfoObject.ViewingSettings.CommandHistory);
- }
-
- ASSERT (ShellInfoObject.ConsoleInfo != NULL);
- if (ShellInfoObject.ConsoleInfo != NULL) {
- ConsoleLoggerUninstall (ShellInfoObject.ConsoleInfo);
- FreePool (ShellInfoObject.ConsoleInfo);
- DEBUG_CODE (
- ShellInfoObject.ConsoleInfo = NULL;
- );
- }
-
- ShellFreeEnvVarList ();
-
- if (ShellCommandGetExit ()) {
- return ((EFI_STATUS)ShellCommandGetExitCode ());
- }
-
- return (Status);
-}
-
-/**
- Sets all the alias' that were registered with the ShellCommandLib library.
-
- @retval EFI_SUCCESS all init commands were run successfully.
-**/
-EFI_STATUS
-SetBuiltInAlias (
- VOID
- )
-{
- EFI_STATUS Status;
- CONST ALIAS_LIST *List;
- ALIAS_LIST *Node;
-
- //
- // Get all the commands we want to run
- //
- List = ShellCommandGetInitAliasList ();
-
- //
- // for each command in the List
- //
- for ( Node = (ALIAS_LIST *)GetFirstNode (&List->Link)
- ; !IsNull (&List->Link, &Node->Link)
- ; Node = (ALIAS_LIST *)GetNextNode (&List->Link, &Node->Link)
- )
- {
- //
- // install the alias'
- //
- Status = InternalSetAlias (Node->CommandString, Node->Alias, TRUE);
- ASSERT_EFI_ERROR (Status);
- }
-
- return (EFI_SUCCESS);
-}
-
-/**
- Internal function to determine if 2 command names are really the same.
-
- @param[in] Command1 The pointer to the first command name.
- @param[in] Command2 The pointer to the second command name.
-
- @retval TRUE The 2 command names are the same.
- @retval FALSE The 2 command names are not the same.
-**/
-BOOLEAN
-IsCommand (
- IN CONST CHAR16 *Command1,
- IN CONST CHAR16 *Command2
- )
-{
- if (StringNoCaseCompare (&Command1, &Command2) == 0) {
- return (TRUE);
- }
-
- return (FALSE);
-}
-
-/**
- Internal function to determine if a command is a script only command.
-
- @param[in] CommandName The pointer to the command name.
-
- @retval TRUE The command is a script only command.
- @retval FALSE The command is not a script only command.
-**/
-BOOLEAN
-IsScriptOnlyCommand (
- IN CONST CHAR16 *CommandName
- )
-{
- if ( IsCommand (CommandName, L"for")
- || IsCommand (CommandName, L"endfor")
- || IsCommand (CommandName, L"if")
- || IsCommand (CommandName, L"else")
- || IsCommand (CommandName, L"endif")
- || IsCommand (CommandName, L"goto"))
- {
- return (TRUE);
- }
-
- return (FALSE);
-}
-
-/**
- This function will populate the 2 device path protocol parameters based on the
- global gImageHandle. The DevPath will point to the device path for the handle that has
- loaded image protocol installed on it. The FilePath will point to the device path
- for the file that was loaded.
-
- @param[in, out] DevPath On a successful return the device path to the loaded image.
- @param[in, out] FilePath On a successful return the device path to the file.
-
- @retval EFI_SUCCESS The 2 device paths were successfully returned.
- @retval other A error from gBS->HandleProtocol.
-
- @sa HandleProtocol
-**/
-EFI_STATUS
-GetDevicePathsForImageAndFile (
- IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,
- IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
- )
-{
- EFI_STATUS Status;
- EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
- EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;
-
- ASSERT (DevPath != NULL);
- ASSERT (FilePath != NULL);
-
- Status = gBS->OpenProtocol (
- gImageHandle,
- &gEfiLoadedImageProtocolGuid,
- (VOID **)&LoadedImage,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- if (!EFI_ERROR (Status)) {
- Status = gBS->OpenProtocol (
- LoadedImage->DeviceHandle,
- &gEfiDevicePathProtocolGuid,
- (VOID **)&ImageDevicePath,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- if (!EFI_ERROR (Status)) {
- *DevPath = DuplicateDevicePath (ImageDevicePath);
- *FilePath = DuplicateDevicePath (LoadedImage->FilePath);
- gBS->CloseProtocol (
- LoadedImage->DeviceHandle,
- &gEfiDevicePathProtocolGuid,
- gImageHandle,
- NULL
- );
- }
-
- gBS->CloseProtocol (
- gImageHandle,
- &gEfiLoadedImageProtocolGuid,
- gImageHandle,
- NULL
- );
- }
-
- return (Status);
-}
-
-/**
- Process all Uefi Shell 2.0 command line options.
-
- see Uefi Shell 2.0 section 3.2 for full details.
-
- the command line must resemble the following:
-
- shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
-
- ShellOpt-options Options which control the initialization behavior of the shell.
- These options are read from the EFI global variable "ShellOpt"
- and are processed before options or file-name.
-
- options Options which control the initialization behavior of the shell.
-
- file-name The name of a UEFI shell application or script to be executed
- after initialization is complete. By default, if file-name is
- specified, then -nostartup is implied. Scripts are not supported
- by level 0.
-
- file-name-options The command-line options that are passed to file-name when it
- is invoked.
-
- This will initialize the ShellInfoObject.ShellInitSettings global variable.
-
- @retval EFI_SUCCESS The variable is initialized.
-**/
-EFI_STATUS
-ProcessCommandLine (
- VOID
- )
-{
- UINTN Size;
- UINTN LoopVar;
- CHAR16 *CurrentArg;
- CHAR16 *DelayValueStr;
- UINT64 DelayValue;
- EFI_STATUS Status;
- EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation;
-
- // `file-name-options` will contain arguments to `file-name` that we don't
- // know about. This would cause ShellCommandLineParse to error, so we parse
- // arguments manually, ignoring those after the first thing that doesn't look
- // like a shell option (which is assumed to be `file-name`).
-
- Status = gBS->LocateProtocol (
- &gEfiUnicodeCollation2ProtocolGuid,
- NULL,
- (VOID **)&UnicodeCollation
- );
- if (EFI_ERROR (Status)) {
- Status = gBS->LocateProtocol (
- &gEfiUnicodeCollationProtocolGuid,
- NULL,
- (VOID **)&UnicodeCollation
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- }
-
- // Set default options
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = FALSE;
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = FALSE;
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = FALSE;
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = FALSE;
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = FALSE;
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = FALSE;
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = FALSE;
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = FALSE;
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = FALSE;
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = FALSE;
- ShellInfoObject.ShellInitSettings.Delay = PcdGet32 (PcdShellDefaultDelay);
-
- //
- // Start LoopVar at 0 to parse only optional arguments at Argv[0]
- // and parse other parameters from Argv[1]. This is for use case that
- // UEFI Shell boot option is created, and OptionalData is provided
- // that starts with shell command-line options.
- //
- for (LoopVar = 0; LoopVar < gEfiShellParametersProtocol->Argc; LoopVar++) {
- CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar];
- if (UnicodeCollation->StriColl (
- UnicodeCollation,
- L"-startup",
- CurrentArg
- ) == 0)
- {
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = TRUE;
- } else if (UnicodeCollation->StriColl (
- UnicodeCollation,
- L"-nostartup",
- CurrentArg
- ) == 0)
- {
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = TRUE;
- } else if (UnicodeCollation->StriColl (
- UnicodeCollation,
- L"-noconsoleout",
- CurrentArg
- ) == 0)
- {
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = TRUE;
- } else if (UnicodeCollation->StriColl (
- UnicodeCollation,
- L"-noconsolein",
- CurrentArg
- ) == 0)
- {
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = TRUE;
- } else if (UnicodeCollation->StriColl (
- UnicodeCollation,
- L"-nointerrupt",
- CurrentArg
- ) == 0)
- {
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = TRUE;
- } else if (UnicodeCollation->StriColl (
- UnicodeCollation,
- L"-nomap",
- CurrentArg
- ) == 0)
- {
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = TRUE;
- } else if (UnicodeCollation->StriColl (
- UnicodeCollation,
- L"-noversion",
- CurrentArg
- ) == 0)
- {
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = TRUE;
- } else if (UnicodeCollation->StriColl (
- UnicodeCollation,
- L"-nonest",
- CurrentArg
- ) == 0)
- {
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = TRUE;
- } else if (UnicodeCollation->StriColl (
- UnicodeCollation,
- L"-delay",
- CurrentArg
- ) == 0)
- {
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = TRUE;
- // Check for optional delay value following "-delay"
- if ((LoopVar + 1) >= gEfiShellParametersProtocol->Argc) {
- DelayValueStr = NULL;
- } else {
- DelayValueStr = gEfiShellParametersProtocol->Argv[LoopVar + 1];
- }
-
- if (DelayValueStr != NULL) {
- if (*DelayValueStr == L':') {
- DelayValueStr++;
- }
-
- if (!EFI_ERROR (
- ShellConvertStringToUint64 (
- DelayValueStr,
- &DelayValue,
- FALSE,
- FALSE
- )
- ))
- {
- ShellInfoObject.ShellInitSettings.Delay = (UINTN)DelayValue;
- LoopVar++;
- }
- }
- } else if (UnicodeCollation->StriColl (
- UnicodeCollation,
- L"-exit",
- CurrentArg
- ) == 0)
- {
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = TRUE;
- } else if (StrnCmp (L"-", CurrentArg, 1) == 0) {
- // Unrecognized option
- ShellPrintHiiEx (
- -1,
- -1,
- NULL,
- STRING_TOKEN (STR_GEN_PROBLEM),
- ShellInfoObject.HiiHandle,
- CurrentArg
- );
- return EFI_INVALID_PARAMETER;
- } else {
- //
- // First argument should be Shell.efi image name
- //
- if (LoopVar == 0) {
- continue;
- }
-
- ShellInfoObject.ShellInitSettings.FileName = NULL;
- Size = 0;
- //
- // If first argument contains a space, then add double quotes before the argument
- //
- if (StrStr (CurrentArg, L" ") != NULL) {
- StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0);
- if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
- }
-
- StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, CurrentArg, 0);
- if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- //
- // If first argument contains a space, then add double quotes after the argument
- //
- if (StrStr (CurrentArg, L" ") != NULL) {
- StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0);
- if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
- }
-
- //
- // We found `file-name`.
- //
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;
- LoopVar++;
-
- // Add `file-name-options`
- for (Size = 0; LoopVar < gEfiShellParametersProtocol->Argc; LoopVar++) {
- ASSERT ((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));
- //
- // Add a space between arguments
- //
- if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
- StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileOptions, &Size, L" ", 0);
- if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
- SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName);
- return (EFI_OUT_OF_RESOURCES);
- }
- }
-
- //
- // If an argument contains a space, then add double quotes before the argument
- //
- if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != NULL) {
- StrnCatGrow (
- &ShellInfoObject.ShellInitSettings.FileOptions,
- &Size,
- L"\"",
- 0
- );
- if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
- SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName);
- return (EFI_OUT_OF_RESOURCES);
- }
- }
-
- StrnCatGrow (
- &ShellInfoObject.ShellInitSettings.FileOptions,
- &Size,
- gEfiShellParametersProtocol->Argv[LoopVar],
- 0
- );
- if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
- SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- //
- // If an argument contains a space, then add double quotes after the argument
- //
- if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != NULL) {
- StrnCatGrow (
- &ShellInfoObject.ShellInitSettings.FileOptions,
- &Size,
- L"\"",
- 0
- );
- if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
- SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName);
- return (EFI_OUT_OF_RESOURCES);
- }
- }
- }
- }
- }
-
- // "-nointerrupt" overrides "-delay"
- if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
- ShellInfoObject.ShellInitSettings.Delay = 0;
- }
-
- return EFI_SUCCESS;
-}
-
-/**
- Function try to find location of the Startup.nsh file.
-
- The buffer is callee allocated and should be freed by the caller.
-
- @param ImageDevicePath The path to the image for shell. first place to look for the startup script
- @param FileDevicePath The path to the file for shell. second place to look for the startup script.
-
- @retval NULL No Startup.nsh file was found.
- @return !=NULL Pointer to NULL-terminated path.
-**/
-CHAR16 *
-LocateStartupScript (
- IN EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath,
- IN EFI_DEVICE_PATH_PROTOCOL *FileDevicePath
- )
-{
- CHAR16 *StartupScriptPath;
- CHAR16 *TempSpot;
- CONST CHAR16 *MapName;
- UINTN Size;
-
- StartupScriptPath = NULL;
- Size = 0;
-
- //
- // Try to find 'Startup.nsh' in the directory where the shell itself was launched.
- //
- MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath (&ImageDevicePath);
- if (MapName != NULL) {
- StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, MapName, 0);
- if (StartupScriptPath == NULL) {
- //
- // Do not locate the startup script in sys path when out of resource.
- //
- return NULL;
- }
-
- TempSpot = StrStr (StartupScriptPath, L";");
- if (TempSpot != NULL) {
- *TempSpot = CHAR_NULL;
- }
-
- InternalEfiShellSetEnv (L"homefilesystem", StartupScriptPath, TRUE);
-
- StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, ((FILEPATH_DEVICE_PATH *)FileDevicePath)->PathName, 0);
- PathRemoveLastItem (StartupScriptPath);
- StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, mStartupScript, 0);
- }
-
- //
- // Try to find 'Startup.nsh' in the execution path defined by the environment variable PATH.
- //
- if ((StartupScriptPath == NULL) || EFI_ERROR (ShellIsFile (StartupScriptPath))) {
- SHELL_FREE_NON_NULL (StartupScriptPath);
- StartupScriptPath = ShellFindFilePath (mStartupScript);
- }
-
- return StartupScriptPath;
-}
-
-/**
- Handles all interaction with the default startup script.
-
- this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
-
- @param ImagePath the path to the image for shell. first place to look for the startup script
- @param FilePath the path to the file for shell. second place to look for the startup script.
-
- @retval EFI_SUCCESS the variable is initialized.
-**/
-EFI_STATUS
-DoStartupScript (
- IN EFI_DEVICE_PATH_PROTOCOL *ImagePath,
- IN EFI_DEVICE_PATH_PROTOCOL *FilePath
- )
-{
- EFI_STATUS Status;
- EFI_STATUS CalleeStatus;
- UINTN Delay;
- EFI_INPUT_KEY Key;
- CHAR16 *FileStringPath;
- CHAR16 *FullFileStringPath;
- UINTN NewSize;
-
- CalleeStatus = EFI_SUCCESS;
- Key.UnicodeChar = CHAR_NULL;
- Key.ScanCode = 0;
-
- if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && (ShellInfoObject.ShellInitSettings.FileName != NULL)) {
- //
- // launch something else instead
- //
- NewSize = StrSize (ShellInfoObject.ShellInitSettings.FileName);
- if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
- NewSize += StrSize (ShellInfoObject.ShellInitSettings.FileOptions) + sizeof (CHAR16);
- }
-
- FileStringPath = AllocateZeroPool (NewSize);
- if (FileStringPath == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- StrCpyS (FileStringPath, NewSize/sizeof (CHAR16), ShellInfoObject.ShellInitSettings.FileName);
- if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
- StrnCatS (FileStringPath, NewSize/sizeof (CHAR16), L" ", NewSize/sizeof (CHAR16) - StrLen (FileStringPath) -1);
- StrnCatS (FileStringPath, NewSize/sizeof (CHAR16), ShellInfoObject.ShellInitSettings.FileOptions, NewSize/sizeof (CHAR16) - StrLen (FileStringPath) -1);
- }
-
- Status = RunShellCommand (FileStringPath, &CalleeStatus);
- if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit == TRUE) {
- ShellCommandRegisterExit (gEfiShellProtocol->BatchIsActive (), (UINT64)CalleeStatus);
- }
-
- FreePool (FileStringPath);
- return (Status);
- }
-
- //
- // for shell level 0 we do no scripts
- // Without the Startup bit overriding we allow for nostartup to prevent scripts
- //
- if ( (PcdGet8 (PcdShellSupportLevel) < 1)
- || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup)
- )
- {
- return (EFI_SUCCESS);
- }
-
- gST->ConOut->EnableCursor (gST->ConOut, FALSE);
- //
- // print out our warning and see if they press a key
- //
- for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay
- ; Delay != 0 && EFI_ERROR (Status)
- ; Delay--
- )
- {
- ShellPrintHiiEx (0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay);
- gBS->Stall (1000000);
- if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
- Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
- }
- }
-
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);
- gST->ConOut->EnableCursor (gST->ConOut, TRUE);
-
- //
- // ESC was pressed
- //
- if ((Status == EFI_SUCCESS) && (Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC)) {
- return (EFI_SUCCESS);
- }
-
- FileStringPath = LocateStartupScript (ImagePath, FilePath);
- if (FileStringPath != NULL) {
- FullFileStringPath = FullyQualifyPath (FileStringPath);
- if (FullFileStringPath == NULL) {
- Status = RunScriptFile (FileStringPath, NULL, FileStringPath, ShellInfoObject.NewShellParametersProtocol);
- } else {
- Status = RunScriptFile (FullFileStringPath, NULL, FullFileStringPath, ShellInfoObject.NewShellParametersProtocol);
- FreePool (FullFileStringPath);
- }
-
- FreePool (FileStringPath);
- } else {
- //
- // we return success since startup script is not mandatory.
- //
- Status = EFI_SUCCESS;
- }
-
- return (Status);
-}
-
-/**
- Function to perform the shell prompt looping. It will do a single prompt,
- dispatch the result, and then return. It is expected that the caller will
- call this function in a loop many times.
-
- @retval EFI_SUCCESS
- @retval RETURN_ABORTED
-**/
-EFI_STATUS
-DoShellPrompt (
- VOID
- )
-{
- UINTN Column;
- UINTN Row;
- CHAR16 *CmdLine;
- CONST CHAR16 *CurDir;
- UINTN BufferSize;
- EFI_STATUS Status;
- LIST_ENTRY OldBufferList;
-
- CurDir = NULL;
-
- //
- // Get screen setting to decide size of the command line buffer
- //
- gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);
- BufferSize = Column * Row * sizeof (CHAR16);
- CmdLine = AllocateZeroPool (BufferSize);
- if (CmdLine == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- SaveBufferList (&OldBufferList);
- CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv (L"cwd");
-
- //
- // Prompt for input
- //
- gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);
-
- if ((CurDir != NULL) && (StrLen (CurDir) > 1)) {
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
- } else {
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
- }
-
- //
- // Read a line from the console
- //
- Status = ShellInfoObject.NewEfiShellProtocol->ReadFile (ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);
-
- //
- // Null terminate the string and parse it
- //
- if (!EFI_ERROR (Status)) {
- //
- // Reset the CTRL-C event just before running the command (yes we ignore the return values)
- //
- Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);
-
- CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
- Status = RunCommand (CmdLine);
- }
-
- //
- // Done with this command
- //
- RestoreBufferList (&OldBufferList);
- FreePool (CmdLine);
- return Status;
-}
-
-/**
- Add a buffer to the Buffer To Free List for safely returning buffers to other
- places without risking letting them modify internal shell information.
-
- @param Buffer Something to pass to FreePool when the shell is exiting.
-**/
-VOID *
-AddBufferToFreeList (
- VOID *Buffer
- )
-{
- BUFFER_LIST *BufferListEntry;
-
- if (Buffer == NULL) {
- return (NULL);
- }
-
- BufferListEntry = AllocateZeroPool (sizeof (BUFFER_LIST));
- if (BufferListEntry == NULL) {
- return NULL;
- }
-
- BufferListEntry->Buffer = Buffer;
- InsertTailList (&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);
- return (Buffer);
-}
-
-/**
- Create a new buffer list and stores the old one to OldBufferList
-
- @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
-**/
-VOID
-SaveBufferList (
- OUT LIST_ENTRY *OldBufferList
- )
-{
- CopyMem (OldBufferList, &ShellInfoObject.BufferToFreeList.Link, sizeof (LIST_ENTRY));
- InitializeListHead (&ShellInfoObject.BufferToFreeList.Link);
-}
-
-/**
- Restore previous nodes into BufferToFreeList .
-
- @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
-**/
-VOID
-RestoreBufferList (
- IN OUT LIST_ENTRY *OldBufferList
- )
-{
- FreeBufferList (&ShellInfoObject.BufferToFreeList);
- CopyMem (&ShellInfoObject.BufferToFreeList.Link, OldBufferList, sizeof (LIST_ENTRY));
-}
-
-/**
- Add a buffer to the Line History List
-
- @param Buffer The line buffer to add.
-**/
-VOID
-AddLineToCommandHistory (
- IN CONST CHAR16 *Buffer
- )
-{
- BUFFER_LIST *Node;
- BUFFER_LIST *Walker;
- UINT16 MaxHistoryCmdCount;
- UINT16 Count;
-
- Count = 0;
- MaxHistoryCmdCount = PcdGet16 (PcdShellMaxHistoryCommandCount);
-
- if (MaxHistoryCmdCount == 0) {
- return;
- }
-
- Node = AllocateZeroPool (sizeof (BUFFER_LIST));
- if (Node == NULL) {
- return;
- }
-
- Node->Buffer = AllocateCopyPool (StrSize (Buffer), Buffer);
- if (Node->Buffer == NULL) {
- FreePool (Node);
- return;
- }
-
- for ( Walker = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link)
- ; !IsNull (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)
- ; Walker = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)
- )
- {
- Count++;
- }
-
- if (Count < MaxHistoryCmdCount) {
- InsertTailList (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
- } else {
- Walker = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link);
- RemoveEntryList (&Walker->Link);
- if (Walker->Buffer != NULL) {
- FreePool (Walker->Buffer);
- }
-
- FreePool (Walker);
- InsertTailList (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
- }
-}
-
-/**
- Checks if a string is an alias for another command. If yes, then it replaces the alias name
- with the correct command name.
-
- @param[in, out] CommandString Upon entry the potential alias. Upon return the
- command name if it was an alias. If it was not
- an alias it will be unchanged. This function may
- change the buffer to fit the command name.
-
- @retval EFI_SUCCESS The name was changed.
- @retval EFI_SUCCESS The name was not an alias.
- @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
-**/
-EFI_STATUS
-ShellConvertAlias (
- IN OUT CHAR16 **CommandString
- )
-{
- CONST CHAR16 *NewString;
-
- NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias (*CommandString, NULL);
- if (NewString == NULL) {
- return (EFI_SUCCESS);
- }
-
- FreePool (*CommandString);
- *CommandString = AllocateCopyPool (StrSize (NewString), NewString);
- if (*CommandString == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- return (EFI_SUCCESS);
-}
-
-/**
- This function will eliminate unreplaced (and therefore non-found) environment variables.
-
- @param[in,out] CmdLine The command line to update.
-**/
-EFI_STATUS
-StripUnreplacedEnvironmentVariables (
- IN OUT CHAR16 *CmdLine
- )
-{
- CHAR16 *FirstPercent;
- CHAR16 *FirstQuote;
- CHAR16 *SecondPercent;
- CHAR16 *SecondQuote;
- CHAR16 *CurrentLocator;
-
- for (CurrentLocator = CmdLine; CurrentLocator != NULL; ) {
- FirstQuote = FindNextInstance (CurrentLocator, L"\"", TRUE);
- FirstPercent = FindNextInstance (CurrentLocator, L"%", TRUE);
- SecondPercent = FirstPercent != NULL ? FindNextInstance (FirstPercent+1, L"%", TRUE) : NULL;
- if ((FirstPercent == NULL) || (SecondPercent == NULL)) {
- //
- // If we ever don't have 2 % we are done.
- //
- break;
- }
-
- if ((FirstQuote != NULL) && (FirstQuote < FirstPercent)) {
- SecondQuote = FindNextInstance (FirstQuote+1, L"\"", TRUE);
- //
- // Quote is first found
- //
-
- if (SecondQuote < FirstPercent) {
- //
- // restart after the pair of "
- //
- CurrentLocator = SecondQuote + 1;
- } else {
- /* FirstPercent < SecondQuote */
- //
- // Restart on the first percent
- //
- CurrentLocator = FirstPercent;
- }
-
- continue;
- }
-
- if ((FirstQuote == NULL) || (SecondPercent < FirstQuote)) {
- if (IsValidEnvironmentVariableName (FirstPercent, SecondPercent)) {
- //
- // We need to remove from FirstPercent to SecondPercent
- //
- CopyMem (FirstPercent, SecondPercent + 1, StrSize (SecondPercent + 1));
- //
- // don't need to update the locator. both % characters are gone.
- //
- } else {
- CurrentLocator = SecondPercent + 1;
- }
-
- continue;
- }
-
- CurrentLocator = FirstQuote;
- }
-
- return (EFI_SUCCESS);
-}
-
-/**
- Function allocates a new command line and replaces all instances of environment
- variable names that are correctly preset to their values.
-
- If the return value is not NULL the memory must be caller freed.
-
- @param[in] OriginalCommandLine The original command line
-
- @retval NULL An error occurred.
- @return The new command line with no environment variables present.
-**/
-CHAR16 *
-ShellConvertVariables (
- IN CONST CHAR16 *OriginalCommandLine
- )
-{
- CONST CHAR16 *MasterEnvList;
- UINTN NewSize;
- CHAR16 *NewCommandLine1;
- CHAR16 *NewCommandLine2;
- CHAR16 *Temp;
- UINTN ItemSize;
- CHAR16 *ItemTemp;
- SCRIPT_FILE *CurrentScriptFile;
- ALIAS_LIST *AliasListNode;
-
- ASSERT (OriginalCommandLine != NULL);
-
- ItemSize = 0;
- NewSize = StrSize (OriginalCommandLine);
- CurrentScriptFile = ShellCommandGetCurrentScriptFile ();
- Temp = NULL;
-
- /// @todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
-
- //
- // calculate the size required for the post-conversion string...
- //
- if (CurrentScriptFile != NULL) {
- for (AliasListNode = (ALIAS_LIST *)GetFirstNode (&CurrentScriptFile->SubstList)
- ; !IsNull (&CurrentScriptFile->SubstList, &AliasListNode->Link)
- ; AliasListNode = (ALIAS_LIST *)GetNextNode (&CurrentScriptFile->SubstList, &AliasListNode->Link)
- )
- {
- for (Temp = StrStr (OriginalCommandLine, AliasListNode->Alias)
- ; Temp != NULL
- ; Temp = StrStr (Temp+1, AliasListNode->Alias)
- )
- {
- //
- // we need a preceding and if there is space no ^ preceding (if no space ignore)
- //
- if ((((Temp-OriginalCommandLine) > 2) && (*(Temp-2) != L'^')) || ((Temp-OriginalCommandLine) <= 2)) {
- NewSize += StrSize (AliasListNode->CommandString);
- }
- }
- }
- }
-
- for (MasterEnvList = EfiShellGetEnv (NULL)
- ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL // && *(MasterEnvList+1) != CHAR_NULL
- ; MasterEnvList += StrLen (MasterEnvList) + 1
- )
- {
- if (StrSize (MasterEnvList) > ItemSize) {
- ItemSize = StrSize (MasterEnvList);
- }
-
- for (Temp = StrStr (OriginalCommandLine, MasterEnvList)
- ; Temp != NULL
- ; Temp = StrStr (Temp+1, MasterEnvList)
- )
- {
- //
- // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)
- //
- if ((*(Temp-1) == L'%') && (*(Temp+StrLen (MasterEnvList)) == L'%') &&
- ((((Temp-OriginalCommandLine) > 2) && (*(Temp-2) != L'^')) || ((Temp-OriginalCommandLine) <= 2)))
- {
- NewSize += StrSize (EfiShellGetEnv (MasterEnvList));
- }
- }
- }
-
- //
- // now do the replacements...
- //
- NewCommandLine1 = AllocateZeroPool (NewSize);
- NewCommandLine2 = AllocateZeroPool (NewSize);
- ItemTemp = AllocateZeroPool (ItemSize+(2*sizeof (CHAR16)));
- if ((NewCommandLine1 == NULL) || (NewCommandLine2 == NULL) || (ItemTemp == NULL)) {
- SHELL_FREE_NON_NULL (NewCommandLine1);
- SHELL_FREE_NON_NULL (NewCommandLine2);
- SHELL_FREE_NON_NULL (ItemTemp);
- return (NULL);
- }
-
- CopyMem (NewCommandLine1, OriginalCommandLine, StrSize (OriginalCommandLine));
-
- for (MasterEnvList = EfiShellGetEnv (NULL)
- ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL
- ; MasterEnvList += StrLen (MasterEnvList) + 1
- )
- {
- StrCpyS (
- ItemTemp,
- ((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)),
- L"%"
- );
- StrCatS (
- ItemTemp,
- ((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)),
- MasterEnvList
- );
- StrCatS (
- ItemTemp,
- ((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)),
- L"%"
- );
- ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv (MasterEnvList), TRUE, FALSE);
- StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), NewCommandLine2);
- }
-
- if (CurrentScriptFile != NULL) {
- for (AliasListNode = (ALIAS_LIST *)GetFirstNode (&CurrentScriptFile->SubstList)
- ; !IsNull (&CurrentScriptFile->SubstList, &AliasListNode->Link)
- ; AliasListNode = (ALIAS_LIST *)GetNextNode (&CurrentScriptFile->SubstList, &AliasListNode->Link)
- )
- {
- ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);
- StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), NewCommandLine2);
- }
- }
-
- //
- // Remove non-existent environment variables
- //
- StripUnreplacedEnvironmentVariables (NewCommandLine1);
-
- //
- // Now cleanup any straggler intentionally ignored "%" characters
- //
- ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);
- StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), NewCommandLine2);
-
- FreePool (NewCommandLine2);
- FreePool (ItemTemp);
-
- return (NewCommandLine1);
-}
-
-/**
- Internal function to run a command line with pipe usage.
-
- @param[in] CmdLine The pointer to the command line.
- @param[in] StdIn The pointer to the Standard input.
- @param[in] StdOut The pointer to the Standard output.
-
- @retval EFI_SUCCESS The split command is executed successfully.
- @retval other Some error occurs when executing the split command.
-**/
-EFI_STATUS
-RunSplitCommand (
- IN CONST CHAR16 *CmdLine,
- IN SHELL_FILE_HANDLE StdIn,
- IN SHELL_FILE_HANDLE StdOut
- )
-{
- EFI_STATUS Status;
- CHAR16 *NextCommandLine;
- CHAR16 *OurCommandLine;
- UINTN Size1;
- UINTN Size2;
- SPLIT_LIST *Split;
- SHELL_FILE_HANDLE TempFileHandle;
- BOOLEAN Unicode;
-
- ASSERT (StdOut == NULL);
-
- ASSERT (StrStr (CmdLine, L"|") != NULL);
-
- Status = EFI_SUCCESS;
- NextCommandLine = NULL;
- OurCommandLine = NULL;
- Size1 = 0;
- Size2 = 0;
-
- NextCommandLine = StrnCatGrow (&NextCommandLine, &Size1, StrStr (CmdLine, L"|")+1, 0);
- OurCommandLine = StrnCatGrow (&OurCommandLine, &Size2, CmdLine, StrStr (CmdLine, L"|") - CmdLine);
-
- if ((NextCommandLine == NULL) || (OurCommandLine == NULL)) {
- SHELL_FREE_NON_NULL (OurCommandLine);
- SHELL_FREE_NON_NULL (NextCommandLine);
- return (EFI_OUT_OF_RESOURCES);
- } else if ((StrStr (OurCommandLine, L"|") != NULL) || (Size1 == 0) || (Size2 == 0)) {
- SHELL_FREE_NON_NULL (OurCommandLine);
- SHELL_FREE_NON_NULL (NextCommandLine);
- return (EFI_INVALID_PARAMETER);
- } else if ((NextCommandLine[0] == L'a') &&
- ((NextCommandLine[1] == L' ') || (NextCommandLine[1] == CHAR_NULL))
- )
- {
- CopyMem (NextCommandLine, NextCommandLine+1, StrSize (NextCommandLine) - sizeof (NextCommandLine[0]));
- while (NextCommandLine[0] == L' ') {
- CopyMem (NextCommandLine, NextCommandLine+1, StrSize (NextCommandLine) - sizeof (NextCommandLine[0]));
- }
-
- if (NextCommandLine[0] == CHAR_NULL) {
- SHELL_FREE_NON_NULL (OurCommandLine);
- SHELL_FREE_NON_NULL (NextCommandLine);
- return (EFI_INVALID_PARAMETER);
- }
-
- Unicode = FALSE;
- } else {
- Unicode = TRUE;
- }
-
- //
- // make a SPLIT_LIST item and add to list
- //
- Split = AllocateZeroPool (sizeof (SPLIT_LIST));
- if (Split == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- Split->SplitStdIn = StdIn;
- Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (Unicode), NULL);
- ASSERT (Split->SplitStdOut != NULL);
- InsertHeadList (&ShellInfoObject.SplitList.Link, &Split->Link);
-
- Status = RunCommand (OurCommandLine);
-
- //
- // move the output from the first to the in to the second.
- //
- TempFileHandle = Split->SplitStdOut;
- if (Split->SplitStdIn == StdIn) {
- Split->SplitStdOut = NULL;
- } else {
- Split->SplitStdOut = Split->SplitStdIn;
- }
-
- Split->SplitStdIn = TempFileHandle;
- ShellInfoObject.NewEfiShellProtocol->SetFilePosition (Split->SplitStdIn, 0);
-
- if (!EFI_ERROR (Status)) {
- Status = RunCommand (NextCommandLine);
- }
-
- //
- // remove the top level from the ScriptList
- //
- ASSERT ((SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link) == Split);
- RemoveEntryList (&Split->Link);
-
- //
- // Note that the original StdIn is now the StdOut...
- //
- if (Split->SplitStdOut != NULL) {
- ShellInfoObject.NewEfiShellProtocol->CloseFile (Split->SplitStdOut);
- }
-
- if (Split->SplitStdIn != NULL) {
- ShellInfoObject.NewEfiShellProtocol->CloseFile (Split->SplitStdIn);
- }
-
- FreePool (Split);
- FreePool (NextCommandLine);
- FreePool (OurCommandLine);
-
- return (Status);
-}
-
-/**
- Take the original command line, substitute any variables, free
- the original string, return the modified copy.
-
- @param[in] CmdLine pointer to the command line to update.
-
- @retval EFI_SUCCESS the function was successful.
- @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
-**/
-EFI_STATUS
-ShellSubstituteVariables (
- IN CHAR16 **CmdLine
- )
-{
- CHAR16 *NewCmdLine;
-
- NewCmdLine = ShellConvertVariables (*CmdLine);
- SHELL_FREE_NON_NULL (*CmdLine);
- if (NewCmdLine == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- *CmdLine = NewCmdLine;
- return (EFI_SUCCESS);
-}
-
-/**
- Take the original command line, substitute any alias in the first group of space delimited characters, free
- the original string, return the modified copy.
-
- @param[in] CmdLine pointer to the command line to update.
-
- @retval EFI_SUCCESS the function was successful.
- @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
-**/
-EFI_STATUS
-ShellSubstituteAliases (
- IN CHAR16 **CmdLine
- )
-{
- CHAR16 *NewCmdLine;
- CHAR16 *CommandName;
- EFI_STATUS Status;
- UINTN PostAliasSize;
-
- ASSERT (CmdLine != NULL);
- ASSERT (*CmdLine != NULL);
-
- CommandName = NULL;
- if (StrStr ((*CmdLine), L" ") == NULL) {
- StrnCatGrow (&CommandName, NULL, (*CmdLine), 0);
- } else {
- StrnCatGrow (&CommandName, NULL, (*CmdLine), StrStr ((*CmdLine), L" ") - (*CmdLine));
- }
-
- //
- // This cannot happen 'inline' since the CmdLine can need extra space.
- //
- NewCmdLine = NULL;
- if (!ShellCommandIsCommandOnList (CommandName)) {
- //
- // Convert via alias
- //
- Status = ShellConvertAlias (&CommandName);
- if (EFI_ERROR (Status)) {
- return (Status);
- }
-
- PostAliasSize = 0;
- NewCmdLine = StrnCatGrow (&NewCmdLine, &PostAliasSize, CommandName, 0);
- if (NewCmdLine == NULL) {
- SHELL_FREE_NON_NULL (CommandName);
- SHELL_FREE_NON_NULL (*CmdLine);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- NewCmdLine = StrnCatGrow (&NewCmdLine, &PostAliasSize, StrStr ((*CmdLine), L" "), 0);
- if (NewCmdLine == NULL) {
- SHELL_FREE_NON_NULL (CommandName);
- SHELL_FREE_NON_NULL (*CmdLine);
- return (EFI_OUT_OF_RESOURCES);
- }
- } else {
- NewCmdLine = StrnCatGrow (&NewCmdLine, NULL, (*CmdLine), 0);
- }
-
- SHELL_FREE_NON_NULL (*CmdLine);
- SHELL_FREE_NON_NULL (CommandName);
-
- //
- // re-assign the passed in double pointer to point to our newly allocated buffer
- //
- *CmdLine = NewCmdLine;
-
- return (EFI_SUCCESS);
-}
-
-/**
- Takes the Argv[0] part of the command line and determine the meaning of it.
-
- @param[in] CmdName pointer to the command line to update.
-
- @retval Internal_Command The name is an internal command.
- @retval File_Sys_Change the name is a file system change.
- @retval Script_File_Name the name is a NSH script file.
- @retval Unknown_Invalid the name is unknown.
- @retval Efi_Application the name is an application (.EFI).
-**/
-SHELL_OPERATION_TYPES
-GetOperationType (
- IN CONST CHAR16 *CmdName
- )
-{
- CHAR16 *FileWithPath;
- CONST CHAR16 *TempLocation;
- CONST CHAR16 *TempLocation2;
-
- FileWithPath = NULL;
- //
- // test for an internal command.
- //
- if (ShellCommandIsCommandOnList (CmdName)) {
- return (Internal_Command);
- }
-
- //
- // Test for file system change request. anything ending with first : and cant have spaces.
- //
- if (CmdName[(StrLen (CmdName)-1)] == L':') {
- if ( (StrStr (CmdName, L" ") != NULL)
- || (StrLen (StrStr (CmdName, L":")) > 1)
- )
- {
- return (Unknown_Invalid);
- }
-
- return (File_Sys_Change);
- }
-
- //
- // Test for a file
- //
- if ((FileWithPath = ShellFindFilePathEx (CmdName, mExecutableExtensions)) != NULL) {
- //
- // See if that file has a script file extension
- //
- if (StrLen (FileWithPath) > 4) {
- TempLocation = FileWithPath+StrLen (FileWithPath)-4;
- TempLocation2 = mScriptExtension;
- if (StringNoCaseCompare ((VOID *)(&TempLocation), (VOID *)(&TempLocation2)) == 0) {
- SHELL_FREE_NON_NULL (FileWithPath);
- return (Script_File_Name);
- }
- }
-
- //
- // Was a file, but not a script. we treat this as an application.
- //
- SHELL_FREE_NON_NULL (FileWithPath);
- return (Efi_Application);
- }
-
- SHELL_FREE_NON_NULL (FileWithPath);
- //
- // No clue what this is... return invalid flag...
- //
- return (Unknown_Invalid);
-}
-
-/**
- Determine if the first item in a command line is valid.
-
- @param[in] CmdLine The command line to parse.
-
- @retval EFI_SUCCESS The item is valid.
- @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
- @retval EFI_NOT_FOUND The operation type is unknown or invalid.
-**/
-EFI_STATUS
-IsValidSplit (
- IN CONST CHAR16 *CmdLine
- )
-{
- CHAR16 *Temp;
- CHAR16 *FirstParameter;
- CHAR16 *TempWalker;
- EFI_STATUS Status;
-
- Temp = NULL;
-
- Temp = StrnCatGrow (&Temp, NULL, CmdLine, 0);
- if (Temp == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- FirstParameter = StrStr (Temp, L"|");
- if (FirstParameter != NULL) {
- *FirstParameter = CHAR_NULL;
- }
-
- FirstParameter = NULL;
-
- //
- // Process the command line
- //
- Status = ProcessCommandLineToFinal (&Temp);
-
- if (!EFI_ERROR (Status)) {
- FirstParameter = AllocateZeroPool (StrSize (CmdLine));
- if (FirstParameter == NULL) {
- SHELL_FREE_NON_NULL (Temp);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- TempWalker = (CHAR16 *)Temp;
- if (!EFI_ERROR (GetNextParameter (&TempWalker, &FirstParameter, StrSize (CmdLine), TRUE))) {
- if (GetOperationType (FirstParameter) == Unknown_Invalid) {
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
- SetLastError (SHELL_NOT_FOUND);
- Status = EFI_NOT_FOUND;
- }
- }
- }
-
- SHELL_FREE_NON_NULL (Temp);
- SHELL_FREE_NON_NULL (FirstParameter);
- return Status;
-}
-
-/**
- Determine if a command line contains with a split contains only valid commands.
-
- @param[in] CmdLine The command line to parse.
-
- @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.
- @retval EFI_ABORTED CmdLine has at least one invalid command or application.
-**/
-EFI_STATUS
-VerifySplit (
- IN CONST CHAR16 *CmdLine
- )
-{
- CONST CHAR16 *TempSpot;
- EFI_STATUS Status;
-
- //
- // If this was the only item, then get out
- //
- if (!ContainsSplit (CmdLine)) {
- return (EFI_SUCCESS);
- }
-
- //
- // Verify up to the pipe or end character
- //
- Status = IsValidSplit (CmdLine);
- if (EFI_ERROR (Status)) {
- return (Status);
- }
-
- //
- // recurse to verify the next item
- //
- TempSpot = FindFirstCharacter (CmdLine, L"|", L'^') + 1;
- if ((*TempSpot == L'a') &&
- ((*(TempSpot + 1) == L' ') || (*(TempSpot + 1) == CHAR_NULL))
- )
- {
- // If it's an ASCII pipe '|a'
- TempSpot += 1;
- }
-
- return (VerifySplit (TempSpot));
-}
-
-/**
- Process a split based operation.
-
- @param[in] CmdLine pointer to the command line to process
-
- @retval EFI_SUCCESS The operation was successful
- @return an error occurred.
-**/
-EFI_STATUS
-ProcessNewSplitCommandLine (
- IN CONST CHAR16 *CmdLine
- )
-{
- SPLIT_LIST *Split;
- EFI_STATUS Status;
-
- Status = VerifySplit (CmdLine);
- if (EFI_ERROR (Status)) {
- return (Status);
- }
-
- Split = NULL;
-
- //
- // are we in an existing split???
- //
- if (!IsListEmpty (&ShellInfoObject.SplitList.Link)) {
- Split = (SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link);
- }
-
- if (Split == NULL) {
- Status = RunSplitCommand (CmdLine, NULL, NULL);
- } else {
- Status = RunSplitCommand (CmdLine, Split->SplitStdIn, Split->SplitStdOut);
- }
-
- if (EFI_ERROR (Status)) {
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);
- }
-
- return (Status);
-}
-
-/**
- Handle a request to change the current file system.
-
- @param[in] CmdLine The passed in command line.
-
- @retval EFI_SUCCESS The operation was successful.
-**/
-EFI_STATUS
-ChangeMappedDrive (
- IN CONST CHAR16 *CmdLine
- )
-{
- EFI_STATUS Status;
-
- Status = EFI_SUCCESS;
-
- //
- // make sure we are the right operation
- //
- ASSERT (CmdLine[(StrLen (CmdLine)-1)] == L':' && StrStr (CmdLine, L" ") == NULL);
-
- //
- // Call the protocol API to do the work
- //
- Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir (NULL, CmdLine);
-
- //
- // Report any errors
- //
- if (EFI_ERROR (Status)) {
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine);
- }
-
- return (Status);
-}
-
-/**
- Reprocess the command line to direct all -? to the help command.
-
- if found, will add "help" as argv[0], and move the rest later.
-
- @param[in,out] CmdLine pointer to the command line to update
-**/
-EFI_STATUS
-DoHelpUpdate (
- IN OUT CHAR16 **CmdLine
- )
-{
- CHAR16 *CurrentParameter;
- CHAR16 *Walker;
- CHAR16 *NewCommandLine;
- EFI_STATUS Status;
- UINTN NewCmdLineSize;
-
- Status = EFI_SUCCESS;
-
- CurrentParameter = AllocateZeroPool (StrSize (*CmdLine));
- if (CurrentParameter == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- Walker = *CmdLine;
- while (Walker != NULL && *Walker != CHAR_NULL) {
- if (!EFI_ERROR (GetNextParameter (&Walker, &CurrentParameter, StrSize (*CmdLine), TRUE))) {
- if (StrStr (CurrentParameter, L"-?") == CurrentParameter) {
- CurrentParameter[0] = L' ';
- CurrentParameter[1] = L' ';
- NewCmdLineSize = StrSize (L"help ") + StrSize (*CmdLine);
- NewCommandLine = AllocateZeroPool (NewCmdLineSize);
- if (NewCommandLine == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- break;
- }
-
- //
- // We know the space is sufficient since we just calculated it.
- //
- StrnCpyS (NewCommandLine, NewCmdLineSize/sizeof (CHAR16), L"help ", 5);
- StrnCatS (NewCommandLine, NewCmdLineSize/sizeof (CHAR16), *CmdLine, StrLen (*CmdLine));
- SHELL_FREE_NON_NULL (*CmdLine);
- *CmdLine = NewCommandLine;
- break;
- }
- }
- }
-
- SHELL_FREE_NON_NULL (CurrentParameter);
-
- return (Status);
-}
-
-/**
- Function to update the shell variable "lasterror".
-
- @param[in] ErrorCode the error code to put into lasterror.
-**/
-EFI_STATUS
-SetLastError (
- IN CONST SHELL_STATUS ErrorCode
- )
-{
- CHAR16 LeString[19];
-
- if (sizeof (EFI_STATUS) == sizeof (UINT64)) {
- UnicodeSPrint (LeString, sizeof (LeString), L"0x%Lx", ErrorCode);
- } else {
- UnicodeSPrint (LeString, sizeof (LeString), L"0x%x", ErrorCode);
- }
-
- DEBUG_CODE (
- InternalEfiShellSetEnv (L"debuglasterror", LeString, TRUE);
- );
- InternalEfiShellSetEnv (L"lasterror", LeString, TRUE);
-
- return (EFI_SUCCESS);
-}
-
-/**
- Converts the command line to its post-processed form. this replaces variables and alias' per UEFI Shell spec.
-
- @param[in,out] CmdLine pointer to the command line to update
-
- @retval EFI_SUCCESS The operation was successful
- @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
- @return some other error occurred
-**/
-EFI_STATUS
-ProcessCommandLineToFinal (
- IN OUT CHAR16 **CmdLine
- )
-{
- EFI_STATUS Status;
-
- TrimSpaces (CmdLine);
-
- Status = ShellSubstituteAliases (CmdLine);
- if (EFI_ERROR (Status)) {
- return (Status);
- }
-
- TrimSpaces (CmdLine);
-
- Status = ShellSubstituteVariables (CmdLine);
- if (EFI_ERROR (Status)) {
- return (Status);
- }
-
- ASSERT (*CmdLine != NULL);
-
- TrimSpaces (CmdLine);
-
- //
- // update for help parsing
- //
- if (StrStr (*CmdLine, L"?") != NULL) {
- //
- // This may do nothing if the ? does not indicate help.
- // Save all the details for in the API below.
- //
- Status = DoHelpUpdate (CmdLine);
- }
-
- TrimSpaces (CmdLine);
-
- return (EFI_SUCCESS);
-}
-
-/**
- Run an internal shell command.
-
- This API will update the shell's environment since these commands are libraries.
-
- @param[in] CmdLine the command line to run.
- @param[in] FirstParameter the first parameter on the command line
- @param[in] ParamProtocol the shell parameters protocol pointer
- @param[out] CommandStatus the status from the command line.
-
- @retval EFI_SUCCESS The command was completed.
- @retval EFI_ABORTED The command's operation was aborted.
-**/
-EFI_STATUS
-RunInternalCommand (
- IN CONST CHAR16 *CmdLine,
- IN CHAR16 *FirstParameter,
- IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
- OUT EFI_STATUS *CommandStatus
- )
-{
- EFI_STATUS Status;
- UINTN Argc;
- CHAR16 **Argv;
- SHELL_STATUS CommandReturnedStatus;
- BOOLEAN LastError;
- CHAR16 *Walker;
- CHAR16 *NewCmdLine;
-
- NewCmdLine = AllocateCopyPool (StrSize (CmdLine), CmdLine);
- if (NewCmdLine == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL; Walker++) {
- if ((*Walker == L'^') && (*(Walker+1) == L'#')) {
- CopyMem (Walker, Walker+1, StrSize (Walker) - sizeof (Walker[0]));
- }
- }
-
- //
- // get the argc and argv updated for internal commands
- //
- Status = UpdateArgcArgv (ParamProtocol, NewCmdLine, Internal_Command, &Argv, &Argc);
- if (!EFI_ERROR (Status)) {
- //
- // Run the internal command.
- //
- Status = ShellCommandRunCommandHandler (FirstParameter, &CommandReturnedStatus, &LastError);
-
- if (!EFI_ERROR (Status)) {
- if (CommandStatus != NULL) {
- if (CommandReturnedStatus != SHELL_SUCCESS) {
- *CommandStatus = (EFI_STATUS)(CommandReturnedStatus | MAX_BIT);
- } else {
- *CommandStatus = EFI_SUCCESS;
- }
- }
-
- //
- // Update last error status.
- // some commands do not update last error.
- //
- if (LastError) {
- SetLastError (CommandReturnedStatus);
- }
-
- //
- // Pass thru the exitcode from the app.
- //
- if (ShellCommandGetExit ()) {
- //
- // An Exit was requested ("exit" command), pass its value up.
- //
- Status = CommandReturnedStatus;
- } else if ((CommandReturnedStatus != SHELL_SUCCESS) && IsScriptOnlyCommand (FirstParameter)) {
- //
- // Always abort when a script only command fails for any reason
- //
- Status = EFI_ABORTED;
- } else if ((ShellCommandGetCurrentScriptFile () != NULL) && (CommandReturnedStatus == SHELL_ABORTED)) {
- //
- // Abort when in a script and a command aborted
- //
- Status = EFI_ABORTED;
- }
- }
- }
-
- //
- // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
- // This is safe even if the update API failed. In this case, it may be a no-op.
- //
- RestoreArgcArgv (ParamProtocol, &Argv, &Argc);
-
- //
- // If a script is running and the command is not a script only command, then
- // change return value to success so the script won't halt (unless aborted).
- //
- // Script only commands have to be able halt the script since the script will
- // not operate if they are failing.
- //
- if ( (ShellCommandGetCurrentScriptFile () != NULL)
- && !IsScriptOnlyCommand (FirstParameter)
- && (Status != EFI_ABORTED)
- )
- {
- Status = EFI_SUCCESS;
- }
-
- FreePool (NewCmdLine);
- return (Status);
-}
-
-/**
- Function to run the command or file.
-
- @param[in] Type the type of operation being run.
- @param[in] CmdLine the command line to run.
- @param[in] FirstParameter the first parameter on the command line
- @param[in] ParamProtocol the shell parameters protocol pointer
- @param[out] CommandStatus the status from the command line.
-
- @retval EFI_SUCCESS The command was completed.
- @retval EFI_ABORTED The command's operation was aborted.
-**/
-EFI_STATUS
-RunCommandOrFile (
- IN SHELL_OPERATION_TYPES Type,
- IN CONST CHAR16 *CmdLine,
- IN CHAR16 *FirstParameter,
- IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
- OUT EFI_STATUS *CommandStatus
- )
-{
- EFI_STATUS Status;
- EFI_STATUS StartStatus;
- CHAR16 *CommandWithPath;
- CHAR16 *FullCommandWithPath;
- EFI_DEVICE_PATH_PROTOCOL *DevPath;
- SHELL_STATUS CalleeExitStatus;
-
- Status = EFI_SUCCESS;
- CommandWithPath = NULL;
- DevPath = NULL;
- CalleeExitStatus = SHELL_INVALID_PARAMETER;
-
- switch (Type) {
- case Internal_Command:
- Status = RunInternalCommand (CmdLine, FirstParameter, ParamProtocol, CommandStatus);
- break;
- case Script_File_Name:
- case Efi_Application:
- //
- // Process a fully qualified path
- //
- if (StrStr (FirstParameter, L":") != NULL) {
- ASSERT (CommandWithPath == NULL);
- if (ShellIsFile (FirstParameter) == EFI_SUCCESS) {
- CommandWithPath = StrnCatGrow (&CommandWithPath, NULL, FirstParameter, 0);
- }
- }
-
- //
- // Process a relative path and also check in the path environment variable
- //
- if (CommandWithPath == NULL) {
- CommandWithPath = ShellFindFilePathEx (FirstParameter, mExecutableExtensions);
- }
-
- //
- // This should be impossible now.
- //
- ASSERT (CommandWithPath != NULL);
-
- //
- // Make sure that path is not just a directory (or not found)
- //
- if (!EFI_ERROR (ShellIsDirectory (CommandWithPath))) {
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
- SetLastError (SHELL_NOT_FOUND);
- }
-
- switch (Type) {
- case Script_File_Name:
- FullCommandWithPath = FullyQualifyPath (CommandWithPath);
- if (FullCommandWithPath == NULL) {
- Status = RunScriptFile (CommandWithPath, NULL, CmdLine, ParamProtocol);
- } else {
- Status = RunScriptFile (FullCommandWithPath, NULL, CmdLine, ParamProtocol);
- FreePool (FullCommandWithPath);
- }
-
- break;
- case Efi_Application:
- //
- // Get the device path of the application image
- //
- DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath (CommandWithPath);
- if (DevPath == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- break;
- }
-
- //
- // Execute the device path
- //
- Status = InternalShellExecuteDevicePath (
- &gImageHandle,
- DevPath,
- CmdLine,
- NULL,
- &StartStatus
- );
-
- SHELL_FREE_NON_NULL (DevPath);
-
- if (EFI_ERROR (Status)) {
- CalleeExitStatus = (SHELL_STATUS)(Status & (~MAX_BIT));
- } else {
- CalleeExitStatus = (SHELL_STATUS)StartStatus;
- }
-
- if (CommandStatus != NULL) {
- *CommandStatus = CalleeExitStatus;
- }
-
- //
- // Update last error status.
- //
- // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
- SetLastError (CalleeExitStatus);
- break;
- default:
- //
- // Do nothing.
- //
- break;
- }
-
- break;
- default:
- //
- // Do nothing.
- //
- break;
- }
-
- SHELL_FREE_NON_NULL (CommandWithPath);
-
- return (Status);
-}
-
-/**
- Function to setup StdIn, StdErr, StdOut, and then run the command or file.
-
- @param[in] Type the type of operation being run.
- @param[in] CmdLine the command line to run.
- @param[in] FirstParameter the first parameter on the command line.
- @param[in] ParamProtocol the shell parameters protocol pointer
- @param[out] CommandStatus the status from the command line.
-
- @retval EFI_SUCCESS The command was completed.
- @retval EFI_ABORTED The command's operation was aborted.
-**/
-EFI_STATUS
-SetupAndRunCommandOrFile (
- IN SHELL_OPERATION_TYPES Type,
- IN CHAR16 *CmdLine,
- IN CHAR16 *FirstParameter,
- IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
- OUT EFI_STATUS *CommandStatus
- )
-{
- EFI_STATUS Status;
- SHELL_FILE_HANDLE OriginalStdIn;
- SHELL_FILE_HANDLE OriginalStdOut;
- SHELL_FILE_HANDLE OriginalStdErr;
- SYSTEM_TABLE_INFO OriginalSystemTableInfo;
- CONST SCRIPT_FILE *ConstScriptFile;
-
- //
- // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
- //
- Status = UpdateStdInStdOutStdErr (ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
-
- //
- // The StdIn, StdOut, and StdErr are set up.
- // Now run the command, script, or application
- //
- if (!EFI_ERROR (Status)) {
- TrimSpaces (&CmdLine);
- Status = RunCommandOrFile (Type, CmdLine, FirstParameter, ParamProtocol, CommandStatus);
- }
-
- //
- // Now print errors
- //
- if (EFI_ERROR (Status)) {
- ConstScriptFile = ShellCommandGetCurrentScriptFile ();
- if ((ConstScriptFile == NULL) || (ConstScriptFile->CurrentCommand == NULL)) {
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID *)(Status));
- } else {
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR_SCRIPT), ShellInfoObject.HiiHandle, (VOID *)(Status), ConstScriptFile->CurrentCommand->Line);
- }
- }
-
- //
- // put back the original StdIn, StdOut, and StdErr
- //
- RestoreStdInStdOutStdErr (ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
-
- return (Status);
-}
-
-/**
- Function will process and run a command line.
-
- This will determine if the command line represents an internal shell
- command or dispatch an external application.
-
- @param[in] CmdLine The command line to parse.
- @param[out] CommandStatus The status from the command line.
-
- @retval EFI_SUCCESS The command was completed.
- @retval EFI_ABORTED The command's operation was aborted.
-**/
-EFI_STATUS
-RunShellCommand (
- IN CONST CHAR16 *CmdLine,
- OUT EFI_STATUS *CommandStatus
- )
-{
- EFI_STATUS Status;
- CHAR16 *CleanOriginal;
- CHAR16 *FirstParameter;
- CHAR16 *TempWalker;
- SHELL_OPERATION_TYPES Type;
- CONST CHAR16 *CurDir;
-
- ASSERT (CmdLine != NULL);
- if (StrLen (CmdLine) == 0) {
- return (EFI_SUCCESS);
- }
-
- Status = EFI_SUCCESS;
- CleanOriginal = NULL;
-
- CleanOriginal = StrnCatGrow (&CleanOriginal, NULL, CmdLine, 0);
- if (CleanOriginal == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- TrimSpaces (&CleanOriginal);
-
- //
- // NULL out comments (leveraged from RunScriptFileHandle() ).
- // The # character on a line is used to denote that all characters on the same line
- // and to the right of the # are to be ignored by the shell.
- // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.
- //
- for (TempWalker = CleanOriginal; TempWalker != NULL && *TempWalker != CHAR_NULL; TempWalker++) {
- if (*TempWalker == L'^') {
- if (*(TempWalker + 1) == L'#') {
- TempWalker++;
- }
- } else if (*TempWalker == L'#') {
- *TempWalker = CHAR_NULL;
- }
- }
-
- TrimSpaces (&CleanOriginal);
-
- //
- // Handle case that passed in command line is just 1 or more " " characters.
- //
- if (StrLen (CleanOriginal) == 0) {
- SHELL_FREE_NON_NULL (CleanOriginal);
- return (EFI_SUCCESS);
- }
-
- Status = ProcessCommandLineToFinal (&CleanOriginal);
- if (EFI_ERROR (Status)) {
- SHELL_FREE_NON_NULL (CleanOriginal);
- return (Status);
- }
-
- //
- // We don't do normal processing with a split command line (output from one command input to another)
- //
- if (ContainsSplit (CleanOriginal)) {
- Status = ProcessNewSplitCommandLine (CleanOriginal);
- SHELL_FREE_NON_NULL (CleanOriginal);
- return (Status);
- }
-
- //
- // We need the first parameter information so we can determine the operation type
- //
- FirstParameter = AllocateZeroPool (StrSize (CleanOriginal));
- if (FirstParameter == NULL) {
- SHELL_FREE_NON_NULL (CleanOriginal);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- TempWalker = CleanOriginal;
- if (!EFI_ERROR (GetNextParameter (&TempWalker, &FirstParameter, StrSize (CleanOriginal), TRUE))) {
- //
- // Depending on the first parameter we change the behavior
- //
- switch (Type = GetOperationType (FirstParameter)) {
- case File_Sys_Change:
- Status = ChangeMappedDrive (FirstParameter);
- break;
- case Internal_Command:
- case Script_File_Name:
- case Efi_Application:
- Status = SetupAndRunCommandOrFile (Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol, CommandStatus);
- break;
- default:
- //
- // Whatever was typed, it was invalid.
- //
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
- SetLastError (SHELL_NOT_FOUND);
- break;
- }
- } else {
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
- SetLastError (SHELL_NOT_FOUND);
- }
-
- //
- // Check whether the current file system still exists. If not exist, we need update "cwd" and gShellCurMapping.
- //
- CurDir = EfiShellGetCurDir (NULL);
- if (CurDir != NULL) {
- if (EFI_ERROR (ShellFileExists (CurDir))) {
- //
- // EfiShellSetCurDir() cannot set current directory to NULL.
- // EfiShellSetEnv() is not allowed to set the "cwd" variable.
- // Only InternalEfiShellSetEnv () is allowed setting the "cwd" variable.
- //
- InternalEfiShellSetEnv (L"cwd", NULL, TRUE);
- gShellCurMapping = NULL;
- }
- }
-
- SHELL_FREE_NON_NULL (CleanOriginal);
- SHELL_FREE_NON_NULL (FirstParameter);
-
- return (Status);
-}
-
-/**
- Function will process and run a command line.
-
- This will determine if the command line represents an internal shell
- command or dispatch an external application.
-
- @param[in] CmdLine The command line to parse.
-
- @retval EFI_SUCCESS The command was completed.
- @retval EFI_ABORTED The command's operation was aborted.
-**/
-EFI_STATUS
-RunCommand (
- IN CONST CHAR16 *CmdLine
- )
-{
- return (RunShellCommand (CmdLine, NULL));
-}
-
-/**
- Function to process a NSH script file via SHELL_FILE_HANDLE.
-
- @param[in] Handle The handle to the already opened file.
- @param[in] Name The name of the script file.
-
- @retval EFI_SUCCESS the script completed successfully
-**/
-EFI_STATUS
-RunScriptFileHandle (
- IN SHELL_FILE_HANDLE Handle,
- IN CONST CHAR16 *Name
- )
-{
- EFI_STATUS Status;
- SCRIPT_FILE *NewScriptFile;
- UINTN LoopVar;
- UINTN PrintBuffSize;
- CHAR16 *CommandLine;
- CHAR16 *CommandLine2;
- CHAR16 *CommandLine3;
- SCRIPT_COMMAND_LIST *LastCommand;
- BOOLEAN Ascii;
- BOOLEAN PreScriptEchoState;
- BOOLEAN PreCommandEchoState;
- CONST CHAR16 *CurDir;
- UINTN LineCount;
- CHAR16 LeString[50];
- LIST_ENTRY OldBufferList;
-
- ASSERT (!ShellCommandGetScriptExit ());
-
- PreScriptEchoState = ShellCommandGetEchoState ();
- PrintBuffSize = PcdGet32 (PcdShellPrintBufferSize);
-
- NewScriptFile = (SCRIPT_FILE *)AllocateZeroPool (sizeof (SCRIPT_FILE));
- if (NewScriptFile == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- //
- // Set up the name
- //
- ASSERT (NewScriptFile->ScriptName == NULL);
- NewScriptFile->ScriptName = StrnCatGrow (&NewScriptFile->ScriptName, NULL, Name, 0);
- if (NewScriptFile->ScriptName == NULL) {
- DeleteScriptFileStruct (NewScriptFile);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- //
- // Save the parameters (used to replace %0 to %9 later on)
- //
- NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;
- if (NewScriptFile->Argc != 0) {
- NewScriptFile->Argv = (CHAR16 **)AllocateZeroPool (NewScriptFile->Argc * sizeof (CHAR16 *));
- if (NewScriptFile->Argv == NULL) {
- DeleteScriptFileStruct (NewScriptFile);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- //
- // Put the full path of the script file into Argv[0] as required by section
- // 3.6.2 of version 2.2 of the shell specification.
- //
- NewScriptFile->Argv[0] = StrnCatGrow (&NewScriptFile->Argv[0], NULL, NewScriptFile->ScriptName, 0);
- for (LoopVar = 1; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {
- ASSERT (NewScriptFile->Argv[LoopVar] == NULL);
- NewScriptFile->Argv[LoopVar] = StrnCatGrow (&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);
- if (NewScriptFile->Argv[LoopVar] == NULL) {
- DeleteScriptFileStruct (NewScriptFile);
- return (EFI_OUT_OF_RESOURCES);
- }
- }
- } else {
- NewScriptFile->Argv = NULL;
- }
-
- InitializeListHead (&NewScriptFile->CommandList);
- InitializeListHead (&NewScriptFile->SubstList);
-
- //
- // Now build the list of all script commands.
- //
- LineCount = 0;
- while (!ShellFileHandleEof (Handle)) {
- CommandLine = ShellFileHandleReturnLine (Handle, &Ascii);
- LineCount++;
- if ((CommandLine == NULL) || (StrLen (CommandLine) == 0) || (CommandLine[0] == '#')) {
- SHELL_FREE_NON_NULL (CommandLine);
- continue;
- }
-
- NewScriptFile->CurrentCommand = AllocateZeroPool (sizeof (SCRIPT_COMMAND_LIST));
- if (NewScriptFile->CurrentCommand == NULL) {
- SHELL_FREE_NON_NULL (CommandLine);
- DeleteScriptFileStruct (NewScriptFile);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- NewScriptFile->CurrentCommand->Cl = CommandLine;
- NewScriptFile->CurrentCommand->Data = NULL;
- NewScriptFile->CurrentCommand->Line = LineCount;
-
- InsertTailList (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
- }
-
- //
- // Add this as the topmost script file
- //
- ShellCommandSetNewScript (NewScriptFile);
-
- //
- // Now enumerate through the commands and run each one.
- //
- CommandLine = AllocateZeroPool (PrintBuffSize);
- if (CommandLine == NULL) {
- DeleteScriptFileStruct (NewScriptFile);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- CommandLine2 = AllocateZeroPool (PrintBuffSize);
- if (CommandLine2 == NULL) {
- FreePool (CommandLine);
- DeleteScriptFileStruct (NewScriptFile);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode (&NewScriptFile->CommandList)
- ; !IsNull (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)
- ; // conditional increment in the body of the loop
- )
- {
- ASSERT (CommandLine2 != NULL);
- StrnCpyS (
- CommandLine2,
- PrintBuffSize/sizeof (CHAR16),
- NewScriptFile->CurrentCommand->Cl,
- PrintBuffSize/sizeof (CHAR16) - 1
- );
-
- SaveBufferList (&OldBufferList);
-
- //
- // NULL out comments
- //
- for (CommandLine3 = CommandLine2; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL; CommandLine3++) {
- if (*CommandLine3 == L'^') {
- if ( *(CommandLine3+1) == L':') {
- CopyMem (CommandLine3, CommandLine3+1, StrSize (CommandLine3) - sizeof (CommandLine3[0]));
- } else if (*(CommandLine3+1) == L'#') {
- CommandLine3++;
- }
- } else if (*CommandLine3 == L'#') {
- *CommandLine3 = CHAR_NULL;
- }
- }
-
- if ((CommandLine2 != NULL) && (StrLen (CommandLine2) >= 1)) {
- //
- // Due to variability in starting the find and replace action we need to have both buffers the same.
- //
- StrnCpyS (
- CommandLine,
- PrintBuffSize/sizeof (CHAR16),
- CommandLine2,
- PrintBuffSize/sizeof (CHAR16) - 1
- );
-
- //
- // Remove the %0 to %9 from the command line (if we have some arguments)
- //
- if (NewScriptFile->Argv != NULL) {
- switch (NewScriptFile->Argc) {
- default:
- Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%9", NewScriptFile->Argv[9], FALSE, FALSE);
- ASSERT_EFI_ERROR (Status);
- case 9:
- Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%8", NewScriptFile->Argv[8], FALSE, FALSE);
- ASSERT_EFI_ERROR (Status);
- case 8:
- Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%7", NewScriptFile->Argv[7], FALSE, FALSE);
- ASSERT_EFI_ERROR (Status);
- case 7:
- Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%6", NewScriptFile->Argv[6], FALSE, FALSE);
- ASSERT_EFI_ERROR (Status);
- case 6:
- Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%5", NewScriptFile->Argv[5], FALSE, FALSE);
- ASSERT_EFI_ERROR (Status);
- case 5:
- Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%4", NewScriptFile->Argv[4], FALSE, FALSE);
- ASSERT_EFI_ERROR (Status);
- case 4:
- Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%3", NewScriptFile->Argv[3], FALSE, FALSE);
- ASSERT_EFI_ERROR (Status);
- case 3:
- Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%2", NewScriptFile->Argv[2], FALSE, FALSE);
- ASSERT_EFI_ERROR (Status);
- case 2:
- Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%1", NewScriptFile->Argv[1], FALSE, FALSE);
- ASSERT_EFI_ERROR (Status);
- case 1:
- Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%0", NewScriptFile->Argv[0], FALSE, FALSE);
- ASSERT_EFI_ERROR (Status);
- break;
- case 0:
- break;
- }
- }
-
- Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%1", L"\"\"", FALSE, FALSE);
- Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%2", L"\"\"", FALSE, FALSE);
- Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%3", L"\"\"", FALSE, FALSE);
- Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%4", L"\"\"", FALSE, FALSE);
- Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%5", L"\"\"", FALSE, FALSE);
- Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%6", L"\"\"", FALSE, FALSE);
- Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%7", L"\"\"", FALSE, FALSE);
- Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%8", L"\"\"", FALSE, FALSE);
- Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%9", L"\"\"", FALSE, FALSE);
-
- StrnCpyS (
- CommandLine2,
- PrintBuffSize/sizeof (CHAR16),
- CommandLine,
- PrintBuffSize/sizeof (CHAR16) - 1
- );
-
- LastCommand = NewScriptFile->CurrentCommand;
-
- for (CommandLine3 = CommandLine2; CommandLine3[0] == L' '; CommandLine3++) {
- }
-
- if ((CommandLine3 != NULL) && (CommandLine3[0] == L':')) {
- //
- // This line is a goto target / label
- //
- } else {
- if ((CommandLine3 != NULL) && (StrLen (CommandLine3) > 0)) {
- if (CommandLine3[0] == L'@') {
- //
- // We need to save the current echo state
- // and disable echo for just this command.
- //
- PreCommandEchoState = ShellCommandGetEchoState ();
- ShellCommandSetEchoState (FALSE);
- Status = RunCommand (CommandLine3+1);
-
- //
- // If command was "@echo -off" or "@echo -on" then don't restore echo state
- //
- if ((StrCmp (L"@echo -off", CommandLine3) != 0) &&
- (StrCmp (L"@echo -on", CommandLine3) != 0))
- {
- //
- // Now restore the pre-'@' echo state.
- //
- ShellCommandSetEchoState (PreCommandEchoState);
- }
- } else {
- if (ShellCommandGetEchoState ()) {
- CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv (L"cwd");
- if ((CurDir != NULL) && (StrLen (CurDir) > 1)) {
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
- } else {
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
- }
-
- ShellPrintEx (-1, -1, L"%s\r\n", CommandLine2);
- }
-
- Status = RunCommand (CommandLine3);
- }
- }
-
- if (ShellCommandGetScriptExit ()) {
- //
- // ShellCommandGetExitCode() always returns a UINT64
- //
- UnicodeSPrint (LeString, sizeof (LeString), L"0x%Lx", ShellCommandGetExitCode ());
- DEBUG_CODE (
- InternalEfiShellSetEnv (L"debuglasterror", LeString, TRUE);
- );
- InternalEfiShellSetEnv (L"lasterror", LeString, TRUE);
-
- ShellCommandRegisterExit (FALSE, 0);
- Status = EFI_SUCCESS;
- RestoreBufferList (&OldBufferList);
- break;
- }
-
- if (ShellGetExecutionBreakFlag ()) {
- RestoreBufferList (&OldBufferList);
- break;
- }
-
- if (EFI_ERROR (Status)) {
- RestoreBufferList (&OldBufferList);
- break;
- }
-
- if (ShellCommandGetExit ()) {
- RestoreBufferList (&OldBufferList);
- break;
- }
- }
-
- //
- // If that commend did not update the CurrentCommand then we need to advance it...
- //
- if (LastCommand == NewScriptFile->CurrentCommand) {
- NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
- if (!IsNull (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
- NewScriptFile->CurrentCommand->Reset = TRUE;
- }
- }
- } else {
- NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
- if (!IsNull (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
- NewScriptFile->CurrentCommand->Reset = TRUE;
- }
- }
-
- RestoreBufferList (&OldBufferList);
- }
-
- FreePool (CommandLine);
- FreePool (CommandLine2);
- ShellCommandSetNewScript (NULL);
-
- //
- // Only if this was the last script reset the state.
- //
- if (ShellCommandGetCurrentScriptFile () == NULL) {
- ShellCommandSetEchoState (PreScriptEchoState);
- }
-
- return (EFI_SUCCESS);
-}
-
-/**
- Function to process a NSH script file.
-
- @param[in] ScriptPath Pointer to the script file name (including file system path).
- @param[in] Handle the handle of the script file already opened.
- @param[in] CmdLine the command line to run.
- @param[in] ParamProtocol the shell parameters protocol pointer
-
- @retval EFI_SUCCESS the script completed successfully
-**/
-EFI_STATUS
-RunScriptFile (
- IN CONST CHAR16 *ScriptPath,
- IN SHELL_FILE_HANDLE Handle OPTIONAL,
- IN CONST CHAR16 *CmdLine,
- IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol
- )
-{
- EFI_STATUS Status;
- SHELL_FILE_HANDLE FileHandle;
- UINTN Argc;
- CHAR16 **Argv;
-
- if (ShellIsFile (ScriptPath) != EFI_SUCCESS) {
- return (EFI_INVALID_PARAMETER);
- }
-
- //
- // get the argc and argv updated for scripts
- //
- Status = UpdateArgcArgv (ParamProtocol, CmdLine, Script_File_Name, &Argv, &Argc);
- if (!EFI_ERROR (Status)) {
- if (Handle == NULL) {
- //
- // open the file
- //
- Status = ShellOpenFileByName (ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);
- if (!EFI_ERROR (Status)) {
- //
- // run it
- //
- Status = RunScriptFileHandle (FileHandle, ScriptPath);
-
- //
- // now close the file
- //
- ShellCloseFile (&FileHandle);
- }
- } else {
- Status = RunScriptFileHandle (Handle, ScriptPath);
- }
- }
-
- //
- // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
- // This is safe even if the update API failed. In this case, it may be a no-op.
- //
- RestoreArgcArgv (ParamProtocol, &Argv, &Argc);
-
- return (Status);
-}
-
-/**
- Return the pointer to the first occurrence of any character from a list of characters.
-
- @param[in] String the string to parse
- @param[in] CharacterList the list of character to look for
- @param[in] EscapeCharacter An escape character to skip
-
- @return the location of the first character in the string
- @retval CHAR_NULL no instance of any character in CharacterList was found in String
-**/
-CONST CHAR16 *
-FindFirstCharacter (
- IN CONST CHAR16 *String,
- IN CONST CHAR16 *CharacterList,
- IN CONST CHAR16 EscapeCharacter
- )
-{
- UINT32 WalkChar;
- UINT32 WalkStr;
-
- for (WalkStr = 0; WalkStr < StrLen (String); WalkStr++) {
- if (String[WalkStr] == EscapeCharacter) {
- WalkStr++;
- continue;
- }
-
- for (WalkChar = 0; WalkChar < StrLen (CharacterList); WalkChar++) {
- if (String[WalkStr] == CharacterList[WalkChar]) {
- return (&String[WalkStr]);
- }
- }
- }
-
- return (String + StrLen (String));
-}
+/** @file + This is THE shell (application) + + Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.<BR> + Copyright 2015-2018 Dell Technologies.<BR> + Copyright (C) 2023, Apple Inc. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "Shell.h" + +// +// Initialize the global structure +// +SHELL_INFO ShellInfoObject = { + NULL, + NULL, + FALSE, + FALSE, + { + { + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + } + }, + 0, + NULL, + NULL + }, + { + { NULL,NULL }, NULL + }, + { + { + { NULL,NULL }, NULL + }, + 0, + 0, + TRUE + }, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + { + { NULL,NULL }, NULL, NULL + }, + { + { NULL,NULL }, NULL, NULL + }, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + FALSE +}; + +STATIC CONST CHAR16 mScriptExtension[] = L".NSH"; +STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI"; +STATIC CONST CHAR16 mStartupScript[] = L"startup.nsh"; +CONST CHAR16 mNoNestingEnvVarName[] = L"nonesting"; +CONST CHAR16 mNoNestingTrue[] = L"True"; +CONST CHAR16 mNoNestingFalse[] = L"False"; + +/** + Cleans off leading and trailing spaces and tabs. + + @param[in] String pointer to the string to trim them off. +**/ +EFI_STATUS +TrimSpaces ( + IN CHAR16 **String + ) +{ + ASSERT (String != NULL); + ASSERT (*String != NULL); + // + // Remove any spaces and tabs at the beginning of the (*String). + // + while (((*String)[0] == L' ') || ((*String)[0] == L'\t')) { + CopyMem ((*String), (*String)+1, StrSize ((*String)) - sizeof ((*String)[0])); + } + + // + // Remove any spaces and tabs at the end of the (*String). + // + while ((StrLen (*String) > 0) && (((*String)[StrLen ((*String))-1] == L' ') || ((*String)[StrLen ((*String))-1] == L'\t'))) { + (*String)[StrLen ((*String))-1] = CHAR_NULL; + } + + return (EFI_SUCCESS); +} + +/** + Parse for the next instance of one string within another string. Can optionally make sure that + the string was not escaped (^ character) per the shell specification. + + @param[in] SourceString The string to search within + @param[in] FindString The string to look for + @param[in] CheckForEscapeCharacter TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances +**/ +CHAR16 * +FindNextInstance ( + IN CONST CHAR16 *SourceString, + IN CONST CHAR16 *FindString, + IN CONST BOOLEAN CheckForEscapeCharacter + ) +{ + CHAR16 *Temp; + + if (SourceString == NULL) { + return (NULL); + } + + Temp = StrStr (SourceString, FindString); + + // + // If nothing found, or we don't care about escape characters + // + if ((Temp == NULL) || !CheckForEscapeCharacter) { + return (Temp); + } + + // + // If we found an escaped character, try again on the remainder of the string + // + if ((Temp > (SourceString)) && (*(Temp-1) == L'^')) { + return FindNextInstance (Temp+1, FindString, CheckForEscapeCharacter); + } + + // + // we found the right character + // + return (Temp); +} + +/** + Check whether the string between a pair of % is a valid environment variable name. + + @param[in] BeginPercent pointer to the first percent. + @param[in] EndPercent pointer to the last percent. + + @retval TRUE is a valid environment variable name. + @retval FALSE is NOT a valid environment variable name. +**/ +BOOLEAN +IsValidEnvironmentVariableName ( + IN CONST CHAR16 *BeginPercent, + IN CONST CHAR16 *EndPercent + ) +{ + CONST CHAR16 *Walker; + + Walker = NULL; + + ASSERT (BeginPercent != NULL); + ASSERT (EndPercent != NULL); + ASSERT (BeginPercent < EndPercent); + + if ((BeginPercent + 1) == EndPercent) { + return FALSE; + } + + for (Walker = BeginPercent + 1; Walker < EndPercent; Walker++) { + if ( + ((*Walker >= L'0') && (*Walker <= L'9')) || + ((*Walker >= L'A') && (*Walker <= L'Z')) || + ((*Walker >= L'a') && (*Walker <= L'z')) || + (*Walker == L'_') + ) + { + if ((Walker == BeginPercent + 1) && ((*Walker >= L'0') && (*Walker <= L'9'))) { + return FALSE; + } else { + continue; + } + } else { + return FALSE; + } + } + + return TRUE; +} + +/** + Determine if a command line contains a split operation + + @param[in] CmdLine The command line to parse. + + @retval TRUE CmdLine has a valid split. + @retval FALSE CmdLine does not have a valid split. +**/ +BOOLEAN +ContainsSplit ( + IN CONST CHAR16 *CmdLine + ) +{ + CONST CHAR16 *TempSpot; + CONST CHAR16 *FirstQuote; + CONST CHAR16 *SecondQuote; + + FirstQuote = FindNextInstance (CmdLine, L"\"", TRUE); + SecondQuote = NULL; + TempSpot = FindFirstCharacter (CmdLine, L"|", L'^'); + + if ((FirstQuote == NULL) || + (TempSpot == NULL) || + (TempSpot == CHAR_NULL) || + (FirstQuote > TempSpot) + ) + { + return (BOOLEAN)((TempSpot != NULL) && (*TempSpot != CHAR_NULL)); + } + + while ((TempSpot != NULL) && (*TempSpot != CHAR_NULL)) { + if ((FirstQuote == NULL) || (FirstQuote > TempSpot)) { + break; + } + + SecondQuote = FindNextInstance (FirstQuote + 1, L"\"", TRUE); + if (SecondQuote == NULL) { + break; + } + + if (SecondQuote < TempSpot) { + FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE); + continue; + } else { + FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE); + TempSpot = FindFirstCharacter (TempSpot + 1, L"|", L'^'); + continue; + } + } + + return (BOOLEAN)((TempSpot != NULL) && (*TempSpot != CHAR_NULL)); +} + +/** + Function to start monitoring for CTRL-S using SimpleTextInputEx. This + feature's enabled state was not known when the shell initially launched. + + @retval EFI_SUCCESS The feature is enabled. + @retval EFI_OUT_OF_RESOURCES There is not enough memory available. +**/ +EFI_STATUS +InternalEfiShellStartCtrlSMonitor ( + VOID + ) +{ + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx; + EFI_KEY_DATA KeyData; + EFI_STATUS Status; + + Status = gBS->OpenProtocol ( + gST->ConsoleInHandle, + &gEfiSimpleTextInputExProtocolGuid, + (VOID **)&SimpleEx, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_SHELL_NO_IN_EX), + ShellInfoObject.HiiHandle + ); + return (EFI_SUCCESS); + } + + KeyData.KeyState.KeyToggleState = 0; + KeyData.Key.ScanCode = 0; + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED; + KeyData.Key.UnicodeChar = L's'; + + Status = SimpleEx->RegisterKeyNotify ( + SimpleEx, + &KeyData, + NotificationFunction, + &ShellInfoObject.CtrlSNotifyHandle1 + ); + + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED; + if (!EFI_ERROR (Status)) { + Status = SimpleEx->RegisterKeyNotify ( + SimpleEx, + &KeyData, + NotificationFunction, + &ShellInfoObject.CtrlSNotifyHandle2 + ); + } + + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED; + KeyData.Key.UnicodeChar = 19; + + if (!EFI_ERROR (Status)) { + Status = SimpleEx->RegisterKeyNotify ( + SimpleEx, + &KeyData, + NotificationFunction, + &ShellInfoObject.CtrlSNotifyHandle3 + ); + } + + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED; + if (!EFI_ERROR (Status)) { + Status = SimpleEx->RegisterKeyNotify ( + SimpleEx, + &KeyData, + NotificationFunction, + &ShellInfoObject.CtrlSNotifyHandle4 + ); + } + + return (Status); +} + +/** + The entry point for the application. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + CHAR16 *TempString; + UINTN Size; + EFI_HANDLE ConInHandle; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *OldConIn; + SPLIT_LIST *Split; + + if (PcdGet8 (PcdShellSupportLevel) > 3) { + return (EFI_UNSUPPORTED); + } + + // + // Clear the screen + // + Status = gST->ConOut->ClearScreen (gST->ConOut); + if (EFI_ERROR (Status)) { + return (Status); + } + + // + // Populate the global structure from PCDs + // + ShellInfoObject.ImageDevPath = NULL; + ShellInfoObject.FileDevPath = NULL; + ShellInfoObject.PageBreakEnabled = PcdGetBool (PcdShellPageBreakDefault); + ShellInfoObject.ViewingSettings.InsertMode = PcdGetBool (PcdShellInsertModeDefault); + ShellInfoObject.LogScreenCount = PcdGet8 (PcdShellScreenLogCount); + + // + // verify we dont allow for spec violation + // + ASSERT (ShellInfoObject.LogScreenCount >= 3); + + // + // Initialize the LIST ENTRY objects... + // + InitializeListHead (&ShellInfoObject.BufferToFreeList.Link); + InitializeListHead (&ShellInfoObject.ViewingSettings.CommandHistory.Link); + InitializeListHead (&ShellInfoObject.SplitList.Link); + + // + // Check PCDs for optional features that are not implemented yet. + // + if ( PcdGetBool (PcdShellSupportOldProtocols) + || !FeaturePcdGet (PcdShellRequireHiiPlatform) + || FeaturePcdGet (PcdShellSupportFrameworkHii) + ) + { + return (EFI_UNSUPPORTED); + } + + // + // turn off the watchdog timer + // + gBS->SetWatchdogTimer (0, 0, 0, NULL); + + // + // install our console logger. This will keep a log of the output for back-browsing + // + Status = ConsoleLoggerInstall (ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo); + if (!EFI_ERROR (Status)) { + // + // Enable the cursor to be visible + // + gST->ConOut->EnableCursor (gST->ConOut, TRUE); + + // + // If supporting EFI 1.1 we need to install HII protocol + // only do this if PcdShellRequireHiiPlatform == FALSE + // + // remove EFI_UNSUPPORTED check above when complete. + /// @todo add support for Framework HII + + // + // install our (solitary) HII package + // + ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL); + if (ShellInfoObject.HiiHandle == NULL) { + if (PcdGetBool (PcdShellSupportFrameworkHii)) { + /// @todo Add our package into Framework HII + } + + if (ShellInfoObject.HiiHandle == NULL) { + Status = EFI_NOT_STARTED; + goto FreeResources; + } + } + + // + // create and install the EfiShellParametersProtocol + // + Status = CreatePopulateInstallShellParametersProtocol (&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance); + ASSERT_EFI_ERROR (Status); + ASSERT (ShellInfoObject.NewShellParametersProtocol != NULL); + + // + // create and install the EfiShellProtocol + // + Status = CreatePopulateInstallShellProtocol (&ShellInfoObject.NewEfiShellProtocol); + ASSERT_EFI_ERROR (Status); + ASSERT (ShellInfoObject.NewEfiShellProtocol != NULL); + + // + // Now initialize the shell library (it requires Shell Parameters protocol) + // + Status = ShellInitialize (); + ASSERT_EFI_ERROR (Status); + + Status = CommandInit (); + ASSERT_EFI_ERROR (Status); + + Status = ShellInitEnvVarList (); + + // + // Check the command line + // + Status = ProcessCommandLine (); + if (EFI_ERROR (Status)) { + goto FreeResources; + } + + // + // If shell support level is >= 1 create the mappings and paths + // + if (PcdGet8 (PcdShellSupportLevel) >= 1) { + Status = ShellCommandCreateInitialMappingsAndPaths (); + } + + // + // Set the environment variable for nesting support + // + Size = 0; + TempString = NULL; + if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) { + // + // No change. require nesting in Shell Protocol Execute() + // + StrnCatGrow ( + &TempString, + &Size, + L"False", + 0 + ); + } else { + StrnCatGrow ( + &TempString, + &Size, + mNoNestingTrue, + 0 + ); + } + + Status = InternalEfiShellSetEnv (mNoNestingEnvVarName, TempString, TRUE); + SHELL_FREE_NON_NULL (TempString); + Size = 0; + + // + // save the device path for the loaded image and the device path for the filepath (under loaded image) + // These are where to look for the startup.nsh file + // + Status = GetDevicePathsForImageAndFile (&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath); + ASSERT_EFI_ERROR (Status); + + // + // Display the version + // + if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) { + ShellPrintHiiEx ( + 0, + gST->ConOut->Mode->CursorRow, + NULL, + STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL), + ShellInfoObject.HiiHandle, + SupportLevel[PcdGet8 (PcdShellSupportLevel)], + gEfiShellProtocol->MajorVersion, + gEfiShellProtocol->MinorVersion + ); + + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER), + ShellInfoObject.HiiHandle, + (CHAR16 *)PcdGetPtr (PcdShellSupplier) + ); + + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI), + ShellInfoObject.HiiHandle, + (gST->Hdr.Revision&0xffff0000)>>16, + (gST->Hdr.Revision&0x0000ffff), + gST->FirmwareVendor, + gST->FirmwareRevision + ); + } + + // + // Display the mapping + // + if ((PcdGet8 (PcdShellSupportLevel) >= 2) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) { + Status = RunCommand (L"map"); + ASSERT_EFI_ERROR (Status); + } + + // + // init all the built in alias' + // + Status = SetBuiltInAlias (); + ASSERT_EFI_ERROR (Status); + + // + // Initialize environment variables + // + if (ShellCommandGetProfileList () != NULL) { + Status = InternalEfiShellSetEnv (L"profiles", ShellCommandGetProfileList (), TRUE); + ASSERT_EFI_ERROR (Status); + } + + Size = 100; + TempString = AllocateZeroPool (Size); + + UnicodeSPrint (TempString, Size, L"%d", PcdGet8 (PcdShellSupportLevel)); + Status = InternalEfiShellSetEnv (L"uefishellsupport", TempString, TRUE); + ASSERT_EFI_ERROR (Status); + + UnicodeSPrint (TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion); + Status = InternalEfiShellSetEnv (L"uefishellversion", TempString, TRUE); + ASSERT_EFI_ERROR (Status); + + UnicodeSPrint (TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF); + Status = InternalEfiShellSetEnv (L"uefiversion", TempString, TRUE); + ASSERT_EFI_ERROR (Status); + + FreePool (TempString); + + if (!EFI_ERROR (Status)) { + if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) { + // + // Set up the event for CTRL-C monitoring... + // + Status = InernalEfiShellStartMonitor (); + } + + if (!EFI_ERROR (Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) { + // + // Set up the event for CTRL-S monitoring... + // + Status = InternalEfiShellStartCtrlSMonitor (); + } + + if (!EFI_ERROR (Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) { + // + // close off the gST->ConIn + // + OldConIn = gST->ConIn; + ConInHandle = gST->ConsoleInHandle; + gST->ConIn = CreateSimpleTextInOnFile ((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle); + } else { + OldConIn = NULL; + ConInHandle = NULL; + } + + if (!EFI_ERROR (Status) && (PcdGet8 (PcdShellSupportLevel) >= 1)) { + // + // process the startup script or launch the called app. + // + Status = DoStartupScript (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath); + } + + if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit () && ((PcdGet8 (PcdShellSupportLevel) >= 3) || PcdGetBool (PcdShellForceConsole)) && !EFI_ERROR (Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) { + // + // begin the UI waiting loop + // + do { + // + // clean out all the memory allocated for CONST <something> * return values + // between each shell prompt presentation + // + if (!IsListEmpty (&ShellInfoObject.BufferToFreeList.Link)) { + FreeBufferList (&ShellInfoObject.BufferToFreeList); + } + + // + // Reset page break back to default. + // + ShellInfoObject.PageBreakEnabled = PcdGetBool (PcdShellPageBreakDefault); + ASSERT (ShellInfoObject.ConsoleInfo != NULL); + ShellInfoObject.ConsoleInfo->Enabled = TRUE; + ShellInfoObject.ConsoleInfo->RowCounter = 0; + + // + // Display Prompt + // + Status = DoShellPrompt (); + } while (!ShellCommandGetExit ()); + } + + if ((OldConIn != NULL) && (ConInHandle != NULL)) { + CloseSimpleTextInOnFile (gST->ConIn); + gST->ConIn = OldConIn; + gST->ConsoleInHandle = ConInHandle; + } + } + } + +FreeResources: + // + // uninstall protocols / free memory / etc... + // + if (ShellInfoObject.UserBreakTimer != NULL) { + gBS->CloseEvent (ShellInfoObject.UserBreakTimer); + DEBUG_CODE ( + ShellInfoObject.UserBreakTimer = NULL; + ); + } + + if (ShellInfoObject.ImageDevPath != NULL) { + FreePool (ShellInfoObject.ImageDevPath); + DEBUG_CODE ( + ShellInfoObject.ImageDevPath = NULL; + ); + } + + if (ShellInfoObject.FileDevPath != NULL) { + FreePool (ShellInfoObject.FileDevPath); + DEBUG_CODE ( + ShellInfoObject.FileDevPath = NULL; + ); + } + + if (ShellInfoObject.NewShellParametersProtocol != NULL) { + CleanUpShellParametersProtocol (ShellInfoObject.NewShellParametersProtocol); + DEBUG_CODE ( + ShellInfoObject.NewShellParametersProtocol = NULL; + ); + } + + if (ShellInfoObject.NewEfiShellProtocol != NULL) { + if (ShellInfoObject.NewEfiShellProtocol->IsRootShell ()) { + InternalEfiShellSetEnv (L"cwd", NULL, TRUE); + } + + CleanUpShellEnvironment (ShellInfoObject.NewEfiShellProtocol); + DEBUG_CODE ( + ShellInfoObject.NewEfiShellProtocol = NULL; + ); + } + + if (!IsListEmpty (&ShellInfoObject.BufferToFreeList.Link)) { + FreeBufferList (&ShellInfoObject.BufferToFreeList); + } + + if (!IsListEmpty (&ShellInfoObject.SplitList.Link)) { + ASSERT (FALSE); /// @todo finish this de-allocation (free SplitStdIn/Out when needed). + + for ( Split = (SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link) + ; !IsNull (&ShellInfoObject.SplitList.Link, &Split->Link) + ; Split = (SPLIT_LIST *)GetNextNode (&ShellInfoObject.SplitList.Link, &Split->Link) + ) + { + RemoveEntryList (&Split->Link); + FreePool (Split); + } + + DEBUG_CODE ( + InitializeListHead (&ShellInfoObject.SplitList.Link); + ); + } + + if (ShellInfoObject.ShellInitSettings.FileName != NULL) { + FreePool (ShellInfoObject.ShellInitSettings.FileName); + DEBUG_CODE ( + ShellInfoObject.ShellInitSettings.FileName = NULL; + ); + } + + if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) { + FreePool (ShellInfoObject.ShellInitSettings.FileOptions); + DEBUG_CODE ( + ShellInfoObject.ShellInitSettings.FileOptions = NULL; + ); + } + + if (ShellInfoObject.HiiHandle != NULL) { + HiiRemovePackages (ShellInfoObject.HiiHandle); + DEBUG_CODE ( + ShellInfoObject.HiiHandle = NULL; + ); + } + + if (!IsListEmpty (&ShellInfoObject.ViewingSettings.CommandHistory.Link)) { + FreeBufferList (&ShellInfoObject.ViewingSettings.CommandHistory); + } + + ASSERT (ShellInfoObject.ConsoleInfo != NULL); + if (ShellInfoObject.ConsoleInfo != NULL) { + ConsoleLoggerUninstall (ShellInfoObject.ConsoleInfo); + FreePool (ShellInfoObject.ConsoleInfo); + DEBUG_CODE ( + ShellInfoObject.ConsoleInfo = NULL; + ); + } + + ShellFreeEnvVarList (); + + if (ShellCommandGetExit ()) { + return ((EFI_STATUS)ShellCommandGetExitCode ()); + } + + return (Status); +} + +/** + Sets all the alias' that were registered with the ShellCommandLib library. + + @retval EFI_SUCCESS all init commands were run successfully. +**/ +EFI_STATUS +SetBuiltInAlias ( + VOID + ) +{ + EFI_STATUS Status; + CONST ALIAS_LIST *List; + ALIAS_LIST *Node; + + // + // Get all the commands we want to run + // + List = ShellCommandGetInitAliasList (); + + // + // for each command in the List + // + for ( Node = (ALIAS_LIST *)GetFirstNode (&List->Link) + ; !IsNull (&List->Link, &Node->Link) + ; Node = (ALIAS_LIST *)GetNextNode (&List->Link, &Node->Link) + ) + { + // + // install the alias' + // + Status = InternalSetAlias (Node->CommandString, Node->Alias, TRUE); + ASSERT_EFI_ERROR (Status); + } + + return (EFI_SUCCESS); +} + +/** + Internal function to determine if 2 command names are really the same. + + @param[in] Command1 The pointer to the first command name. + @param[in] Command2 The pointer to the second command name. + + @retval TRUE The 2 command names are the same. + @retval FALSE The 2 command names are not the same. +**/ +BOOLEAN +IsCommand ( + IN CONST CHAR16 *Command1, + IN CONST CHAR16 *Command2 + ) +{ + if (StringNoCaseCompare (&Command1, &Command2) == 0) { + return (TRUE); + } + + return (FALSE); +} + +/** + Internal function to determine if a command is a script only command. + + @param[in] CommandName The pointer to the command name. + + @retval TRUE The command is a script only command. + @retval FALSE The command is not a script only command. +**/ +BOOLEAN +IsScriptOnlyCommand ( + IN CONST CHAR16 *CommandName + ) +{ + if ( IsCommand (CommandName, L"for") + || IsCommand (CommandName, L"endfor") + || IsCommand (CommandName, L"if") + || IsCommand (CommandName, L"else") + || IsCommand (CommandName, L"endif") + || IsCommand (CommandName, L"goto")) + { + return (TRUE); + } + + return (FALSE); +} + +/** + This function will populate the 2 device path protocol parameters based on the + global gImageHandle. The DevPath will point to the device path for the handle that has + loaded image protocol installed on it. The FilePath will point to the device path + for the file that was loaded. + + @param[in, out] DevPath On a successful return the device path to the loaded image. + @param[in, out] FilePath On a successful return the device path to the file. + + @retval EFI_SUCCESS The 2 device paths were successfully returned. + @retval other A error from gBS->HandleProtocol. + + @sa HandleProtocol +**/ +EFI_STATUS +GetDevicePathsForImageAndFile ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath, + IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath + ) +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath; + + ASSERT (DevPath != NULL); + ASSERT (FilePath != NULL); + + Status = gBS->OpenProtocol ( + gImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + LoadedImage->DeviceHandle, + &gEfiDevicePathProtocolGuid, + (VOID **)&ImageDevicePath, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + *DevPath = DuplicateDevicePath (ImageDevicePath); + *FilePath = DuplicateDevicePath (LoadedImage->FilePath); + gBS->CloseProtocol ( + LoadedImage->DeviceHandle, + &gEfiDevicePathProtocolGuid, + gImageHandle, + NULL + ); + } + + gBS->CloseProtocol ( + gImageHandle, + &gEfiLoadedImageProtocolGuid, + gImageHandle, + NULL + ); + } + + return (Status); +} + +/** + Process all Uefi Shell 2.0 command line options. + + see Uefi Shell 2.0 section 3.2 for full details. + + the command line must resemble the following: + + shell.efi [ShellOpt-options] [options] [file-name [file-name-options]] + + ShellOpt-options Options which control the initialization behavior of the shell. + These options are read from the EFI global variable "ShellOpt" + and are processed before options or file-name. + + options Options which control the initialization behavior of the shell. + + file-name The name of a UEFI shell application or script to be executed + after initialization is complete. By default, if file-name is + specified, then -nostartup is implied. Scripts are not supported + by level 0. + + file-name-options The command-line options that are passed to file-name when it + is invoked. + + This will initialize the ShellInfoObject.ShellInitSettings global variable. + + @retval EFI_SUCCESS The variable is initialized. +**/ +EFI_STATUS +ProcessCommandLine ( + VOID + ) +{ + UINTN Size; + UINTN LoopVar; + CHAR16 *CurrentArg; + CHAR16 *DelayValueStr; + UINT64 DelayValue; + EFI_STATUS Status; + EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation; + + // `file-name-options` will contain arguments to `file-name` that we don't + // know about. This would cause ShellCommandLineParse to error, so we parse + // arguments manually, ignoring those after the first thing that doesn't look + // like a shell option (which is assumed to be `file-name`). + + Status = gBS->LocateProtocol ( + &gEfiUnicodeCollation2ProtocolGuid, + NULL, + (VOID **)&UnicodeCollation + ); + if (EFI_ERROR (Status)) { + Status = gBS->LocateProtocol ( + &gEfiUnicodeCollationProtocolGuid, + NULL, + (VOID **)&UnicodeCollation + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // Set default options + ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = FALSE; + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = FALSE; + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = FALSE; + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = FALSE; + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = FALSE; + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = FALSE; + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = FALSE; + ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = FALSE; + ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = FALSE; + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = FALSE; + ShellInfoObject.ShellInitSettings.Delay = PcdGet32 (PcdShellDefaultDelay); + + // + // Start LoopVar at 0 to parse only optional arguments at Argv[0] + // and parse other parameters from Argv[1]. This is for use case that + // UEFI Shell boot option is created, and OptionalData is provided + // that starts with shell command-line options. + // + for (LoopVar = 0; LoopVar < gEfiShellParametersProtocol->Argc; LoopVar++) { + CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar]; + if (UnicodeCollation->StriColl ( + UnicodeCollation, + L"-startup", + CurrentArg + ) == 0) + { + ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = TRUE; + } else if (UnicodeCollation->StriColl ( + UnicodeCollation, + L"-nostartup", + CurrentArg + ) == 0) + { + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = TRUE; + } else if (UnicodeCollation->StriColl ( + UnicodeCollation, + L"-noconsoleout", + CurrentArg + ) == 0) + { + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = TRUE; + } else if (UnicodeCollation->StriColl ( + UnicodeCollation, + L"-noconsolein", + CurrentArg + ) == 0) + { + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = TRUE; + } else if (UnicodeCollation->StriColl ( + UnicodeCollation, + L"-nointerrupt", + CurrentArg + ) == 0) + { + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = TRUE; + } else if (UnicodeCollation->StriColl ( + UnicodeCollation, + L"-nomap", + CurrentArg + ) == 0) + { + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = TRUE; + } else if (UnicodeCollation->StriColl ( + UnicodeCollation, + L"-noversion", + CurrentArg + ) == 0) + { + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = TRUE; + } else if (UnicodeCollation->StriColl ( + UnicodeCollation, + L"-nonest", + CurrentArg + ) == 0) + { + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = TRUE; + } else if (UnicodeCollation->StriColl ( + UnicodeCollation, + L"-delay", + CurrentArg + ) == 0) + { + ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = TRUE; + // Check for optional delay value following "-delay" + if ((LoopVar + 1) >= gEfiShellParametersProtocol->Argc) { + DelayValueStr = NULL; + } else { + DelayValueStr = gEfiShellParametersProtocol->Argv[LoopVar + 1]; + } + + if (DelayValueStr != NULL) { + if (*DelayValueStr == L':') { + DelayValueStr++; + } + + if (!EFI_ERROR ( + ShellConvertStringToUint64 ( + DelayValueStr, + &DelayValue, + FALSE, + FALSE + ) + )) + { + ShellInfoObject.ShellInitSettings.Delay = (UINTN)DelayValue; + LoopVar++; + } + } + } else if (UnicodeCollation->StriColl ( + UnicodeCollation, + L"-exit", + CurrentArg + ) == 0) + { + ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = TRUE; + } else if (StrnCmp (L"-", CurrentArg, 1) == 0) { + // Unrecognized option + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_GEN_PROBLEM), + ShellInfoObject.HiiHandle, + CurrentArg + ); + return EFI_INVALID_PARAMETER; + } else { + // + // First argument should be Shell.efi image name + // + if (LoopVar == 0) { + continue; + } + + ShellInfoObject.ShellInitSettings.FileName = NULL; + Size = 0; + // + // If first argument contains a space, then add double quotes before the argument + // + if (StrStr (CurrentArg, L" ") != NULL) { + StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0); + if (ShellInfoObject.ShellInitSettings.FileName == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + } + + StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, CurrentArg, 0); + if (ShellInfoObject.ShellInitSettings.FileName == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + // + // If first argument contains a space, then add double quotes after the argument + // + if (StrStr (CurrentArg, L" ") != NULL) { + StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0); + if (ShellInfoObject.ShellInitSettings.FileName == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + } + + // + // We found `file-name`. + // + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1; + LoopVar++; + + // Add `file-name-options` + for (Size = 0; LoopVar < gEfiShellParametersProtocol->Argc; LoopVar++) { + ASSERT ((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL)); + // + // Add a space between arguments + // + if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) { + StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileOptions, &Size, L" ", 0); + if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) { + SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName); + return (EFI_OUT_OF_RESOURCES); + } + } + + // + // If an argument contains a space, then add double quotes before the argument + // + if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != NULL) { + StrnCatGrow ( + &ShellInfoObject.ShellInitSettings.FileOptions, + &Size, + L"\"", + 0 + ); + if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) { + SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName); + return (EFI_OUT_OF_RESOURCES); + } + } + + StrnCatGrow ( + &ShellInfoObject.ShellInitSettings.FileOptions, + &Size, + gEfiShellParametersProtocol->Argv[LoopVar], + 0 + ); + if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) { + SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName); + return (EFI_OUT_OF_RESOURCES); + } + + // + // If an argument contains a space, then add double quotes after the argument + // + if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != NULL) { + StrnCatGrow ( + &ShellInfoObject.ShellInitSettings.FileOptions, + &Size, + L"\"", + 0 + ); + if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) { + SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName); + return (EFI_OUT_OF_RESOURCES); + } + } + } + } + } + + // "-nointerrupt" overrides "-delay" + if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) { + ShellInfoObject.ShellInitSettings.Delay = 0; + } + + return EFI_SUCCESS; +} + +/** + Function try to find location of the Startup.nsh file. + + The buffer is callee allocated and should be freed by the caller. + + @param ImageDevicePath The path to the image for shell. first place to look for the startup script + @param FileDevicePath The path to the file for shell. second place to look for the startup script. + + @retval NULL No Startup.nsh file was found. + @return !=NULL Pointer to NULL-terminated path. +**/ +CHAR16 * +LocateStartupScript ( + IN EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *FileDevicePath + ) +{ + CHAR16 *StartupScriptPath; + CHAR16 *TempSpot; + CONST CHAR16 *MapName; + UINTN Size; + + StartupScriptPath = NULL; + Size = 0; + + // + // Try to find 'Startup.nsh' in the directory where the shell itself was launched. + // + MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath (&ImageDevicePath); + if (MapName != NULL) { + StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, MapName, 0); + if (StartupScriptPath == NULL) { + // + // Do not locate the startup script in sys path when out of resource. + // + return NULL; + } + + TempSpot = StrStr (StartupScriptPath, L";"); + if (TempSpot != NULL) { + *TempSpot = CHAR_NULL; + } + + InternalEfiShellSetEnv (L"homefilesystem", StartupScriptPath, TRUE); + + StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, ((FILEPATH_DEVICE_PATH *)FileDevicePath)->PathName, 0); + PathRemoveLastItem (StartupScriptPath); + StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, mStartupScript, 0); + } + + // + // Try to find 'Startup.nsh' in the execution path defined by the environment variable PATH. + // + if ((StartupScriptPath == NULL) || EFI_ERROR (ShellIsFile (StartupScriptPath))) { + SHELL_FREE_NON_NULL (StartupScriptPath); + StartupScriptPath = ShellFindFilePath (mStartupScript); + } + + return StartupScriptPath; +} + +/** + Handles all interaction with the default startup script. + + this will check that the correct command line parameters were passed, handle the delay, and then start running the script. + + @param ImagePath the path to the image for shell. first place to look for the startup script + @param FilePath the path to the file for shell. second place to look for the startup script. + + @retval EFI_SUCCESS the variable is initialized. +**/ +EFI_STATUS +DoStartupScript ( + IN EFI_DEVICE_PATH_PROTOCOL *ImagePath, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath + ) +{ + EFI_STATUS Status; + EFI_STATUS CalleeStatus; + UINTN Delay; + EFI_INPUT_KEY Key; + CHAR16 *FileStringPath; + CHAR16 *FullFileStringPath; + UINTN NewSize; + + CalleeStatus = EFI_SUCCESS; + Key.UnicodeChar = CHAR_NULL; + Key.ScanCode = 0; + + if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && (ShellInfoObject.ShellInitSettings.FileName != NULL)) { + // + // launch something else instead + // + NewSize = StrSize (ShellInfoObject.ShellInitSettings.FileName); + if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) { + NewSize += StrSize (ShellInfoObject.ShellInitSettings.FileOptions) + sizeof (CHAR16); + } + + FileStringPath = AllocateZeroPool (NewSize); + if (FileStringPath == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + StrCpyS (FileStringPath, NewSize/sizeof (CHAR16), ShellInfoObject.ShellInitSettings.FileName); + if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) { + StrnCatS (FileStringPath, NewSize/sizeof (CHAR16), L" ", NewSize/sizeof (CHAR16) - StrLen (FileStringPath) -1); + StrnCatS (FileStringPath, NewSize/sizeof (CHAR16), ShellInfoObject.ShellInitSettings.FileOptions, NewSize/sizeof (CHAR16) - StrLen (FileStringPath) -1); + } + + Status = RunShellCommand (FileStringPath, &CalleeStatus); + if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit == TRUE) { + ShellCommandRegisterExit (gEfiShellProtocol->BatchIsActive (), (UINT64)CalleeStatus); + } + + FreePool (FileStringPath); + return (Status); + } + + // + // for shell level 0 we do no scripts + // Without the Startup bit overriding we allow for nostartup to prevent scripts + // + if ( (PcdGet8 (PcdShellSupportLevel) < 1) + || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup) + ) + { + return (EFI_SUCCESS); + } + + gST->ConOut->EnableCursor (gST->ConOut, FALSE); + // + // print out our warning and see if they press a key + // + for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay + ; Delay != 0 && EFI_ERROR (Status) + ; Delay-- + ) + { + ShellPrintHiiEx (0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay); + gBS->Stall (1000000); + if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) { + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + } + } + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle); + gST->ConOut->EnableCursor (gST->ConOut, TRUE); + + // + // ESC was pressed + // + if ((Status == EFI_SUCCESS) && (Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC)) { + return (EFI_SUCCESS); + } + + FileStringPath = LocateStartupScript (ImagePath, FilePath); + if (FileStringPath != NULL) { + FullFileStringPath = FullyQualifyPath (FileStringPath); + if (FullFileStringPath == NULL) { + Status = RunScriptFile (FileStringPath, NULL, FileStringPath, ShellInfoObject.NewShellParametersProtocol); + } else { + Status = RunScriptFile (FullFileStringPath, NULL, FullFileStringPath, ShellInfoObject.NewShellParametersProtocol); + FreePool (FullFileStringPath); + } + + FreePool (FileStringPath); + } else { + // + // we return success since startup script is not mandatory. + // + Status = EFI_SUCCESS; + } + + return (Status); +} + +/** + Function to perform the shell prompt looping. It will do a single prompt, + dispatch the result, and then return. It is expected that the caller will + call this function in a loop many times. + + @retval EFI_SUCCESS + @retval RETURN_ABORTED +**/ +EFI_STATUS +DoShellPrompt ( + VOID + ) +{ + UINTN Column; + UINTN Row; + CHAR16 *CmdLine; + CONST CHAR16 *CurDir; + UINTN BufferSize; + EFI_STATUS Status; + LIST_ENTRY OldBufferList; + + CurDir = NULL; + + // + // Get screen setting to decide size of the command line buffer + // + gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row); + BufferSize = Column * Row * sizeof (CHAR16); + CmdLine = AllocateZeroPool (BufferSize); + if (CmdLine == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SaveBufferList (&OldBufferList); + CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv (L"cwd"); + + // + // Prompt for input + // + gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow); + + if ((CurDir != NULL) && (StrLen (CurDir) > 1)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle); + } + + // + // Read a line from the console + // + Status = ShellInfoObject.NewEfiShellProtocol->ReadFile (ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine); + + // + // Null terminate the string and parse it + // + if (!EFI_ERROR (Status)) { + // + // Reset the CTRL-C event just before running the command (yes we ignore the return values) + // + Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak); + + CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL; + Status = RunCommand (CmdLine); + } + + // + // Done with this command + // + RestoreBufferList (&OldBufferList); + FreePool (CmdLine); + return Status; +} + +/** + Add a buffer to the Buffer To Free List for safely returning buffers to other + places without risking letting them modify internal shell information. + + @param Buffer Something to pass to FreePool when the shell is exiting. +**/ +VOID * +AddBufferToFreeList ( + VOID *Buffer + ) +{ + BUFFER_LIST *BufferListEntry; + + if (Buffer == NULL) { + return (NULL); + } + + BufferListEntry = AllocateZeroPool (sizeof (BUFFER_LIST)); + if (BufferListEntry == NULL) { + return NULL; + } + + BufferListEntry->Buffer = Buffer; + InsertTailList (&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link); + return (Buffer); +} + +/** + Create a new buffer list and stores the old one to OldBufferList + + @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList. +**/ +VOID +SaveBufferList ( + OUT LIST_ENTRY *OldBufferList + ) +{ + CopyMem (OldBufferList, &ShellInfoObject.BufferToFreeList.Link, sizeof (LIST_ENTRY)); + InitializeListHead (&ShellInfoObject.BufferToFreeList.Link); +} + +/** + Restore previous nodes into BufferToFreeList . + + @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList. +**/ +VOID +RestoreBufferList ( + IN OUT LIST_ENTRY *OldBufferList + ) +{ + FreeBufferList (&ShellInfoObject.BufferToFreeList); + CopyMem (&ShellInfoObject.BufferToFreeList.Link, OldBufferList, sizeof (LIST_ENTRY)); +} + +/** + Add a buffer to the Line History List + + @param Buffer The line buffer to add. +**/ +VOID +AddLineToCommandHistory ( + IN CONST CHAR16 *Buffer + ) +{ + BUFFER_LIST *Node; + BUFFER_LIST *Walker; + UINT16 MaxHistoryCmdCount; + UINT16 Count; + + Count = 0; + MaxHistoryCmdCount = PcdGet16 (PcdShellMaxHistoryCommandCount); + + if (MaxHistoryCmdCount == 0) { + return; + } + + Node = AllocateZeroPool (sizeof (BUFFER_LIST)); + if (Node == NULL) { + return; + } + + Node->Buffer = AllocateCopyPool (StrSize (Buffer), Buffer); + if (Node->Buffer == NULL) { + FreePool (Node); + return; + } + + for ( Walker = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link) + ; !IsNull (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link) + ; Walker = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link) + ) + { + Count++; + } + + if (Count < MaxHistoryCmdCount) { + InsertTailList (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link); + } else { + Walker = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link); + RemoveEntryList (&Walker->Link); + if (Walker->Buffer != NULL) { + FreePool (Walker->Buffer); + } + + FreePool (Walker); + InsertTailList (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link); + } +} + +/** + Checks if a string is an alias for another command. If yes, then it replaces the alias name + with the correct command name. + + @param[in, out] CommandString Upon entry the potential alias. Upon return the + command name if it was an alias. If it was not + an alias it will be unchanged. This function may + change the buffer to fit the command name. + + @retval EFI_SUCCESS The name was changed. + @retval EFI_SUCCESS The name was not an alias. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +ShellConvertAlias ( + IN OUT CHAR16 **CommandString + ) +{ + CONST CHAR16 *NewString; + + NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias (*CommandString, NULL); + if (NewString == NULL) { + return (EFI_SUCCESS); + } + + FreePool (*CommandString); + *CommandString = AllocateCopyPool (StrSize (NewString), NewString); + if (*CommandString == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + return (EFI_SUCCESS); +} + +/** + This function will eliminate unreplaced (and therefore non-found) environment variables. + + @param[in,out] CmdLine The command line to update. +**/ +EFI_STATUS +StripUnreplacedEnvironmentVariables ( + IN OUT CHAR16 *CmdLine + ) +{ + CHAR16 *FirstPercent; + CHAR16 *FirstQuote; + CHAR16 *SecondPercent; + CHAR16 *SecondQuote; + CHAR16 *CurrentLocator; + + for (CurrentLocator = CmdLine; CurrentLocator != NULL; ) { + FirstQuote = FindNextInstance (CurrentLocator, L"\"", TRUE); + FirstPercent = FindNextInstance (CurrentLocator, L"%", TRUE); + SecondPercent = FirstPercent != NULL ? FindNextInstance (FirstPercent+1, L"%", TRUE) : NULL; + if ((FirstPercent == NULL) || (SecondPercent == NULL)) { + // + // If we ever don't have 2 % we are done. + // + break; + } + + if ((FirstQuote != NULL) && (FirstQuote < FirstPercent)) { + SecondQuote = FindNextInstance (FirstQuote+1, L"\"", TRUE); + // + // Quote is first found + // + + if (SecondQuote < FirstPercent) { + // + // restart after the pair of " + // + CurrentLocator = SecondQuote + 1; + } else { + /* FirstPercent < SecondQuote */ + // + // Restart on the first percent + // + CurrentLocator = FirstPercent; + } + + continue; + } + + if ((FirstQuote == NULL) || (SecondPercent < FirstQuote)) { + if (IsValidEnvironmentVariableName (FirstPercent, SecondPercent)) { + // + // We need to remove from FirstPercent to SecondPercent + // + CopyMem (FirstPercent, SecondPercent + 1, StrSize (SecondPercent + 1)); + // + // don't need to update the locator. both % characters are gone. + // + } else { + CurrentLocator = SecondPercent + 1; + } + + continue; + } + + CurrentLocator = FirstQuote; + } + + return (EFI_SUCCESS); +} + +/** + Function allocates a new command line and replaces all instances of environment + variable names that are correctly preset to their values. + + If the return value is not NULL the memory must be caller freed. + + @param[in] OriginalCommandLine The original command line + + @retval NULL An error occurred. + @return The new command line with no environment variables present. +**/ +CHAR16 * +ShellConvertVariables ( + IN CONST CHAR16 *OriginalCommandLine + ) +{ + CONST CHAR16 *MasterEnvList; + UINTN NewSize; + CHAR16 *NewCommandLine1; + CHAR16 *NewCommandLine2; + CHAR16 *Temp; + UINTN ItemSize; + CHAR16 *ItemTemp; + SCRIPT_FILE *CurrentScriptFile; + ALIAS_LIST *AliasListNode; + + ASSERT (OriginalCommandLine != NULL); + + ItemSize = 0; + NewSize = StrSize (OriginalCommandLine); + CurrentScriptFile = ShellCommandGetCurrentScriptFile (); + Temp = NULL; + + /// @todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ? + + // + // calculate the size required for the post-conversion string... + // + if (CurrentScriptFile != NULL) { + for (AliasListNode = (ALIAS_LIST *)GetFirstNode (&CurrentScriptFile->SubstList) + ; !IsNull (&CurrentScriptFile->SubstList, &AliasListNode->Link) + ; AliasListNode = (ALIAS_LIST *)GetNextNode (&CurrentScriptFile->SubstList, &AliasListNode->Link) + ) + { + for (Temp = StrStr (OriginalCommandLine, AliasListNode->Alias) + ; Temp != NULL + ; Temp = StrStr (Temp+1, AliasListNode->Alias) + ) + { + // + // we need a preceding and if there is space no ^ preceding (if no space ignore) + // + if ((((Temp-OriginalCommandLine) > 2) && (*(Temp-2) != L'^')) || ((Temp-OriginalCommandLine) <= 2)) { + NewSize += StrSize (AliasListNode->CommandString); + } + } + } + } + + for (MasterEnvList = EfiShellGetEnv (NULL) + ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL // && *(MasterEnvList+1) != CHAR_NULL + ; MasterEnvList += StrLen (MasterEnvList) + 1 + ) + { + if (StrSize (MasterEnvList) > ItemSize) { + ItemSize = StrSize (MasterEnvList); + } + + for (Temp = StrStr (OriginalCommandLine, MasterEnvList) + ; Temp != NULL + ; Temp = StrStr (Temp+1, MasterEnvList) + ) + { + // + // we need a preceding and following % and if there is space no ^ preceding (if no space ignore) + // + if ((*(Temp-1) == L'%') && (*(Temp+StrLen (MasterEnvList)) == L'%') && + ((((Temp-OriginalCommandLine) > 2) && (*(Temp-2) != L'^')) || ((Temp-OriginalCommandLine) <= 2))) + { + NewSize += StrSize (EfiShellGetEnv (MasterEnvList)); + } + } + } + + // + // now do the replacements... + // + NewCommandLine1 = AllocateZeroPool (NewSize); + NewCommandLine2 = AllocateZeroPool (NewSize); + ItemTemp = AllocateZeroPool (ItemSize+(2*sizeof (CHAR16))); + if ((NewCommandLine1 == NULL) || (NewCommandLine2 == NULL) || (ItemTemp == NULL)) { + SHELL_FREE_NON_NULL (NewCommandLine1); + SHELL_FREE_NON_NULL (NewCommandLine2); + SHELL_FREE_NON_NULL (ItemTemp); + return (NULL); + } + + CopyMem (NewCommandLine1, OriginalCommandLine, StrSize (OriginalCommandLine)); + + for (MasterEnvList = EfiShellGetEnv (NULL) + ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL + ; MasterEnvList += StrLen (MasterEnvList) + 1 + ) + { + StrCpyS ( + ItemTemp, + ((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)), + L"%" + ); + StrCatS ( + ItemTemp, + ((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)), + MasterEnvList + ); + StrCatS ( + ItemTemp, + ((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)), + L"%" + ); + ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv (MasterEnvList), TRUE, FALSE); + StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), NewCommandLine2); + } + + if (CurrentScriptFile != NULL) { + for (AliasListNode = (ALIAS_LIST *)GetFirstNode (&CurrentScriptFile->SubstList) + ; !IsNull (&CurrentScriptFile->SubstList, &AliasListNode->Link) + ; AliasListNode = (ALIAS_LIST *)GetNextNode (&CurrentScriptFile->SubstList, &AliasListNode->Link) + ) + { + ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE); + StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), NewCommandLine2); + } + } + + // + // Remove non-existent environment variables + // + StripUnreplacedEnvironmentVariables (NewCommandLine1); + + // + // Now cleanup any straggler intentionally ignored "%" characters + // + ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE); + StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), NewCommandLine2); + + FreePool (NewCommandLine2); + FreePool (ItemTemp); + + return (NewCommandLine1); +} + +/** + Internal function to run a command line with pipe usage. + + @param[in] CmdLine The pointer to the command line. + @param[in] StdIn The pointer to the Standard input. + @param[in] StdOut The pointer to the Standard output. + + @retval EFI_SUCCESS The split command is executed successfully. + @retval other Some error occurs when executing the split command. +**/ +EFI_STATUS +RunSplitCommand ( + IN CONST CHAR16 *CmdLine, + IN SHELL_FILE_HANDLE StdIn, + IN SHELL_FILE_HANDLE StdOut + ) +{ + EFI_STATUS Status; + CHAR16 *NextCommandLine; + CHAR16 *OurCommandLine; + UINTN Size1; + UINTN Size2; + SPLIT_LIST *Split; + SHELL_FILE_HANDLE TempFileHandle; + BOOLEAN Unicode; + + ASSERT (StdOut == NULL); + + ASSERT (StrStr (CmdLine, L"|") != NULL); + + Status = EFI_SUCCESS; + NextCommandLine = NULL; + OurCommandLine = NULL; + Size1 = 0; + Size2 = 0; + + NextCommandLine = StrnCatGrow (&NextCommandLine, &Size1, StrStr (CmdLine, L"|")+1, 0); + OurCommandLine = StrnCatGrow (&OurCommandLine, &Size2, CmdLine, StrStr (CmdLine, L"|") - CmdLine); + + if ((NextCommandLine == NULL) || (OurCommandLine == NULL)) { + SHELL_FREE_NON_NULL (OurCommandLine); + SHELL_FREE_NON_NULL (NextCommandLine); + return (EFI_OUT_OF_RESOURCES); + } else if ((StrStr (OurCommandLine, L"|") != NULL) || (Size1 == 0) || (Size2 == 0)) { + SHELL_FREE_NON_NULL (OurCommandLine); + SHELL_FREE_NON_NULL (NextCommandLine); + return (EFI_INVALID_PARAMETER); + } else if ((NextCommandLine[0] == L'a') && + ((NextCommandLine[1] == L' ') || (NextCommandLine[1] == CHAR_NULL)) + ) + { + CopyMem (NextCommandLine, NextCommandLine+1, StrSize (NextCommandLine) - sizeof (NextCommandLine[0])); + while (NextCommandLine[0] == L' ') { + CopyMem (NextCommandLine, NextCommandLine+1, StrSize (NextCommandLine) - sizeof (NextCommandLine[0])); + } + + if (NextCommandLine[0] == CHAR_NULL) { + SHELL_FREE_NON_NULL (OurCommandLine); + SHELL_FREE_NON_NULL (NextCommandLine); + return (EFI_INVALID_PARAMETER); + } + + Unicode = FALSE; + } else { + Unicode = TRUE; + } + + // + // make a SPLIT_LIST item and add to list + // + Split = AllocateZeroPool (sizeof (SPLIT_LIST)); + if (Split == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Split->SplitStdIn = StdIn; + Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (Unicode), NULL); + ASSERT (Split->SplitStdOut != NULL); + InsertHeadList (&ShellInfoObject.SplitList.Link, &Split->Link); + + Status = RunCommand (OurCommandLine); + + // + // move the output from the first to the in to the second. + // + TempFileHandle = Split->SplitStdOut; + if (Split->SplitStdIn == StdIn) { + Split->SplitStdOut = NULL; + } else { + Split->SplitStdOut = Split->SplitStdIn; + } + + Split->SplitStdIn = TempFileHandle; + ShellInfoObject.NewEfiShellProtocol->SetFilePosition (Split->SplitStdIn, 0); + + if (!EFI_ERROR (Status)) { + Status = RunCommand (NextCommandLine); + } + + // + // remove the top level from the ScriptList + // + ASSERT ((SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link) == Split); + RemoveEntryList (&Split->Link); + + // + // Note that the original StdIn is now the StdOut... + // + if (Split->SplitStdOut != NULL) { + ShellInfoObject.NewEfiShellProtocol->CloseFile (Split->SplitStdOut); + } + + if (Split->SplitStdIn != NULL) { + ShellInfoObject.NewEfiShellProtocol->CloseFile (Split->SplitStdIn); + } + + FreePool (Split); + FreePool (NextCommandLine); + FreePool (OurCommandLine); + + return (Status); +} + +/** + Take the original command line, substitute any variables, free + the original string, return the modified copy. + + @param[in] CmdLine pointer to the command line to update. + + @retval EFI_SUCCESS the function was successful. + @retval EFI_OUT_OF_RESOURCES a memory allocation failed. +**/ +EFI_STATUS +ShellSubstituteVariables ( + IN CHAR16 **CmdLine + ) +{ + CHAR16 *NewCmdLine; + + NewCmdLine = ShellConvertVariables (*CmdLine); + SHELL_FREE_NON_NULL (*CmdLine); + if (NewCmdLine == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + *CmdLine = NewCmdLine; + return (EFI_SUCCESS); +} + +/** + Take the original command line, substitute any alias in the first group of space delimited characters, free + the original string, return the modified copy. + + @param[in] CmdLine pointer to the command line to update. + + @retval EFI_SUCCESS the function was successful. + @retval EFI_OUT_OF_RESOURCES a memory allocation failed. +**/ +EFI_STATUS +ShellSubstituteAliases ( + IN CHAR16 **CmdLine + ) +{ + CHAR16 *NewCmdLine; + CHAR16 *CommandName; + EFI_STATUS Status; + UINTN PostAliasSize; + + ASSERT (CmdLine != NULL); + ASSERT (*CmdLine != NULL); + + CommandName = NULL; + if (StrStr ((*CmdLine), L" ") == NULL) { + StrnCatGrow (&CommandName, NULL, (*CmdLine), 0); + } else { + StrnCatGrow (&CommandName, NULL, (*CmdLine), StrStr ((*CmdLine), L" ") - (*CmdLine)); + } + + // + // This cannot happen 'inline' since the CmdLine can need extra space. + // + NewCmdLine = NULL; + if (!ShellCommandIsCommandOnList (CommandName)) { + // + // Convert via alias + // + Status = ShellConvertAlias (&CommandName); + if (EFI_ERROR (Status)) { + return (Status); + } + + PostAliasSize = 0; + NewCmdLine = StrnCatGrow (&NewCmdLine, &PostAliasSize, CommandName, 0); + if (NewCmdLine == NULL) { + SHELL_FREE_NON_NULL (CommandName); + SHELL_FREE_NON_NULL (*CmdLine); + return (EFI_OUT_OF_RESOURCES); + } + + NewCmdLine = StrnCatGrow (&NewCmdLine, &PostAliasSize, StrStr ((*CmdLine), L" "), 0); + if (NewCmdLine == NULL) { + SHELL_FREE_NON_NULL (CommandName); + SHELL_FREE_NON_NULL (*CmdLine); + return (EFI_OUT_OF_RESOURCES); + } + } else { + NewCmdLine = StrnCatGrow (&NewCmdLine, NULL, (*CmdLine), 0); + } + + SHELL_FREE_NON_NULL (*CmdLine); + SHELL_FREE_NON_NULL (CommandName); + + // + // re-assign the passed in double pointer to point to our newly allocated buffer + // + *CmdLine = NewCmdLine; + + return (EFI_SUCCESS); +} + +/** + Takes the Argv[0] part of the command line and determine the meaning of it. + + @param[in] CmdName pointer to the command line to update. + + @retval Internal_Command The name is an internal command. + @retval File_Sys_Change the name is a file system change. + @retval Script_File_Name the name is a NSH script file. + @retval Unknown_Invalid the name is unknown. + @retval Efi_Application the name is an application (.EFI). +**/ +SHELL_OPERATION_TYPES +GetOperationType ( + IN CONST CHAR16 *CmdName + ) +{ + CHAR16 *FileWithPath; + CONST CHAR16 *TempLocation; + CONST CHAR16 *TempLocation2; + + FileWithPath = NULL; + // + // test for an internal command. + // + if (ShellCommandIsCommandOnList (CmdName)) { + return (Internal_Command); + } + + // + // Test for file system change request. anything ending with first : and cant have spaces. + // + if (CmdName[(StrLen (CmdName)-1)] == L':') { + if ( (StrStr (CmdName, L" ") != NULL) + || (StrLen (StrStr (CmdName, L":")) > 1) + ) + { + return (Unknown_Invalid); + } + + return (File_Sys_Change); + } + + // + // Test for a file + // + if ((FileWithPath = ShellFindFilePathEx (CmdName, mExecutableExtensions)) != NULL) { + // + // See if that file has a script file extension + // + if (StrLen (FileWithPath) > 4) { + TempLocation = FileWithPath+StrLen (FileWithPath)-4; + TempLocation2 = mScriptExtension; + if (StringNoCaseCompare ((VOID *)(&TempLocation), (VOID *)(&TempLocation2)) == 0) { + SHELL_FREE_NON_NULL (FileWithPath); + return (Script_File_Name); + } + } + + // + // Was a file, but not a script. we treat this as an application. + // + SHELL_FREE_NON_NULL (FileWithPath); + return (Efi_Application); + } + + SHELL_FREE_NON_NULL (FileWithPath); + // + // No clue what this is... return invalid flag... + // + return (Unknown_Invalid); +} + +/** + Determine if the first item in a command line is valid. + + @param[in] CmdLine The command line to parse. + + @retval EFI_SUCCESS The item is valid. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_NOT_FOUND The operation type is unknown or invalid. +**/ +EFI_STATUS +IsValidSplit ( + IN CONST CHAR16 *CmdLine + ) +{ + CHAR16 *Temp; + CHAR16 *FirstParameter; + CHAR16 *TempWalker; + EFI_STATUS Status; + + Temp = NULL; + + Temp = StrnCatGrow (&Temp, NULL, CmdLine, 0); + if (Temp == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + FirstParameter = StrStr (Temp, L"|"); + if (FirstParameter != NULL) { + *FirstParameter = CHAR_NULL; + } + + FirstParameter = NULL; + + // + // Process the command line + // + Status = ProcessCommandLineToFinal (&Temp); + + if (!EFI_ERROR (Status)) { + FirstParameter = AllocateZeroPool (StrSize (CmdLine)); + if (FirstParameter == NULL) { + SHELL_FREE_NON_NULL (Temp); + return (EFI_OUT_OF_RESOURCES); + } + + TempWalker = (CHAR16 *)Temp; + if (!EFI_ERROR (GetNextParameter (&TempWalker, &FirstParameter, StrSize (CmdLine), TRUE))) { + if (GetOperationType (FirstParameter) == Unknown_Invalid) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter); + SetLastError (SHELL_NOT_FOUND); + Status = EFI_NOT_FOUND; + } + } + } + + SHELL_FREE_NON_NULL (Temp); + SHELL_FREE_NON_NULL (FirstParameter); + return Status; +} + +/** + Determine if a command line contains with a split contains only valid commands. + + @param[in] CmdLine The command line to parse. + + @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split. + @retval EFI_ABORTED CmdLine has at least one invalid command or application. +**/ +EFI_STATUS +VerifySplit ( + IN CONST CHAR16 *CmdLine + ) +{ + CONST CHAR16 *TempSpot; + EFI_STATUS Status; + + // + // If this was the only item, then get out + // + if (!ContainsSplit (CmdLine)) { + return (EFI_SUCCESS); + } + + // + // Verify up to the pipe or end character + // + Status = IsValidSplit (CmdLine); + if (EFI_ERROR (Status)) { + return (Status); + } + + // + // recurse to verify the next item + // + TempSpot = FindFirstCharacter (CmdLine, L"|", L'^') + 1; + if ((*TempSpot == L'a') && + ((*(TempSpot + 1) == L' ') || (*(TempSpot + 1) == CHAR_NULL)) + ) + { + // If it's an ASCII pipe '|a' + TempSpot += 1; + } + + return (VerifySplit (TempSpot)); +} + +/** + Process a split based operation. + + @param[in] CmdLine pointer to the command line to process + + @retval EFI_SUCCESS The operation was successful + @return an error occurred. +**/ +EFI_STATUS +ProcessNewSplitCommandLine ( + IN CONST CHAR16 *CmdLine + ) +{ + SPLIT_LIST *Split; + EFI_STATUS Status; + + Status = VerifySplit (CmdLine); + if (EFI_ERROR (Status)) { + return (Status); + } + + Split = NULL; + + // + // are we in an existing split??? + // + if (!IsListEmpty (&ShellInfoObject.SplitList.Link)) { + Split = (SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link); + } + + if (Split == NULL) { + Status = RunSplitCommand (CmdLine, NULL, NULL); + } else { + Status = RunSplitCommand (CmdLine, Split->SplitStdIn, Split->SplitStdOut); + } + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine); + } + + return (Status); +} + +/** + Handle a request to change the current file system. + + @param[in] CmdLine The passed in command line. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +ChangeMappedDrive ( + IN CONST CHAR16 *CmdLine + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + // + // make sure we are the right operation + // + ASSERT (CmdLine[(StrLen (CmdLine)-1)] == L':' && StrStr (CmdLine, L" ") == NULL); + + // + // Call the protocol API to do the work + // + Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir (NULL, CmdLine); + + // + // Report any errors + // + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine); + } + + return (Status); +} + +/** + Reprocess the command line to direct all -? to the help command. + + if found, will add "help" as argv[0], and move the rest later. + + @param[in,out] CmdLine pointer to the command line to update +**/ +EFI_STATUS +DoHelpUpdate ( + IN OUT CHAR16 **CmdLine + ) +{ + CHAR16 *CurrentParameter; + CHAR16 *Walker; + CHAR16 *NewCommandLine; + EFI_STATUS Status; + UINTN NewCmdLineSize; + + Status = EFI_SUCCESS; + + CurrentParameter = AllocateZeroPool (StrSize (*CmdLine)); + if (CurrentParameter == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + Walker = *CmdLine; + while (Walker != NULL && *Walker != CHAR_NULL) { + if (!EFI_ERROR (GetNextParameter (&Walker, &CurrentParameter, StrSize (*CmdLine), TRUE))) { + if (StrStr (CurrentParameter, L"-?") == CurrentParameter) { + CurrentParameter[0] = L' '; + CurrentParameter[1] = L' '; + NewCmdLineSize = StrSize (L"help ") + StrSize (*CmdLine); + NewCommandLine = AllocateZeroPool (NewCmdLineSize); + if (NewCommandLine == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + // + // We know the space is sufficient since we just calculated it. + // + StrnCpyS (NewCommandLine, NewCmdLineSize/sizeof (CHAR16), L"help ", 5); + StrnCatS (NewCommandLine, NewCmdLineSize/sizeof (CHAR16), *CmdLine, StrLen (*CmdLine)); + SHELL_FREE_NON_NULL (*CmdLine); + *CmdLine = NewCommandLine; + break; + } + } + } + + SHELL_FREE_NON_NULL (CurrentParameter); + + return (Status); +} + +/** + Function to update the shell variable "lasterror". + + @param[in] ErrorCode the error code to put into lasterror. +**/ +EFI_STATUS +SetLastError ( + IN CONST SHELL_STATUS ErrorCode + ) +{ + CHAR16 LeString[19]; + + if (sizeof (EFI_STATUS) == sizeof (UINT64)) { + UnicodeSPrint (LeString, sizeof (LeString), L"0x%Lx", ErrorCode); + } else { + UnicodeSPrint (LeString, sizeof (LeString), L"0x%x", ErrorCode); + } + + DEBUG_CODE ( + InternalEfiShellSetEnv (L"debuglasterror", LeString, TRUE); + ); + InternalEfiShellSetEnv (L"lasterror", LeString, TRUE); + + return (EFI_SUCCESS); +} + +/** + Converts the command line to its post-processed form. this replaces variables and alias' per UEFI Shell spec. + + @param[in,out] CmdLine pointer to the command line to update + + @retval EFI_SUCCESS The operation was successful + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @return some other error occurred +**/ +EFI_STATUS +ProcessCommandLineToFinal ( + IN OUT CHAR16 **CmdLine + ) +{ + EFI_STATUS Status; + + TrimSpaces (CmdLine); + + Status = ShellSubstituteAliases (CmdLine); + if (EFI_ERROR (Status)) { + return (Status); + } + + TrimSpaces (CmdLine); + + Status = ShellSubstituteVariables (CmdLine); + if (EFI_ERROR (Status)) { + return (Status); + } + + ASSERT (*CmdLine != NULL); + + TrimSpaces (CmdLine); + + // + // update for help parsing + // + if (StrStr (*CmdLine, L"?") != NULL) { + // + // This may do nothing if the ? does not indicate help. + // Save all the details for in the API below. + // + Status = DoHelpUpdate (CmdLine); + } + + TrimSpaces (CmdLine); + + return (EFI_SUCCESS); +} + +/** + Run an internal shell command. + + This API will update the shell's environment since these commands are libraries. + + @param[in] CmdLine the command line to run. + @param[in] FirstParameter the first parameter on the command line + @param[in] ParamProtocol the shell parameters protocol pointer + @param[out] CommandStatus the status from the command line. + + @retval EFI_SUCCESS The command was completed. + @retval EFI_ABORTED The command's operation was aborted. +**/ +EFI_STATUS +RunInternalCommand ( + IN CONST CHAR16 *CmdLine, + IN CHAR16 *FirstParameter, + IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol, + OUT EFI_STATUS *CommandStatus + ) +{ + EFI_STATUS Status; + UINTN Argc; + CHAR16 **Argv; + SHELL_STATUS CommandReturnedStatus; + BOOLEAN LastError; + CHAR16 *Walker; + CHAR16 *NewCmdLine; + + NewCmdLine = AllocateCopyPool (StrSize (CmdLine), CmdLine); + if (NewCmdLine == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL; Walker++) { + if ((*Walker == L'^') && (*(Walker+1) == L'#')) { + CopyMem (Walker, Walker+1, StrSize (Walker) - sizeof (Walker[0])); + } + } + + // + // get the argc and argv updated for internal commands + // + Status = UpdateArgcArgv (ParamProtocol, NewCmdLine, Internal_Command, &Argv, &Argc); + if (!EFI_ERROR (Status)) { + // + // Run the internal command. + // + Status = ShellCommandRunCommandHandler (FirstParameter, &CommandReturnedStatus, &LastError); + + if (!EFI_ERROR (Status)) { + if (CommandStatus != NULL) { + if (CommandReturnedStatus != SHELL_SUCCESS) { + *CommandStatus = (EFI_STATUS)(CommandReturnedStatus | MAX_BIT); + } else { + *CommandStatus = EFI_SUCCESS; + } + } + + // + // Update last error status. + // some commands do not update last error. + // + if (LastError) { + SetLastError (CommandReturnedStatus); + } + + // + // Pass thru the exitcode from the app. + // + if (ShellCommandGetExit ()) { + // + // An Exit was requested ("exit" command), pass its value up. + // + Status = CommandReturnedStatus; + } else if ((CommandReturnedStatus != SHELL_SUCCESS) && IsScriptOnlyCommand (FirstParameter)) { + // + // Always abort when a script only command fails for any reason + // + Status = EFI_ABORTED; + } else if ((ShellCommandGetCurrentScriptFile () != NULL) && (CommandReturnedStatus == SHELL_ABORTED)) { + // + // Abort when in a script and a command aborted + // + Status = EFI_ABORTED; + } + } + } + + // + // This is guaranteed to be called after UpdateArgcArgv no matter what else happened. + // This is safe even if the update API failed. In this case, it may be a no-op. + // + RestoreArgcArgv (ParamProtocol, &Argv, &Argc); + + // + // If a script is running and the command is not a script only command, then + // change return value to success so the script won't halt (unless aborted). + // + // Script only commands have to be able halt the script since the script will + // not operate if they are failing. + // + if ( (ShellCommandGetCurrentScriptFile () != NULL) + && !IsScriptOnlyCommand (FirstParameter) + && (Status != EFI_ABORTED) + ) + { + Status = EFI_SUCCESS; + } + + FreePool (NewCmdLine); + return (Status); +} + +/** + Function to run the command or file. + + @param[in] Type the type of operation being run. + @param[in] CmdLine the command line to run. + @param[in] FirstParameter the first parameter on the command line + @param[in] ParamProtocol the shell parameters protocol pointer + @param[out] CommandStatus the status from the command line. + + @retval EFI_SUCCESS The command was completed. + @retval EFI_ABORTED The command's operation was aborted. +**/ +EFI_STATUS +RunCommandOrFile ( + IN SHELL_OPERATION_TYPES Type, + IN CONST CHAR16 *CmdLine, + IN CHAR16 *FirstParameter, + IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol, + OUT EFI_STATUS *CommandStatus + ) +{ + EFI_STATUS Status; + EFI_STATUS StartStatus; + CHAR16 *CommandWithPath; + CHAR16 *FullCommandWithPath; + EFI_DEVICE_PATH_PROTOCOL *DevPath; + SHELL_STATUS CalleeExitStatus; + + Status = EFI_SUCCESS; + CommandWithPath = NULL; + DevPath = NULL; + CalleeExitStatus = SHELL_INVALID_PARAMETER; + + switch (Type) { + case Internal_Command: + Status = RunInternalCommand (CmdLine, FirstParameter, ParamProtocol, CommandStatus); + break; + case Script_File_Name: + case Efi_Application: + // + // Process a fully qualified path + // + if (StrStr (FirstParameter, L":") != NULL) { + ASSERT (CommandWithPath == NULL); + if (ShellIsFile (FirstParameter) == EFI_SUCCESS) { + CommandWithPath = StrnCatGrow (&CommandWithPath, NULL, FirstParameter, 0); + } + } + + // + // Process a relative path and also check in the path environment variable + // + if (CommandWithPath == NULL) { + CommandWithPath = ShellFindFilePathEx (FirstParameter, mExecutableExtensions); + } + + // + // This should be impossible now. + // + ASSERT (CommandWithPath != NULL); + + // + // Make sure that path is not just a directory (or not found) + // + if (!EFI_ERROR (ShellIsDirectory (CommandWithPath))) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter); + SetLastError (SHELL_NOT_FOUND); + } + + switch (Type) { + case Script_File_Name: + FullCommandWithPath = FullyQualifyPath (CommandWithPath); + if (FullCommandWithPath == NULL) { + Status = RunScriptFile (CommandWithPath, NULL, CmdLine, ParamProtocol); + } else { + Status = RunScriptFile (FullCommandWithPath, NULL, CmdLine, ParamProtocol); + FreePool (FullCommandWithPath); + } + + break; + case Efi_Application: + // + // Get the device path of the application image + // + DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath (CommandWithPath); + if (DevPath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + // + // Execute the device path + // + Status = InternalShellExecuteDevicePath ( + &gImageHandle, + DevPath, + CmdLine, + NULL, + &StartStatus + ); + + SHELL_FREE_NON_NULL (DevPath); + + if (EFI_ERROR (Status)) { + CalleeExitStatus = (SHELL_STATUS)(Status & (~MAX_BIT)); + } else { + CalleeExitStatus = (SHELL_STATUS)StartStatus; + } + + if (CommandStatus != NULL) { + *CommandStatus = CalleeExitStatus; + } + + // + // Update last error status. + // + // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS + SetLastError (CalleeExitStatus); + break; + default: + // + // Do nothing. + // + break; + } + + break; + default: + // + // Do nothing. + // + break; + } + + SHELL_FREE_NON_NULL (CommandWithPath); + + return (Status); +} + +/** + Function to setup StdIn, StdErr, StdOut, and then run the command or file. + + @param[in] Type the type of operation being run. + @param[in] CmdLine the command line to run. + @param[in] FirstParameter the first parameter on the command line. + @param[in] ParamProtocol the shell parameters protocol pointer + @param[out] CommandStatus the status from the command line. + + @retval EFI_SUCCESS The command was completed. + @retval EFI_ABORTED The command's operation was aborted. +**/ +EFI_STATUS +SetupAndRunCommandOrFile ( + IN SHELL_OPERATION_TYPES Type, + IN CHAR16 *CmdLine, + IN CHAR16 *FirstParameter, + IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol, + OUT EFI_STATUS *CommandStatus + ) +{ + EFI_STATUS Status; + SHELL_FILE_HANDLE OriginalStdIn; + SHELL_FILE_HANDLE OriginalStdOut; + SHELL_FILE_HANDLE OriginalStdErr; + SYSTEM_TABLE_INFO OriginalSystemTableInfo; + CONST SCRIPT_FILE *ConstScriptFile; + + // + // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII + // + Status = UpdateStdInStdOutStdErr (ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo); + + // + // The StdIn, StdOut, and StdErr are set up. + // Now run the command, script, or application + // + if (!EFI_ERROR (Status)) { + TrimSpaces (&CmdLine); + Status = RunCommandOrFile (Type, CmdLine, FirstParameter, ParamProtocol, CommandStatus); + } + + // + // Now print errors + // + if (EFI_ERROR (Status)) { + ConstScriptFile = ShellCommandGetCurrentScriptFile (); + if ((ConstScriptFile == NULL) || (ConstScriptFile->CurrentCommand == NULL)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID *)(Status)); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR_SCRIPT), ShellInfoObject.HiiHandle, (VOID *)(Status), ConstScriptFile->CurrentCommand->Line); + } + } + + // + // put back the original StdIn, StdOut, and StdErr + // + RestoreStdInStdOutStdErr (ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo); + + return (Status); +} + +/** + Function will process and run a command line. + + This will determine if the command line represents an internal shell + command or dispatch an external application. + + @param[in] CmdLine The command line to parse. + @param[out] CommandStatus The status from the command line. + + @retval EFI_SUCCESS The command was completed. + @retval EFI_ABORTED The command's operation was aborted. +**/ +EFI_STATUS +RunShellCommand ( + IN CONST CHAR16 *CmdLine, + OUT EFI_STATUS *CommandStatus + ) +{ + EFI_STATUS Status; + CHAR16 *CleanOriginal; + CHAR16 *FirstParameter; + CHAR16 *TempWalker; + SHELL_OPERATION_TYPES Type; + CONST CHAR16 *CurDir; + + ASSERT (CmdLine != NULL); + if (StrLen (CmdLine) == 0) { + return (EFI_SUCCESS); + } + + Status = EFI_SUCCESS; + CleanOriginal = NULL; + + CleanOriginal = StrnCatGrow (&CleanOriginal, NULL, CmdLine, 0); + if (CleanOriginal == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + TrimSpaces (&CleanOriginal); + + // + // NULL out comments (leveraged from RunScriptFileHandle() ). + // The # character on a line is used to denote that all characters on the same line + // and to the right of the # are to be ignored by the shell. + // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'. + // + for (TempWalker = CleanOriginal; TempWalker != NULL && *TempWalker != CHAR_NULL; TempWalker++) { + if (*TempWalker == L'^') { + if (*(TempWalker + 1) == L'#') { + TempWalker++; + } + } else if (*TempWalker == L'#') { + *TempWalker = CHAR_NULL; + } + } + + TrimSpaces (&CleanOriginal); + + // + // Handle case that passed in command line is just 1 or more " " characters. + // + if (StrLen (CleanOriginal) == 0) { + SHELL_FREE_NON_NULL (CleanOriginal); + return (EFI_SUCCESS); + } + + Status = ProcessCommandLineToFinal (&CleanOriginal); + if (EFI_ERROR (Status)) { + SHELL_FREE_NON_NULL (CleanOriginal); + return (Status); + } + + // + // We don't do normal processing with a split command line (output from one command input to another) + // + if (ContainsSplit (CleanOriginal)) { + Status = ProcessNewSplitCommandLine (CleanOriginal); + SHELL_FREE_NON_NULL (CleanOriginal); + return (Status); + } + + // + // We need the first parameter information so we can determine the operation type + // + FirstParameter = AllocateZeroPool (StrSize (CleanOriginal)); + if (FirstParameter == NULL) { + SHELL_FREE_NON_NULL (CleanOriginal); + return (EFI_OUT_OF_RESOURCES); + } + + TempWalker = CleanOriginal; + if (!EFI_ERROR (GetNextParameter (&TempWalker, &FirstParameter, StrSize (CleanOriginal), TRUE))) { + // + // Depending on the first parameter we change the behavior + // + switch (Type = GetOperationType (FirstParameter)) { + case File_Sys_Change: + Status = ChangeMappedDrive (FirstParameter); + break; + case Internal_Command: + case Script_File_Name: + case Efi_Application: + Status = SetupAndRunCommandOrFile (Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol, CommandStatus); + break; + default: + // + // Whatever was typed, it was invalid. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter); + SetLastError (SHELL_NOT_FOUND); + break; + } + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter); + SetLastError (SHELL_NOT_FOUND); + } + + // + // Check whether the current file system still exists. If not exist, we need update "cwd" and gShellCurMapping. + // + CurDir = EfiShellGetCurDir (NULL); + if (CurDir != NULL) { + if (EFI_ERROR (ShellFileExists (CurDir))) { + // + // EfiShellSetCurDir() cannot set current directory to NULL. + // EfiShellSetEnv() is not allowed to set the "cwd" variable. + // Only InternalEfiShellSetEnv () is allowed setting the "cwd" variable. + // + InternalEfiShellSetEnv (L"cwd", NULL, TRUE); + gShellCurMapping = NULL; + } + } + + SHELL_FREE_NON_NULL (CleanOriginal); + SHELL_FREE_NON_NULL (FirstParameter); + + return (Status); +} + +/** + Function will process and run a command line. + + This will determine if the command line represents an internal shell + command or dispatch an external application. + + @param[in] CmdLine The command line to parse. + + @retval EFI_SUCCESS The command was completed. + @retval EFI_ABORTED The command's operation was aborted. +**/ +EFI_STATUS +RunCommand ( + IN CONST CHAR16 *CmdLine + ) +{ + return (RunShellCommand (CmdLine, NULL)); +} + +/** + Function to process a NSH script file via SHELL_FILE_HANDLE. + + @param[in] Handle The handle to the already opened file. + @param[in] Name The name of the script file. + + @retval EFI_SUCCESS the script completed successfully +**/ +EFI_STATUS +RunScriptFileHandle ( + IN SHELL_FILE_HANDLE Handle, + IN CONST CHAR16 *Name + ) +{ + EFI_STATUS Status; + SCRIPT_FILE *NewScriptFile; + UINTN LoopVar; + UINTN PrintBuffSize; + CHAR16 *CommandLine; + CHAR16 *CommandLine2; + CHAR16 *CommandLine3; + SCRIPT_COMMAND_LIST *LastCommand; + BOOLEAN Ascii; + BOOLEAN PreScriptEchoState; + BOOLEAN PreCommandEchoState; + CONST CHAR16 *CurDir; + UINTN LineCount; + CHAR16 LeString[50]; + LIST_ENTRY OldBufferList; + + ASSERT (!ShellCommandGetScriptExit ()); + + PreScriptEchoState = ShellCommandGetEchoState (); + PrintBuffSize = PcdGet32 (PcdShellPrintBufferSize); + + NewScriptFile = (SCRIPT_FILE *)AllocateZeroPool (sizeof (SCRIPT_FILE)); + if (NewScriptFile == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + // + // Set up the name + // + ASSERT (NewScriptFile->ScriptName == NULL); + NewScriptFile->ScriptName = StrnCatGrow (&NewScriptFile->ScriptName, NULL, Name, 0); + if (NewScriptFile->ScriptName == NULL) { + DeleteScriptFileStruct (NewScriptFile); + return (EFI_OUT_OF_RESOURCES); + } + + // + // Save the parameters (used to replace %0 to %9 later on) + // + NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc; + if (NewScriptFile->Argc != 0) { + NewScriptFile->Argv = (CHAR16 **)AllocateZeroPool (NewScriptFile->Argc * sizeof (CHAR16 *)); + if (NewScriptFile->Argv == NULL) { + DeleteScriptFileStruct (NewScriptFile); + return (EFI_OUT_OF_RESOURCES); + } + + // + // Put the full path of the script file into Argv[0] as required by section + // 3.6.2 of version 2.2 of the shell specification. + // + NewScriptFile->Argv[0] = StrnCatGrow (&NewScriptFile->Argv[0], NULL, NewScriptFile->ScriptName, 0); + for (LoopVar = 1; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) { + ASSERT (NewScriptFile->Argv[LoopVar] == NULL); + NewScriptFile->Argv[LoopVar] = StrnCatGrow (&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0); + if (NewScriptFile->Argv[LoopVar] == NULL) { + DeleteScriptFileStruct (NewScriptFile); + return (EFI_OUT_OF_RESOURCES); + } + } + } else { + NewScriptFile->Argv = NULL; + } + + InitializeListHead (&NewScriptFile->CommandList); + InitializeListHead (&NewScriptFile->SubstList); + + // + // Now build the list of all script commands. + // + LineCount = 0; + while (!ShellFileHandleEof (Handle)) { + CommandLine = ShellFileHandleReturnLine (Handle, &Ascii); + LineCount++; + if ((CommandLine == NULL) || (StrLen (CommandLine) == 0) || (CommandLine[0] == '#')) { + SHELL_FREE_NON_NULL (CommandLine); + continue; + } + + NewScriptFile->CurrentCommand = AllocateZeroPool (sizeof (SCRIPT_COMMAND_LIST)); + if (NewScriptFile->CurrentCommand == NULL) { + SHELL_FREE_NON_NULL (CommandLine); + DeleteScriptFileStruct (NewScriptFile); + return (EFI_OUT_OF_RESOURCES); + } + + NewScriptFile->CurrentCommand->Cl = CommandLine; + NewScriptFile->CurrentCommand->Data = NULL; + NewScriptFile->CurrentCommand->Line = LineCount; + + InsertTailList (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link); + } + + // + // Add this as the topmost script file + // + ShellCommandSetNewScript (NewScriptFile); + + // + // Now enumerate through the commands and run each one. + // + CommandLine = AllocateZeroPool (PrintBuffSize); + if (CommandLine == NULL) { + DeleteScriptFileStruct (NewScriptFile); + return (EFI_OUT_OF_RESOURCES); + } + + CommandLine2 = AllocateZeroPool (PrintBuffSize); + if (CommandLine2 == NULL) { + FreePool (CommandLine); + DeleteScriptFileStruct (NewScriptFile); + return (EFI_OUT_OF_RESOURCES); + } + + for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode (&NewScriptFile->CommandList) + ; !IsNull (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link) + ; // conditional increment in the body of the loop + ) + { + ASSERT (CommandLine2 != NULL); + StrnCpyS ( + CommandLine2, + PrintBuffSize/sizeof (CHAR16), + NewScriptFile->CurrentCommand->Cl, + PrintBuffSize/sizeof (CHAR16) - 1 + ); + + SaveBufferList (&OldBufferList); + + // + // NULL out comments + // + for (CommandLine3 = CommandLine2; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL; CommandLine3++) { + if (*CommandLine3 == L'^') { + if ( *(CommandLine3+1) == L':') { + CopyMem (CommandLine3, CommandLine3+1, StrSize (CommandLine3) - sizeof (CommandLine3[0])); + } else if (*(CommandLine3+1) == L'#') { + CommandLine3++; + } + } else if (*CommandLine3 == L'#') { + *CommandLine3 = CHAR_NULL; + } + } + + if ((CommandLine2 != NULL) && (StrLen (CommandLine2) >= 1)) { + // + // Due to variability in starting the find and replace action we need to have both buffers the same. + // + StrnCpyS ( + CommandLine, + PrintBuffSize/sizeof (CHAR16), + CommandLine2, + PrintBuffSize/sizeof (CHAR16) - 1 + ); + + // + // Remove the %0 to %9 from the command line (if we have some arguments) + // + if (NewScriptFile->Argv != NULL) { + switch (NewScriptFile->Argc) { + default: + Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%9", NewScriptFile->Argv[9], FALSE, FALSE); + ASSERT_EFI_ERROR (Status); + case 9: + Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%8", NewScriptFile->Argv[8], FALSE, FALSE); + ASSERT_EFI_ERROR (Status); + case 8: + Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%7", NewScriptFile->Argv[7], FALSE, FALSE); + ASSERT_EFI_ERROR (Status); + case 7: + Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%6", NewScriptFile->Argv[6], FALSE, FALSE); + ASSERT_EFI_ERROR (Status); + case 6: + Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%5", NewScriptFile->Argv[5], FALSE, FALSE); + ASSERT_EFI_ERROR (Status); + case 5: + Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%4", NewScriptFile->Argv[4], FALSE, FALSE); + ASSERT_EFI_ERROR (Status); + case 4: + Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%3", NewScriptFile->Argv[3], FALSE, FALSE); + ASSERT_EFI_ERROR (Status); + case 3: + Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%2", NewScriptFile->Argv[2], FALSE, FALSE); + ASSERT_EFI_ERROR (Status); + case 2: + Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%1", NewScriptFile->Argv[1], FALSE, FALSE); + ASSERT_EFI_ERROR (Status); + case 1: + Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%0", NewScriptFile->Argv[0], FALSE, FALSE); + ASSERT_EFI_ERROR (Status); + break; + case 0: + break; + } + } + + Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%1", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%2", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%3", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%4", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%5", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%6", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%7", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%8", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%9", L"\"\"", FALSE, FALSE); + + StrnCpyS ( + CommandLine2, + PrintBuffSize/sizeof (CHAR16), + CommandLine, + PrintBuffSize/sizeof (CHAR16) - 1 + ); + + LastCommand = NewScriptFile->CurrentCommand; + + for (CommandLine3 = CommandLine2; CommandLine3[0] == L' '; CommandLine3++) { + } + + if ((CommandLine3 != NULL) && (CommandLine3[0] == L':')) { + // + // This line is a goto target / label + // + } else { + if ((CommandLine3 != NULL) && (StrLen (CommandLine3) > 0)) { + if (CommandLine3[0] == L'@') { + // + // We need to save the current echo state + // and disable echo for just this command. + // + PreCommandEchoState = ShellCommandGetEchoState (); + ShellCommandSetEchoState (FALSE); + Status = RunCommand (CommandLine3+1); + + // + // If command was "@echo -off" or "@echo -on" then don't restore echo state + // + if ((StrCmp (L"@echo -off", CommandLine3) != 0) && + (StrCmp (L"@echo -on", CommandLine3) != 0)) + { + // + // Now restore the pre-'@' echo state. + // + ShellCommandSetEchoState (PreCommandEchoState); + } + } else { + if (ShellCommandGetEchoState ()) { + CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv (L"cwd"); + if ((CurDir != NULL) && (StrLen (CurDir) > 1)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle); + } + + ShellPrintEx (-1, -1, L"%s\r\n", CommandLine2); + } + + Status = RunCommand (CommandLine3); + } + } + + if (ShellCommandGetScriptExit ()) { + // + // ShellCommandGetExitCode() always returns a UINT64 + // + UnicodeSPrint (LeString, sizeof (LeString), L"0x%Lx", ShellCommandGetExitCode ()); + DEBUG_CODE ( + InternalEfiShellSetEnv (L"debuglasterror", LeString, TRUE); + ); + InternalEfiShellSetEnv (L"lasterror", LeString, TRUE); + + ShellCommandRegisterExit (FALSE, 0); + Status = EFI_SUCCESS; + RestoreBufferList (&OldBufferList); + break; + } + + if (ShellGetExecutionBreakFlag ()) { + RestoreBufferList (&OldBufferList); + break; + } + + if (EFI_ERROR (Status)) { + RestoreBufferList (&OldBufferList); + break; + } + + if (ShellCommandGetExit ()) { + RestoreBufferList (&OldBufferList); + break; + } + } + + // + // If that commend did not update the CurrentCommand then we need to advance it... + // + if (LastCommand == NewScriptFile->CurrentCommand) { + NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link); + if (!IsNull (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) { + NewScriptFile->CurrentCommand->Reset = TRUE; + } + } + } else { + NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link); + if (!IsNull (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) { + NewScriptFile->CurrentCommand->Reset = TRUE; + } + } + + RestoreBufferList (&OldBufferList); + } + + FreePool (CommandLine); + FreePool (CommandLine2); + ShellCommandSetNewScript (NULL); + + // + // Only if this was the last script reset the state. + // + if (ShellCommandGetCurrentScriptFile () == NULL) { + ShellCommandSetEchoState (PreScriptEchoState); + } + + return (EFI_SUCCESS); +} + +/** + Function to process a NSH script file. + + @param[in] ScriptPath Pointer to the script file name (including file system path). + @param[in] Handle the handle of the script file already opened. + @param[in] CmdLine the command line to run. + @param[in] ParamProtocol the shell parameters protocol pointer + + @retval EFI_SUCCESS the script completed successfully +**/ +EFI_STATUS +RunScriptFile ( + IN CONST CHAR16 *ScriptPath, + IN SHELL_FILE_HANDLE Handle OPTIONAL, + IN CONST CHAR16 *CmdLine, + IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol + ) +{ + EFI_STATUS Status; + SHELL_FILE_HANDLE FileHandle; + UINTN Argc; + CHAR16 **Argv; + + if (ShellIsFile (ScriptPath) != EFI_SUCCESS) { + return (EFI_INVALID_PARAMETER); + } + + // + // get the argc and argv updated for scripts + // + Status = UpdateArgcArgv (ParamProtocol, CmdLine, Script_File_Name, &Argv, &Argc); + if (!EFI_ERROR (Status)) { + if (Handle == NULL) { + // + // open the file + // + Status = ShellOpenFileByName (ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0); + if (!EFI_ERROR (Status)) { + // + // run it + // + Status = RunScriptFileHandle (FileHandle, ScriptPath); + + // + // now close the file + // + ShellCloseFile (&FileHandle); + } + } else { + Status = RunScriptFileHandle (Handle, ScriptPath); + } + } + + // + // This is guaranteed to be called after UpdateArgcArgv no matter what else happened. + // This is safe even if the update API failed. In this case, it may be a no-op. + // + RestoreArgcArgv (ParamProtocol, &Argv, &Argc); + + return (Status); +} + +/** + Return the pointer to the first occurrence of any character from a list of characters. + + @param[in] String the string to parse + @param[in] CharacterList the list of character to look for + @param[in] EscapeCharacter An escape character to skip + + @return the location of the first character in the string + @retval CHAR_NULL no instance of any character in CharacterList was found in String +**/ +CONST CHAR16 * +FindFirstCharacter ( + IN CONST CHAR16 *String, + IN CONST CHAR16 *CharacterList, + IN CONST CHAR16 EscapeCharacter + ) +{ + UINT32 WalkChar; + UINT32 WalkStr; + + for (WalkStr = 0; WalkStr < StrLen (String); WalkStr++) { + if (String[WalkStr] == EscapeCharacter) { + WalkStr++; + continue; + } + + for (WalkChar = 0; WalkChar < StrLen (CharacterList); WalkChar++) { + if (String[WalkStr] == CharacterList[WalkChar]) { + return (&String[WalkStr]); + } + } + } + + return (String + StrLen (String)); +} diff --git a/ShellPkg/Application/Shell/Shell.h b/ShellPkg/Application/Shell/Shell.h index 89b4ac6b02..e3a4b8f4a3 100644 --- a/ShellPkg/Application/Shell/Shell.h +++ b/ShellPkg/Application/Shell/Shell.h @@ -1,386 +1,386 @@ -/** @file
- function definitions for internal to shell functions.
-
- (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
- Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#ifndef _SHELL_INTERNAL_HEADER_
-#define _SHELL_INTERNAL_HEADER_
-
-#include <Uefi.h>
-
-#include <Guid/ShellVariableGuid.h>
-#include <Guid/ShellAliasGuid.h>
-
-#include <Protocol/LoadedImage.h>
-#include <Protocol/SimpleTextOut.h>
-#include <Protocol/Shell.h>
-#include <Protocol/EfiShellInterface.h>
-#include <Protocol/EfiShellEnvironment2.h>
-#include <Protocol/ShellParameters.h>
-#include <Protocol/BlockIo.h>
-#include <Protocol/HiiPackageList.h>
-
-#include <Library/BaseLib.h>
-#include <Library/UefiApplicationEntryPoint.h>
-#include <Library/UefiLib.h>
-#include <Library/DebugLib.h>
-#include <Library/MemoryAllocationLib.h>
-#include <Library/ShellCommandLib.h>
-#include <Library/UefiRuntimeServicesTableLib.h>
-#include <Library/UefiBootServicesTableLib.h>
-#include <Library/DevicePathLib.h>
-#include <Library/BaseMemoryLib.h>
-#include <Library/PcdLib.h>
-#include <Library/ShellLib.h>
-#include <Library/SortLib.h>
-#include <Library/HiiLib.h>
-#include <Library/PrintLib.h>
-#include <Library/HandleParsingLib.h>
-#include <Library/FileHandleLib.h>
-#include <Library/UefiHiiServicesLib.h>
-
-#include "ShellParametersProtocol.h"
-#include "ShellProtocol.h"
-#include "ShellEnvVar.h"
-#include "ConsoleLogger.h"
-#include "ShellManParser.h"
-#include "ConsoleWrappers.h"
-#include "FileHandleWrappers.h"
-
-extern CONST CHAR16 mNoNestingEnvVarName[];
-extern CONST CHAR16 mNoNestingTrue[];
-extern CONST CHAR16 mNoNestingFalse[];
-
-typedef struct {
- LIST_ENTRY Link; ///< Standard linked list handler.
- SHELL_FILE_HANDLE SplitStdOut; ///< ConsoleOut for use in the split.
- SHELL_FILE_HANDLE SplitStdIn; ///< ConsoleIn for use in the split.
-} SPLIT_LIST;
-
-typedef struct {
- UINT32 Startup : 1; ///< Was "-startup" found on command line.
- UINT32 NoStartup : 1; ///< Was "-nostartup" found on command line.
- UINT32 NoConsoleOut : 1; ///< Was "-noconsoleout" found on command line.
- UINT32 NoConsoleIn : 1; ///< Was "-noconsolein" found on command line.
- UINT32 NoInterrupt : 1; ///< Was "-nointerrupt" found on command line.
- UINT32 NoMap : 1; ///< Was "-nomap" found on command line.
- UINT32 NoVersion : 1; ///< Was "-noversion" found on command line.
- UINT32 Delay : 1; ///< Was "-delay[:n] found on command line
- UINT32 Exit : 1; ///< Was "-_exit" found on command line
- UINT32 NoNest : 1; ///< Was "-nonest" found on command line
- UINT32 Reserved : 7; ///< Extra bits
-} SHELL_BITS;
-
-typedef union {
- SHELL_BITS Bits;
- UINT16 AllBits;
-} SHELL_BIT_UNION;
-
-typedef struct {
- SHELL_BIT_UNION BitUnion;
- UINTN Delay; ///< Seconds of delay default:5.
- CHAR16 *FileName; ///< Filename to run upon successful initialization.
- CHAR16 *FileOptions; ///< Options to pass to FileName.
-} SHELL_INIT_SETTINGS;
-
-typedef struct {
- BUFFER_LIST CommandHistory;
- UINTN VisibleRowNumber;
- UINTN OriginalVisibleRowNumber;
- BOOLEAN InsertMode; ///< Is the current typing mode insert (FALSE = overwrite).
-} SHELL_VIEWING_SETTINGS;
-
-typedef struct {
- EFI_SHELL_PARAMETERS_PROTOCOL *NewShellParametersProtocol;
- EFI_SHELL_PROTOCOL *NewEfiShellProtocol;
- BOOLEAN PageBreakEnabled;
- BOOLEAN RootShellInstance;
- SHELL_INIT_SETTINGS ShellInitSettings;
- BUFFER_LIST BufferToFreeList; ///< List of buffers that were returned to the user to free.
- SHELL_VIEWING_SETTINGS ViewingSettings;
- EFI_HII_HANDLE HiiHandle; ///< Handle from HiiLib.
- UINTN LogScreenCount; ///< How many screens of log information to save.
- EFI_EVENT UserBreakTimer; ///< Timer event for polling for CTRL-C.
- EFI_DEVICE_PATH_PROTOCOL *ImageDevPath; ///< DevicePath for ourselves.
- EFI_DEVICE_PATH_PROTOCOL *FileDevPath; ///< DevicePath for ourselves.
- CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; ///< Pointer for ConsoleInformation.
- EFI_SHELL_PARAMETERS_PROTOCOL *OldShellParameters; ///< old shell parameters to reinstall upon exiting.
- SHELL_PROTOCOL_HANDLE_LIST OldShellList; ///< List of other instances to reinstall when closing.
- SPLIT_LIST SplitList; ///< List of Splits in FILO stack.
- VOID *CtrlCNotifyHandle1; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
- VOID *CtrlCNotifyHandle2; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
- VOID *CtrlCNotifyHandle3; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
- VOID *CtrlCNotifyHandle4; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
- VOID *CtrlSNotifyHandle1; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
- VOID *CtrlSNotifyHandle2; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
- VOID *CtrlSNotifyHandle3; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
- VOID *CtrlSNotifyHandle4; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
- BOOLEAN HaltOutput; ///< TRUE to start a CTRL-S halt.
-} SHELL_INFO;
-
-#pragma pack(1)
-///
-/// HII specific Vendor Device Path definition.
-///
-typedef struct {
- VENDOR_DEVICE_PATH VendorDevicePath;
- EFI_DEVICE_PATH_PROTOCOL End;
-} SHELL_MAN_HII_VENDOR_DEVICE_PATH;
-#pragma pack()
-
-extern SHELL_INFO ShellInfoObject;
-
-/**
- Converts the command line to its post-processed form. this replaces variables and alias' per UEFI Shell spec.
-
- @param[in,out] CmdLine pointer to the command line to update
-
- @retval EFI_SUCCESS The operation was successful
- @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
- @return some other error occurred
-**/
-EFI_STATUS
-ProcessCommandLineToFinal (
- IN OUT CHAR16 **CmdLine
- );
-
-/**
- Function to update the shell variable "lasterror".
-
- @param[in] ErrorCode the error code to put into lasterror
-**/
-EFI_STATUS
-SetLastError (
- IN CONST SHELL_STATUS ErrorCode
- );
-
-/**
- Sets all the alias' that were registered with the ShellCommandLib library.
-
- @retval EFI_SUCCESS all init commands were run successfully.
-**/
-EFI_STATUS
-SetBuiltInAlias (
- VOID
- );
-
-/**
- This function will populate the 2 device path protocol parameters based on the
- global gImageHandle. the DevPath will point to the device path for the handle that has
- loaded image protocol installed on it. the FilePath will point to the device path
- for the file that was loaded.
-
- @param[in, out] DevPath on a successful return the device path to the loaded image
- @param[in, out] FilePath on a successful return the device path to the file
-
- @retval EFI_SUCCESS the 2 device paths were successfully returned.
- @return other a error from gBS->HandleProtocol
-
- @sa HandleProtocol
-**/
-EFI_STATUS
-GetDevicePathsForImageAndFile (
- IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,
- IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
- );
-
-/**
- Process all Uefi Shell 2.0 command line options.
-
- see Uefi Shell 2.0 section 3.2 for full details.
-
- the command line should resemble the following:
-
- shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
-
- ShellOpt options Options which control the initialization behavior of the shell.
- These options are read from the EFI global variable "ShellOpt"
- and are processed before options or file-name.
-
- options Options which control the initialization behavior of the shell.
-
- file-name The name of a UEFI shell application or script to be executed
- after initialization is complete. By default, if file-name is
- specified, then -nostartup is implied. Scripts are not supported
- by level 0.
-
- file-nameoptions The command-line options that are passed to file-name when it
- is invoked.
-
- This will initialize the ShellInitSettings global variable.
-
- @retval EFI_SUCCESS the variable is initialized.
-**/
-EFI_STATUS
-ProcessCommandLine (
- VOID
- );
-
-/**
- Handles all interaction with the default startup script.
-
- this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
-
- @param[in] ImagePath The path to the image for shell. The first place to look for the startup script.
- @param[in] FilePath The path to the file for shell. The second place to look for the startup script.
-
- @retval EFI_SUCCESS The variable is initialized.
-**/
-EFI_STATUS
-DoStartupScript (
- IN EFI_DEVICE_PATH_PROTOCOL *ImagePath,
- IN EFI_DEVICE_PATH_PROTOCOL *FilePath
- );
-
-/**
- Function to perform the shell prompt looping. It will do a single prompt,
- dispatch the result, and then return. It is expected that the caller will
- call this function in a loop many times.
-
- @retval EFI_SUCCESS
- @retval RETURN_ABORTED
-**/
-EFI_STATUS
-DoShellPrompt (
- VOID
- );
-
-/**
- Add a buffer to the Buffer To Free List for safely returning buffers to other
- places without risking letting them modify internal shell information.
-
- @param Buffer Something to pass to FreePool when the shell is exiting.
-**/
-VOID *
-AddBufferToFreeList (
- VOID *Buffer
- );
-
-/**
- Add a buffer to the Command History List.
-
- @param Buffer[in] The line buffer to add.
-**/
-VOID
-AddLineToCommandHistory (
- IN CONST CHAR16 *Buffer
- );
-
-/**
- Function will process and run a command line.
-
- This will determine if the command line represents an internal shell command or dispatch an external application.
-
- @param[in] CmdLine the command line to parse
-
- @retval EFI_SUCCESS the command was completed
- @retval EFI_ABORTED the command's operation was aborted
-**/
-EFI_STATUS
-RunCommand (
- IN CONST CHAR16 *CmdLine
- );
-
-/**
- Function will process and run a command line.
-
- This will determine if the command line represents an internal shell
- command or dispatch an external application.
-
- @param[in] CmdLine The command line to parse.
- @param[out] CommandStatus The status from the command line.
-
- @retval EFI_SUCCESS The command was completed.
- @retval EFI_ABORTED The command's operation was aborted.
-**/
-EFI_STATUS
-RunShellCommand (
- IN CONST CHAR16 *CmdLine,
- OUT EFI_STATUS *CommandStatus
- );
-
-/**
- Function to process a NSH script file via SHELL_FILE_HANDLE.
-
- @param[in] Handle The handle to the already opened file.
- @param[in] Name The name of the script file.
-
- @retval EFI_SUCCESS the script completed successfully
-**/
-EFI_STATUS
-RunScriptFileHandle (
- IN SHELL_FILE_HANDLE Handle,
- IN CONST CHAR16 *Name
- );
-
-/**
- Function to process a NSH script file.
-
- @param[in] ScriptPath Pointer to the script file name (including file system path).
- @param[in] Handle the handle of the script file already opened.
- @param[in] CmdLine the command line to run.
- @param[in] ParamProtocol the shell parameters protocol pointer
-
- @retval EFI_SUCCESS the script completed successfully
-**/
-EFI_STATUS
-RunScriptFile (
- IN CONST CHAR16 *ScriptPath,
- IN SHELL_FILE_HANDLE Handle OPTIONAL,
- IN CONST CHAR16 *CmdLine,
- IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol
- );
-
-/**
- Return the pointer to the first occurrence of any character from a list of characters.
-
- @param[in] String the string to parse
- @param[in] CharacterList the list of character to look for
- @param[in] EscapeCharacter An escape character to skip
-
- @return the location of the first character in the string
- @retval CHAR_NULL no instance of any character in CharacterList was found in String
-**/
-CONST CHAR16 *
-FindFirstCharacter (
- IN CONST CHAR16 *String,
- IN CONST CHAR16 *CharacterList,
- IN CONST CHAR16 EscapeCharacter
- );
-
-/**
- Cleans off leading and trailing spaces and tabs.
-
- @param[in] String pointer to the string to trim them off.
-**/
-EFI_STATUS
-TrimSpaces (
- IN CHAR16 **String
- );
-
-/**
-
- Create a new buffer list and stores the old one to OldBufferList
-
- @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
-**/
-VOID
-SaveBufferList (
- OUT LIST_ENTRY *OldBufferList
- );
-
-/**
- Restore previous nodes into BufferToFreeList .
-
- @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
-**/
-VOID
-RestoreBufferList (
- IN OUT LIST_ENTRY *OldBufferList
- );
-
-#endif //_SHELL_INTERNAL_HEADER_
+/** @file + function definitions for internal to shell functions. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR> + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SHELL_INTERNAL_HEADER_ +#define _SHELL_INTERNAL_HEADER_ + +#include <Uefi.h> + +#include <Guid/ShellVariableGuid.h> +#include <Guid/ShellAliasGuid.h> + +#include <Protocol/LoadedImage.h> +#include <Protocol/SimpleTextOut.h> +#include <Protocol/Shell.h> +#include <Protocol/EfiShellInterface.h> +#include <Protocol/EfiShellEnvironment2.h> +#include <Protocol/ShellParameters.h> +#include <Protocol/BlockIo.h> +#include <Protocol/HiiPackageList.h> + +#include <Library/BaseLib.h> +#include <Library/UefiApplicationEntryPoint.h> +#include <Library/UefiLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/ShellCommandLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/DevicePathLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/PcdLib.h> +#include <Library/ShellLib.h> +#include <Library/SortLib.h> +#include <Library/HiiLib.h> +#include <Library/PrintLib.h> +#include <Library/HandleParsingLib.h> +#include <Library/FileHandleLib.h> +#include <Library/UefiHiiServicesLib.h> + +#include "ShellParametersProtocol.h" +#include "ShellProtocol.h" +#include "ShellEnvVar.h" +#include "ConsoleLogger.h" +#include "ShellManParser.h" +#include "ConsoleWrappers.h" +#include "FileHandleWrappers.h" + +extern CONST CHAR16 mNoNestingEnvVarName[]; +extern CONST CHAR16 mNoNestingTrue[]; +extern CONST CHAR16 mNoNestingFalse[]; + +typedef struct { + LIST_ENTRY Link; ///< Standard linked list handler. + SHELL_FILE_HANDLE SplitStdOut; ///< ConsoleOut for use in the split. + SHELL_FILE_HANDLE SplitStdIn; ///< ConsoleIn for use in the split. +} SPLIT_LIST; + +typedef struct { + UINT32 Startup : 1; ///< Was "-startup" found on command line. + UINT32 NoStartup : 1; ///< Was "-nostartup" found on command line. + UINT32 NoConsoleOut : 1; ///< Was "-noconsoleout" found on command line. + UINT32 NoConsoleIn : 1; ///< Was "-noconsolein" found on command line. + UINT32 NoInterrupt : 1; ///< Was "-nointerrupt" found on command line. + UINT32 NoMap : 1; ///< Was "-nomap" found on command line. + UINT32 NoVersion : 1; ///< Was "-noversion" found on command line. + UINT32 Delay : 1; ///< Was "-delay[:n] found on command line + UINT32 Exit : 1; ///< Was "-_exit" found on command line + UINT32 NoNest : 1; ///< Was "-nonest" found on command line + UINT32 Reserved : 7; ///< Extra bits +} SHELL_BITS; + +typedef union { + SHELL_BITS Bits; + UINT16 AllBits; +} SHELL_BIT_UNION; + +typedef struct { + SHELL_BIT_UNION BitUnion; + UINTN Delay; ///< Seconds of delay default:5. + CHAR16 *FileName; ///< Filename to run upon successful initialization. + CHAR16 *FileOptions; ///< Options to pass to FileName. +} SHELL_INIT_SETTINGS; + +typedef struct { + BUFFER_LIST CommandHistory; + UINTN VisibleRowNumber; + UINTN OriginalVisibleRowNumber; + BOOLEAN InsertMode; ///< Is the current typing mode insert (FALSE = overwrite). +} SHELL_VIEWING_SETTINGS; + +typedef struct { + EFI_SHELL_PARAMETERS_PROTOCOL *NewShellParametersProtocol; + EFI_SHELL_PROTOCOL *NewEfiShellProtocol; + BOOLEAN PageBreakEnabled; + BOOLEAN RootShellInstance; + SHELL_INIT_SETTINGS ShellInitSettings; + BUFFER_LIST BufferToFreeList; ///< List of buffers that were returned to the user to free. + SHELL_VIEWING_SETTINGS ViewingSettings; + EFI_HII_HANDLE HiiHandle; ///< Handle from HiiLib. + UINTN LogScreenCount; ///< How many screens of log information to save. + EFI_EVENT UserBreakTimer; ///< Timer event for polling for CTRL-C. + EFI_DEVICE_PATH_PROTOCOL *ImageDevPath; ///< DevicePath for ourselves. + EFI_DEVICE_PATH_PROTOCOL *FileDevPath; ///< DevicePath for ourselves. + CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; ///< Pointer for ConsoleInformation. + EFI_SHELL_PARAMETERS_PROTOCOL *OldShellParameters; ///< old shell parameters to reinstall upon exiting. + SHELL_PROTOCOL_HANDLE_LIST OldShellList; ///< List of other instances to reinstall when closing. + SPLIT_LIST SplitList; ///< List of Splits in FILO stack. + VOID *CtrlCNotifyHandle1; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify. + VOID *CtrlCNotifyHandle2; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify. + VOID *CtrlCNotifyHandle3; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify. + VOID *CtrlCNotifyHandle4; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify. + VOID *CtrlSNotifyHandle1; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify. + VOID *CtrlSNotifyHandle2; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify. + VOID *CtrlSNotifyHandle3; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify. + VOID *CtrlSNotifyHandle4; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify. + BOOLEAN HaltOutput; ///< TRUE to start a CTRL-S halt. +} SHELL_INFO; + +#pragma pack(1) +/// +/// HII specific Vendor Device Path definition. +/// +typedef struct { + VENDOR_DEVICE_PATH VendorDevicePath; + EFI_DEVICE_PATH_PROTOCOL End; +} SHELL_MAN_HII_VENDOR_DEVICE_PATH; +#pragma pack() + +extern SHELL_INFO ShellInfoObject; + +/** + Converts the command line to its post-processed form. this replaces variables and alias' per UEFI Shell spec. + + @param[in,out] CmdLine pointer to the command line to update + + @retval EFI_SUCCESS The operation was successful + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @return some other error occurred +**/ +EFI_STATUS +ProcessCommandLineToFinal ( + IN OUT CHAR16 **CmdLine + ); + +/** + Function to update the shell variable "lasterror". + + @param[in] ErrorCode the error code to put into lasterror +**/ +EFI_STATUS +SetLastError ( + IN CONST SHELL_STATUS ErrorCode + ); + +/** + Sets all the alias' that were registered with the ShellCommandLib library. + + @retval EFI_SUCCESS all init commands were run successfully. +**/ +EFI_STATUS +SetBuiltInAlias ( + VOID + ); + +/** + This function will populate the 2 device path protocol parameters based on the + global gImageHandle. the DevPath will point to the device path for the handle that has + loaded image protocol installed on it. the FilePath will point to the device path + for the file that was loaded. + + @param[in, out] DevPath on a successful return the device path to the loaded image + @param[in, out] FilePath on a successful return the device path to the file + + @retval EFI_SUCCESS the 2 device paths were successfully returned. + @return other a error from gBS->HandleProtocol + + @sa HandleProtocol +**/ +EFI_STATUS +GetDevicePathsForImageAndFile ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath, + IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath + ); + +/** + Process all Uefi Shell 2.0 command line options. + + see Uefi Shell 2.0 section 3.2 for full details. + + the command line should resemble the following: + + shell.efi [ShellOpt-options] [options] [file-name [file-name-options]] + + ShellOpt options Options which control the initialization behavior of the shell. + These options are read from the EFI global variable "ShellOpt" + and are processed before options or file-name. + + options Options which control the initialization behavior of the shell. + + file-name The name of a UEFI shell application or script to be executed + after initialization is complete. By default, if file-name is + specified, then -nostartup is implied. Scripts are not supported + by level 0. + + file-nameoptions The command-line options that are passed to file-name when it + is invoked. + + This will initialize the ShellInitSettings global variable. + + @retval EFI_SUCCESS the variable is initialized. +**/ +EFI_STATUS +ProcessCommandLine ( + VOID + ); + +/** + Handles all interaction with the default startup script. + + this will check that the correct command line parameters were passed, handle the delay, and then start running the script. + + @param[in] ImagePath The path to the image for shell. The first place to look for the startup script. + @param[in] FilePath The path to the file for shell. The second place to look for the startup script. + + @retval EFI_SUCCESS The variable is initialized. +**/ +EFI_STATUS +DoStartupScript ( + IN EFI_DEVICE_PATH_PROTOCOL *ImagePath, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath + ); + +/** + Function to perform the shell prompt looping. It will do a single prompt, + dispatch the result, and then return. It is expected that the caller will + call this function in a loop many times. + + @retval EFI_SUCCESS + @retval RETURN_ABORTED +**/ +EFI_STATUS +DoShellPrompt ( + VOID + ); + +/** + Add a buffer to the Buffer To Free List for safely returning buffers to other + places without risking letting them modify internal shell information. + + @param Buffer Something to pass to FreePool when the shell is exiting. +**/ +VOID * +AddBufferToFreeList ( + VOID *Buffer + ); + +/** + Add a buffer to the Command History List. + + @param Buffer[in] The line buffer to add. +**/ +VOID +AddLineToCommandHistory ( + IN CONST CHAR16 *Buffer + ); + +/** + Function will process and run a command line. + + This will determine if the command line represents an internal shell command or dispatch an external application. + + @param[in] CmdLine the command line to parse + + @retval EFI_SUCCESS the command was completed + @retval EFI_ABORTED the command's operation was aborted +**/ +EFI_STATUS +RunCommand ( + IN CONST CHAR16 *CmdLine + ); + +/** + Function will process and run a command line. + + This will determine if the command line represents an internal shell + command or dispatch an external application. + + @param[in] CmdLine The command line to parse. + @param[out] CommandStatus The status from the command line. + + @retval EFI_SUCCESS The command was completed. + @retval EFI_ABORTED The command's operation was aborted. +**/ +EFI_STATUS +RunShellCommand ( + IN CONST CHAR16 *CmdLine, + OUT EFI_STATUS *CommandStatus + ); + +/** + Function to process a NSH script file via SHELL_FILE_HANDLE. + + @param[in] Handle The handle to the already opened file. + @param[in] Name The name of the script file. + + @retval EFI_SUCCESS the script completed successfully +**/ +EFI_STATUS +RunScriptFileHandle ( + IN SHELL_FILE_HANDLE Handle, + IN CONST CHAR16 *Name + ); + +/** + Function to process a NSH script file. + + @param[in] ScriptPath Pointer to the script file name (including file system path). + @param[in] Handle the handle of the script file already opened. + @param[in] CmdLine the command line to run. + @param[in] ParamProtocol the shell parameters protocol pointer + + @retval EFI_SUCCESS the script completed successfully +**/ +EFI_STATUS +RunScriptFile ( + IN CONST CHAR16 *ScriptPath, + IN SHELL_FILE_HANDLE Handle OPTIONAL, + IN CONST CHAR16 *CmdLine, + IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol + ); + +/** + Return the pointer to the first occurrence of any character from a list of characters. + + @param[in] String the string to parse + @param[in] CharacterList the list of character to look for + @param[in] EscapeCharacter An escape character to skip + + @return the location of the first character in the string + @retval CHAR_NULL no instance of any character in CharacterList was found in String +**/ +CONST CHAR16 * +FindFirstCharacter ( + IN CONST CHAR16 *String, + IN CONST CHAR16 *CharacterList, + IN CONST CHAR16 EscapeCharacter + ); + +/** + Cleans off leading and trailing spaces and tabs. + + @param[in] String pointer to the string to trim them off. +**/ +EFI_STATUS +TrimSpaces ( + IN CHAR16 **String + ); + +/** + + Create a new buffer list and stores the old one to OldBufferList + + @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList. +**/ +VOID +SaveBufferList ( + OUT LIST_ENTRY *OldBufferList + ); + +/** + Restore previous nodes into BufferToFreeList . + + @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList. +**/ +VOID +RestoreBufferList ( + IN OUT LIST_ENTRY *OldBufferList + ); + +#endif //_SHELL_INTERNAL_HEADER_ diff --git a/ShellPkg/Application/Shell/Shell.inf b/ShellPkg/Application/Shell/Shell.inf index f1e41de133..2487e3d390 100644 --- a/ShellPkg/Application/Shell/Shell.inf +++ b/ShellPkg/Application/Shell/Shell.inf @@ -1,106 +1,106 @@ -## @file
-# This is the shell application
-#
-# (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR>
-# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
-#
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
-#
-##
-
-[Defines]
- INF_VERSION = 0x00010006
- BASE_NAME = Shell
- FILE_GUID = 7C04A583-9E3E-4f1c-AD65-E05268D0B4D1 # gUefiShellFileGuid
- MODULE_TYPE = UEFI_APPLICATION
- VERSION_STRING = 1.0
- ENTRY_POINT = UefiMain
-
-#
-# The following information is for reference only and not required by the build tools.
-#
-# VALID_ARCHITECTURES = IA32 X64 EBC
-#
-
-[Sources]
- Shell.c
- Shell.h
- ShellParametersProtocol.c
- ShellParametersProtocol.h
- ShellProtocol.c
- ShellProtocol.h
- FileHandleWrappers.c
- FileHandleWrappers.h
- FileHandleInternal.h
- ShellEnvVar.c
- ShellEnvVar.h
- ShellManParser.c
- ShellManParser.h
- Shell.uni
- ConsoleLogger.c
- ConsoleLogger.h
- ConsoleWrappers.c
- ConsoleWrappers.h
-
-[Packages]
- MdePkg/MdePkg.dec
- ShellPkg/ShellPkg.dec
- MdeModulePkg/MdeModulePkg.dec
-
-[LibraryClasses]
- BaseLib
- UefiApplicationEntryPoint
- UefiLib
- DebugLib
- MemoryAllocationLib
- ShellCommandLib
- UefiRuntimeServicesTableLib
- UefiBootServicesTableLib
- DevicePathLib
- BaseMemoryLib
- PcdLib
- FileHandleLib
- PrintLib
- HiiLib
- SortLib
- HandleParsingLib
- UefiHiiServicesLib
-
-[Guids]
- gShellVariableGuid ## SOMETIMES_CONSUMES ## GUID
- gShellAliasGuid ## SOMETIMES_CONSUMES ## GUID
- gShellAliasGuid ## SOMETIMES_PRODUCES ## GUID
-
-[Protocols]
- gEfiShellProtocolGuid ## PRODUCES
- ## SOMETIMES_CONSUMES
- gEfiShellParametersProtocolGuid ## PRODUCES
- ## SOMETIMES_CONSUMES
-
-# gEfiShellEnvironment2Guid ## SOMETIMES_PRODUCES
-# gEfiShellInterfaceGuid ## SOMETIMES_PRODUCES
-
- gEfiSimpleTextInputExProtocolGuid ## CONSUMES
- gEfiSimpleTextInProtocolGuid ## CONSUMES
- gEfiSimpleTextOutProtocolGuid ## CONSUMES
- gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES
- gEfiLoadedImageProtocolGuid ## CONSUMES
- gEfiComponentName2ProtocolGuid ## SOMETIMES_CONSUMES
- gEfiUnicodeCollation2ProtocolGuid ## CONSUMES
- gEfiDevicePathProtocolGuid ## CONSUMES
- gEfiHiiPackageListProtocolGuid ## SOMETIMES_PRODUCES
-
-[Pcd]
- gEfiShellPkgTokenSpaceGuid.PcdShellSupportLevel ## CONSUMES
- gEfiShellPkgTokenSpaceGuid.PcdShellSupportOldProtocols ## CONSUMES
- gEfiShellPkgTokenSpaceGuid.PcdShellRequireHiiPlatform ## CONSUMES
- gEfiShellPkgTokenSpaceGuid.PcdShellSupportFrameworkHii ## CONSUMES
- gEfiShellPkgTokenSpaceGuid.PcdShellPageBreakDefault ## CONSUMES
- gEfiShellPkgTokenSpaceGuid.PcdShellInsertModeDefault ## CONSUMES
- gEfiShellPkgTokenSpaceGuid.PcdShellScreenLogCount ## CONSUMES
- gEfiShellPkgTokenSpaceGuid.PcdShellPrintBufferSize ## CONSUMES
- gEfiShellPkgTokenSpaceGuid.PcdShellForceConsole ## CONSUMES
- gEfiShellPkgTokenSpaceGuid.PcdShellSupplier ## CONSUMES
- gEfiShellPkgTokenSpaceGuid.PcdShellMaxHistoryCommandCount ## CONSUMES
- gEfiShellPkgTokenSpaceGuid.PcdShellDefaultDelay ## CONSUMES
+## @file +# This is the shell application +# +# (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR> +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = Shell + FILE_GUID = 7C04A583-9E3E-4f1c-AD65-E05268D0B4D1 # gUefiShellFileGuid + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = UefiMain + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + Shell.c + Shell.h + ShellParametersProtocol.c + ShellParametersProtocol.h + ShellProtocol.c + ShellProtocol.h + FileHandleWrappers.c + FileHandleWrappers.h + FileHandleInternal.h + ShellEnvVar.c + ShellEnvVar.h + ShellManParser.c + ShellManParser.h + Shell.uni + ConsoleLogger.c + ConsoleLogger.h + ConsoleWrappers.c + ConsoleWrappers.h + +[Packages] + MdePkg/MdePkg.dec + ShellPkg/ShellPkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + UefiApplicationEntryPoint + UefiLib + DebugLib + MemoryAllocationLib + ShellCommandLib + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + DevicePathLib + BaseMemoryLib + PcdLib + FileHandleLib + PrintLib + HiiLib + SortLib + HandleParsingLib + UefiHiiServicesLib + +[Guids] + gShellVariableGuid ## SOMETIMES_CONSUMES ## GUID + gShellAliasGuid ## SOMETIMES_CONSUMES ## GUID + gShellAliasGuid ## SOMETIMES_PRODUCES ## GUID + +[Protocols] + gEfiShellProtocolGuid ## PRODUCES + ## SOMETIMES_CONSUMES + gEfiShellParametersProtocolGuid ## PRODUCES + ## SOMETIMES_CONSUMES + +# gEfiShellEnvironment2Guid ## SOMETIMES_PRODUCES +# gEfiShellInterfaceGuid ## SOMETIMES_PRODUCES + + gEfiSimpleTextInputExProtocolGuid ## CONSUMES + gEfiSimpleTextInProtocolGuid ## CONSUMES + gEfiSimpleTextOutProtocolGuid ## CONSUMES + gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES + gEfiLoadedImageProtocolGuid ## CONSUMES + gEfiComponentName2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiUnicodeCollation2ProtocolGuid ## CONSUMES + gEfiDevicePathProtocolGuid ## CONSUMES + gEfiHiiPackageListProtocolGuid ## SOMETIMES_PRODUCES + +[Pcd] + gEfiShellPkgTokenSpaceGuid.PcdShellSupportLevel ## CONSUMES + gEfiShellPkgTokenSpaceGuid.PcdShellSupportOldProtocols ## CONSUMES + gEfiShellPkgTokenSpaceGuid.PcdShellRequireHiiPlatform ## CONSUMES + gEfiShellPkgTokenSpaceGuid.PcdShellSupportFrameworkHii ## CONSUMES + gEfiShellPkgTokenSpaceGuid.PcdShellPageBreakDefault ## CONSUMES + gEfiShellPkgTokenSpaceGuid.PcdShellInsertModeDefault ## CONSUMES + gEfiShellPkgTokenSpaceGuid.PcdShellScreenLogCount ## CONSUMES + gEfiShellPkgTokenSpaceGuid.PcdShellPrintBufferSize ## CONSUMES + gEfiShellPkgTokenSpaceGuid.PcdShellForceConsole ## CONSUMES + gEfiShellPkgTokenSpaceGuid.PcdShellSupplier ## CONSUMES + gEfiShellPkgTokenSpaceGuid.PcdShellMaxHistoryCommandCount ## CONSUMES + gEfiShellPkgTokenSpaceGuid.PcdShellDefaultDelay ## CONSUMES diff --git a/ShellPkg/Application/Shell/Shell.uni b/ShellPkg/Application/Shell/Shell.uni index 7dd0072601..04a9e3d50d 100644 --- a/ShellPkg/Application/Shell/Shell.uni +++ b/ShellPkg/Application/Shell/Shell.uni @@ -1,52 +1,52 @@ -// *++
-//
-// (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
-// Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved. <BR>
-// SPDX-License-Identifier: BSD-2-Clause-Patent
-//
-// Module Name:
-//
-// ShellStrings.uni
-//
-// Abstract:
-//
-// String definitions for the main UEFI Shell 2.0 application
-//
-// Revision History:
-//
-// --*/
-
-/=#
-
-#langdef en-US "English"
-
-#string STR_SHELL_CURDIR #language en-US "%E%s\> %N"
-#string STR_GEN_PROBLEM #language en-US "The argument '%B%s%N' is incorrect.\r\n"
-#string STR_SHELL_SHELL #language en-US "%EShell> %N"
-
-#string STR_SHELL_STARTUP_QUESTION #language en-US "%NPress %HESC%N in %d seconds to skip %Estartup.nsh%N or any other key to continue."
-
-#string STR_SHELL_ENTER_TO_CONT #language en-US "%NPress ENTER to continue:%E"
-#string STR_SHELL_QUIT_CONT #language en-US "%NPress ENTER to continue or 'Q' break:%E"
-
-#string STR_SHELL_NOT_FOUND #language en-US "%N'%E%s%N' is not recognized as an internal or external command, operable program, or script file.\r\n"
-
-#string STR_SHELL_CRLF #language en-US "\r\n"
-
-#string STR_SHELL_ERROR #language en-US "%NCommand Error Status: %r\r\n"
-#string STR_SHELL_ERROR_SCRIPT #language en-US "%NScript Error Status: %r (line number %d)\r\n"
-
-#string STR_SHELL_INVALID_MAPPING #language en-US "%N'%B%s%N' is not a valid mapping.\r\n"
-#string STR_SHELL_INVALID_SPLIT #language en-US "Invalid use of pipe (%B|%N).\r\n"
-
-#string STR_SHELL_INVALID_REDIR #language en-US "Unable to redirect file.\r\n"
-#string STR_SHELL_REDUNDA_REDIR #language en-US "Redundant redirection specified.\r\n"
-
-#string STR_VER_OUTPUT_MAIN_SHELL #language en-US "UEFI %s Shell v%d.%d\r\n"
-#string STR_VER_OUTPUT_MAIN_SUPPLIER #language en-US "%s\r\n"
-#string STR_VER_OUTPUT_MAIN_UEFI #language en-US "UEFI v%d.%02d (%s, 0x%08x)\r\n"
-
-#string STR_SHELL_NO_IN_EX #language en-US "No SimpleTextInputEx was found. CTRL-based features are not usable.\r\n"
-
-#string STR_SHELL_IMAGE_NOT_APP #language en-US "The image is not an application.\r\n"
-
+// *++ +// +// (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR> +// Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved. <BR> +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// Module Name: +// +// ShellStrings.uni +// +// Abstract: +// +// String definitions for the main UEFI Shell 2.0 application +// +// Revision History: +// +// --*/ + +/=# + +#langdef en-US "English" + +#string STR_SHELL_CURDIR #language en-US "%E%s\> %N" +#string STR_GEN_PROBLEM #language en-US "The argument '%B%s%N' is incorrect.\r\n" +#string STR_SHELL_SHELL #language en-US "%EShell> %N" + +#string STR_SHELL_STARTUP_QUESTION #language en-US "%NPress %HESC%N in %d seconds to skip %Estartup.nsh%N or any other key to continue." + +#string STR_SHELL_ENTER_TO_CONT #language en-US "%NPress ENTER to continue:%E" +#string STR_SHELL_QUIT_CONT #language en-US "%NPress ENTER to continue or 'Q' break:%E" + +#string STR_SHELL_NOT_FOUND #language en-US "%N'%E%s%N' is not recognized as an internal or external command, operable program, or script file.\r\n" + +#string STR_SHELL_CRLF #language en-US "\r\n" + +#string STR_SHELL_ERROR #language en-US "%NCommand Error Status: %r\r\n" +#string STR_SHELL_ERROR_SCRIPT #language en-US "%NScript Error Status: %r (line number %d)\r\n" + +#string STR_SHELL_INVALID_MAPPING #language en-US "%N'%B%s%N' is not a valid mapping.\r\n" +#string STR_SHELL_INVALID_SPLIT #language en-US "Invalid use of pipe (%B|%N).\r\n" + +#string STR_SHELL_INVALID_REDIR #language en-US "Unable to redirect file.\r\n" +#string STR_SHELL_REDUNDA_REDIR #language en-US "Redundant redirection specified.\r\n" + +#string STR_VER_OUTPUT_MAIN_SHELL #language en-US "UEFI %s Shell v%d.%d\r\n" +#string STR_VER_OUTPUT_MAIN_SUPPLIER #language en-US "%s\r\n" +#string STR_VER_OUTPUT_MAIN_UEFI #language en-US "UEFI v%d.%02d (%s, 0x%08x)\r\n" + +#string STR_SHELL_NO_IN_EX #language en-US "No SimpleTextInputEx was found. CTRL-based features are not usable.\r\n" + +#string STR_SHELL_IMAGE_NOT_APP #language en-US "The image is not an application.\r\n" + diff --git a/ShellPkg/Application/Shell/ShellEnvVar.c b/ShellPkg/Application/Shell/ShellEnvVar.c index b97cfe91b9..b422ec9249 100644 --- a/ShellPkg/Application/Shell/ShellEnvVar.c +++ b/ShellPkg/Application/Shell/ShellEnvVar.c @@ -1,601 +1,601 @@ -/** @file
- function declarations for shell environment functions.
-
- Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "Shell.h"
-
-#define INIT_NAME_BUFFER_SIZE 128
-#define INIT_DATA_BUFFER_SIZE 1024
-
-//
-// The list is used to cache the environment variables.
-//
-ENV_VAR_LIST gShellEnvVarList;
-
-/**
- Reports whether an environment variable is Volatile or Non-Volatile.
-
- @param EnvVarName The name of the environment variable in question
- @param Volatile Return TRUE if the environment variable is volatile
-
- @retval EFI_SUCCESS The volatile attribute is returned successfully
- @retval others Some errors happened.
-**/
-EFI_STATUS
-IsVolatileEnv (
- IN CONST CHAR16 *EnvVarName,
- OUT BOOLEAN *Volatile
- )
-{
- EFI_STATUS Status;
- UINTN Size;
- VOID *Buffer;
- UINT32 Attribs;
-
- ASSERT (Volatile != NULL);
-
- Size = 0;
- Buffer = NULL;
-
- //
- // get the variable
- //
- Status = gRT->GetVariable (
- (CHAR16 *)EnvVarName,
- &gShellVariableGuid,
- &Attribs,
- &Size,
- Buffer
- );
- if (Status == EFI_BUFFER_TOO_SMALL) {
- Buffer = AllocateZeroPool (Size);
- if (Buffer == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- Status = gRT->GetVariable (
- (CHAR16 *)EnvVarName,
- &gShellVariableGuid,
- &Attribs,
- &Size,
- Buffer
- );
- FreePool (Buffer);
- }
-
- //
- // not found means volatile
- //
- if (Status == EFI_NOT_FOUND) {
- *Volatile = TRUE;
- return EFI_SUCCESS;
- }
-
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- //
- // check for the Non Volatile bit
- //
- *Volatile = !(BOOLEAN)((Attribs & EFI_VARIABLE_NON_VOLATILE) == EFI_VARIABLE_NON_VOLATILE);
- return EFI_SUCCESS;
-}
-
-/**
- free function for ENV_VAR_LIST objects.
-
- @param[in] List The pointer to pointer to list.
-**/
-VOID
-FreeEnvironmentVariableList (
- IN LIST_ENTRY *List
- )
-{
- ENV_VAR_LIST *Node;
-
- ASSERT (List != NULL);
- if (List == NULL) {
- return;
- }
-
- for ( Node = (ENV_VAR_LIST *)GetFirstNode (List)
- ; !IsListEmpty (List)
- ; Node = (ENV_VAR_LIST *)GetFirstNode (List)
- )
- {
- ASSERT (Node != NULL);
- RemoveEntryList (&Node->Link);
- if (Node->Key != NULL) {
- FreePool (Node->Key);
- }
-
- if (Node->Val != NULL) {
- FreePool (Node->Val);
- }
-
- FreePool (Node);
- }
-}
-
-/**
- Creates a list of all Shell-Guid-based environment variables.
-
- @param[in, out] ListHead The pointer to pointer to LIST ENTRY object for
- storing this list.
-
- @retval EFI_SUCCESS the list was created successfully.
-**/
-EFI_STATUS
-GetEnvironmentVariableList (
- IN OUT LIST_ENTRY *ListHead
- )
-{
- CHAR16 *VariableName;
- UINTN NameSize;
- UINTN NameBufferSize;
- EFI_STATUS Status;
- EFI_GUID Guid;
- UINTN ValSize;
- UINTN ValBufferSize;
- ENV_VAR_LIST *VarList;
-
- if (ListHead == NULL) {
- return (EFI_INVALID_PARAMETER);
- }
-
- Status = EFI_SUCCESS;
-
- ValBufferSize = INIT_DATA_BUFFER_SIZE;
- NameBufferSize = INIT_NAME_BUFFER_SIZE;
- VariableName = AllocateZeroPool (NameBufferSize);
- if (VariableName == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- *VariableName = CHAR_NULL;
-
- while (!EFI_ERROR (Status)) {
- NameSize = NameBufferSize;
- Status = gRT->GetNextVariableName (&NameSize, VariableName, &Guid);
- if (Status == EFI_NOT_FOUND) {
- Status = EFI_SUCCESS;
- break;
- } else if (Status == EFI_BUFFER_TOO_SMALL) {
- NameBufferSize = NameSize > NameBufferSize * 2 ? NameSize : NameBufferSize * 2;
- SHELL_FREE_NON_NULL (VariableName);
- VariableName = AllocateZeroPool (NameBufferSize);
- if (VariableName == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- break;
- }
-
- NameSize = NameBufferSize;
- Status = gRT->GetNextVariableName (&NameSize, VariableName, &Guid);
- }
-
- if (!EFI_ERROR (Status) && CompareGuid (&Guid, &gShellVariableGuid)) {
- VarList = AllocateZeroPool (sizeof (ENV_VAR_LIST));
- if (VarList == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- } else {
- ValSize = ValBufferSize;
- //
- // We need another CHAR16 to save '\0' in VarList->Val.
- //
- VarList->Val = AllocateZeroPool (ValSize + sizeof (CHAR16));
- if (VarList->Val == NULL) {
- SHELL_FREE_NON_NULL (VarList);
- Status = EFI_OUT_OF_RESOURCES;
- break;
- }
-
- Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES (VariableName, &VarList->Atts, &ValSize, VarList->Val);
- if (Status == EFI_BUFFER_TOO_SMALL) {
- ValBufferSize = ValSize > ValBufferSize * 2 ? ValSize : ValBufferSize * 2;
- SHELL_FREE_NON_NULL (VarList->Val);
- //
- // We need another CHAR16 to save '\0' in VarList->Val.
- //
- VarList->Val = AllocateZeroPool (ValBufferSize + sizeof (CHAR16));
- if (VarList->Val == NULL) {
- SHELL_FREE_NON_NULL (VarList);
- Status = EFI_OUT_OF_RESOURCES;
- break;
- }
-
- ValSize = ValBufferSize;
- Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES (VariableName, &VarList->Atts, &ValSize, VarList->Val);
- }
-
- if (!EFI_ERROR (Status)) {
- VarList->Key = AllocateCopyPool (StrSize (VariableName), VariableName);
- if (VarList->Key == NULL) {
- SHELL_FREE_NON_NULL (VarList->Val);
- SHELL_FREE_NON_NULL (VarList);
- Status = EFI_OUT_OF_RESOURCES;
- } else {
- InsertTailList (ListHead, &VarList->Link);
- }
- } else {
- SHELL_FREE_NON_NULL (VarList->Val);
- SHELL_FREE_NON_NULL (VarList);
- }
- } // if (VarList == NULL) ... else ...
- } // compare guid
- } // while
-
- SHELL_FREE_NON_NULL (VariableName);
-
- if (EFI_ERROR (Status)) {
- FreeEnvironmentVariableList (ListHead);
- }
-
- return (Status);
-}
-
-/**
- Sets a list of all Shell-Guid-based environment variables. this will
- also eliminate all existing shell environment variables (even if they
- are not on the list).
-
- This function will also deallocate the memory from List.
-
- @param[in] ListHead The pointer to LIST_ENTRY from
- GetShellEnvVarList().
-
- @retval EFI_SUCCESS the list was Set successfully.
-**/
-EFI_STATUS
-SetEnvironmentVariableList (
- IN LIST_ENTRY *ListHead
- )
-{
- ENV_VAR_LIST VarList;
- ENV_VAR_LIST *Node;
- EFI_STATUS Status;
- UINTN Size;
-
- InitializeListHead (&VarList.Link);
-
- //
- // Delete all the current environment variables
- //
- Status = GetEnvironmentVariableList (&VarList.Link);
- ASSERT_EFI_ERROR (Status);
-
- for ( Node = (ENV_VAR_LIST *)GetFirstNode (&VarList.Link)
- ; !IsNull (&VarList.Link, &Node->Link)
- ; Node = (ENV_VAR_LIST *)GetNextNode (&VarList.Link, &Node->Link)
- )
- {
- if (Node->Key != NULL) {
- Status = SHELL_DELETE_ENVIRONMENT_VARIABLE (Node->Key);
- }
-
- ASSERT_EFI_ERROR (Status);
- }
-
- FreeEnvironmentVariableList (&VarList.Link);
-
- //
- // set all the variables from the list
- //
- for ( Node = (ENV_VAR_LIST *)GetFirstNode (ListHead)
- ; !IsNull (ListHead, &Node->Link)
- ; Node = (ENV_VAR_LIST *)GetNextNode (ListHead, &Node->Link)
- )
- {
- Size = StrSize (Node->Val) - sizeof (CHAR16);
- if (Node->Atts & EFI_VARIABLE_NON_VOLATILE) {
- Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV (Node->Key, Size, Node->Val);
- } else {
- Status = SHELL_SET_ENVIRONMENT_VARIABLE_V (Node->Key, Size, Node->Val);
- }
-
- ASSERT_EFI_ERROR (Status);
- }
-
- FreeEnvironmentVariableList (ListHead);
-
- return (Status);
-}
-
-/**
- sets a list of all Shell-Guid-based environment variables.
-
- @param Environment Points to a NULL-terminated array of environment
- variables with the format 'x=y', where x is the
- environment variable name and y is the value.
-
- @retval EFI_SUCCESS The command executed successfully.
- @retval EFI_INVALID_PARAMETER The parameter is invalid.
- @retval EFI_OUT_OF_RESOURCES Out of resources.
-
- @sa SetEnvironmentVariableList
-**/
-EFI_STATUS
-SetEnvironmentVariables (
- IN CONST CHAR16 **Environment
- )
-{
- CONST CHAR16 *CurrentString;
- UINTN CurrentCount;
- ENV_VAR_LIST *VarList;
- ENV_VAR_LIST *Node;
-
- VarList = NULL;
-
- if (Environment == NULL) {
- return (EFI_INVALID_PARAMETER);
- }
-
- //
- // Build a list identical to the ones used for get/set list functions above
- //
- for ( CurrentCount = 0
- ;
- ; CurrentCount++
- )
- {
- CurrentString = Environment[CurrentCount];
- if (CurrentString == NULL) {
- break;
- }
-
- ASSERT (StrStr (CurrentString, L"=") != NULL);
- Node = AllocateZeroPool (sizeof (ENV_VAR_LIST));
- if (Node == NULL) {
- SetEnvironmentVariableList (&VarList->Link);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- Node->Key = AllocateZeroPool ((StrStr (CurrentString, L"=") - CurrentString + 1) * sizeof (CHAR16));
- if (Node->Key == NULL) {
- SHELL_FREE_NON_NULL (Node);
- SetEnvironmentVariableList (&VarList->Link);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- //
- // Copy the string into the Key, leaving the last character allocated as NULL to terminate
- //
- StrnCpyS (
- Node->Key,
- StrStr (CurrentString, L"=") - CurrentString + 1,
- CurrentString,
- StrStr (CurrentString, L"=") - CurrentString
- );
-
- //
- // ValueSize = TotalSize - already removed size - size for '=' + size for terminator (the last 2 items cancel each other)
- //
- Node->Val = AllocateCopyPool (StrSize (CurrentString) - StrSize (Node->Key), CurrentString + StrLen (Node->Key) + 1);
- if (Node->Val == NULL) {
- SHELL_FREE_NON_NULL (Node->Key);
- SHELL_FREE_NON_NULL (Node);
- SetEnvironmentVariableList (&VarList->Link);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- Node->Atts = EFI_VARIABLE_BOOTSERVICE_ACCESS;
-
- if (VarList == NULL) {
- VarList = AllocateZeroPool (sizeof (ENV_VAR_LIST));
- if (VarList == NULL) {
- SHELL_FREE_NON_NULL (Node->Key);
- SHELL_FREE_NON_NULL (Node->Val);
- SHELL_FREE_NON_NULL (Node);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- InitializeListHead (&VarList->Link);
- }
-
- InsertTailList (&VarList->Link, &Node->Link);
- } // for loop
-
- //
- // set this new list as the set of all environment variables.
- // this function also frees the memory and deletes all pre-existing
- // shell-guid based environment variables.
- //
- return (SetEnvironmentVariableList (&VarList->Link));
-}
-
-/**
- Find an environment variable in the gShellEnvVarList.
-
- @param Key The name of the environment variable.
- @param Value The value of the environment variable, the buffer
- shoule be freed by the caller.
- @param ValueSize The size in bytes of the environment variable
- including the tailing CHAR_NELL.
- @param Atts The attributes of the variable.
-
- @retval EFI_SUCCESS The command executed successfully.
- @retval EFI_NOT_FOUND The environment variable is not found in
- gShellEnvVarList.
-
-**/
-EFI_STATUS
-ShellFindEnvVarInList (
- IN CONST CHAR16 *Key,
- OUT CHAR16 **Value,
- OUT UINTN *ValueSize,
- OUT UINT32 *Atts OPTIONAL
- )
-{
- ENV_VAR_LIST *Node;
-
- if ((Key == NULL) || (Value == NULL) || (ValueSize == NULL)) {
- return SHELL_INVALID_PARAMETER;
- }
-
- for ( Node = (ENV_VAR_LIST *)GetFirstNode (&gShellEnvVarList.Link)
- ; !IsNull (&gShellEnvVarList.Link, &Node->Link)
- ; Node = (ENV_VAR_LIST *)GetNextNode (&gShellEnvVarList.Link, &Node->Link)
- )
- {
- if ((Node->Key != NULL) && (StrCmp (Key, Node->Key) == 0)) {
- *Value = AllocateCopyPool (StrSize (Node->Val), Node->Val);
- *ValueSize = StrSize (Node->Val);
- if (Atts != NULL) {
- *Atts = Node->Atts;
- }
-
- return EFI_SUCCESS;
- }
- }
-
- return EFI_NOT_FOUND;
-}
-
-/**
- Add an environment variable into gShellEnvVarList.
-
- @param Key The name of the environment variable.
- @param Value The value of environment variable.
- @param ValueSize The size in bytes of the environment variable
- including the tailing CHAR_NULL
- @param Atts The attributes of the variable.
-
- @retval EFI_SUCCESS The environment variable was added to list successfully.
- @retval others Some errors happened.
-
-**/
-EFI_STATUS
-ShellAddEnvVarToList (
- IN CONST CHAR16 *Key,
- IN CONST CHAR16 *Value,
- IN UINTN ValueSize,
- IN UINT32 Atts
- )
-{
- ENV_VAR_LIST *Node;
- CHAR16 *LocalKey;
- CHAR16 *LocalValue;
-
- if ((Key == NULL) || (Value == NULL) || (ValueSize == 0)) {
- return EFI_INVALID_PARAMETER;
- }
-
- LocalValue = AllocateCopyPool (ValueSize, Value);
- if (LocalValue == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- //
- // Update the variable value if it exists in gShellEnvVarList.
- //
- for ( Node = (ENV_VAR_LIST *)GetFirstNode (&gShellEnvVarList.Link)
- ; !IsNull (&gShellEnvVarList.Link, &Node->Link)
- ; Node = (ENV_VAR_LIST *)GetNextNode (&gShellEnvVarList.Link, &Node->Link)
- )
- {
- if ((Node->Key != NULL) && (StrCmp (Key, Node->Key) == 0)) {
- Node->Atts = Atts;
- SHELL_FREE_NON_NULL (Node->Val);
- Node->Val = LocalValue;
- return EFI_SUCCESS;
- }
- }
-
- //
- // If the environment variable key doesn't exist in list just insert
- // a new node.
- //
- LocalKey = AllocateCopyPool (StrSize (Key), Key);
- if (LocalKey == NULL) {
- FreePool (LocalValue);
- return EFI_OUT_OF_RESOURCES;
- }
-
- Node = (ENV_VAR_LIST *)AllocateZeroPool (sizeof (ENV_VAR_LIST));
- if (Node == NULL) {
- FreePool (LocalKey);
- FreePool (LocalValue);
- return EFI_OUT_OF_RESOURCES;
- }
-
- Node->Key = LocalKey;
- Node->Val = LocalValue;
- Node->Atts = Atts;
- InsertTailList (&gShellEnvVarList.Link, &Node->Link);
-
- return EFI_SUCCESS;
-}
-
-/**
- Remove a specified environment variable in gShellEnvVarList.
-
- @param Key The name of the environment variable.
-
- @retval EFI_SUCCESS The command executed successfully.
- @retval EFI_NOT_FOUND The environment variable is not found in
- gShellEnvVarList.
-**/
-EFI_STATUS
-ShellRemvoeEnvVarFromList (
- IN CONST CHAR16 *Key
- )
-{
- ENV_VAR_LIST *Node;
-
- if (Key == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- for ( Node = (ENV_VAR_LIST *)GetFirstNode (&gShellEnvVarList.Link)
- ; !IsNull (&gShellEnvVarList.Link, &Node->Link)
- ; Node = (ENV_VAR_LIST *)GetNextNode (&gShellEnvVarList.Link, &Node->Link)
- )
- {
- if ((Node->Key != NULL) && (StrCmp (Key, Node->Key) == 0)) {
- SHELL_FREE_NON_NULL (Node->Key);
- SHELL_FREE_NON_NULL (Node->Val);
- RemoveEntryList (&Node->Link);
- SHELL_FREE_NON_NULL (Node);
- return EFI_SUCCESS;
- }
- }
-
- return EFI_NOT_FOUND;
-}
-
-/**
- Initialize the gShellEnvVarList and cache all Shell-Guid-based environment
- variables.
-
-**/
-EFI_STATUS
-ShellInitEnvVarList (
- VOID
- )
-{
- EFI_STATUS Status;
-
- InitializeListHead (&gShellEnvVarList.Link);
- Status = GetEnvironmentVariableList (&gShellEnvVarList.Link);
-
- return Status;
-}
-
-/**
- Destructe the gShellEnvVarList.
-
-**/
-VOID
-ShellFreeEnvVarList (
- VOID
- )
-{
- FreeEnvironmentVariableList (&gShellEnvVarList.Link);
- InitializeListHead (&gShellEnvVarList.Link);
-
- return;
-}
+/** @file + function declarations for shell environment functions. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Shell.h" + +#define INIT_NAME_BUFFER_SIZE 128 +#define INIT_DATA_BUFFER_SIZE 1024 + +// +// The list is used to cache the environment variables. +// +ENV_VAR_LIST gShellEnvVarList; + +/** + Reports whether an environment variable is Volatile or Non-Volatile. + + @param EnvVarName The name of the environment variable in question + @param Volatile Return TRUE if the environment variable is volatile + + @retval EFI_SUCCESS The volatile attribute is returned successfully + @retval others Some errors happened. +**/ +EFI_STATUS +IsVolatileEnv ( + IN CONST CHAR16 *EnvVarName, + OUT BOOLEAN *Volatile + ) +{ + EFI_STATUS Status; + UINTN Size; + VOID *Buffer; + UINT32 Attribs; + + ASSERT (Volatile != NULL); + + Size = 0; + Buffer = NULL; + + // + // get the variable + // + Status = gRT->GetVariable ( + (CHAR16 *)EnvVarName, + &gShellVariableGuid, + &Attribs, + &Size, + Buffer + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + Buffer = AllocateZeroPool (Size); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gRT->GetVariable ( + (CHAR16 *)EnvVarName, + &gShellVariableGuid, + &Attribs, + &Size, + Buffer + ); + FreePool (Buffer); + } + + // + // not found means volatile + // + if (Status == EFI_NOT_FOUND) { + *Volatile = TRUE; + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // check for the Non Volatile bit + // + *Volatile = !(BOOLEAN)((Attribs & EFI_VARIABLE_NON_VOLATILE) == EFI_VARIABLE_NON_VOLATILE); + return EFI_SUCCESS; +} + +/** + free function for ENV_VAR_LIST objects. + + @param[in] List The pointer to pointer to list. +**/ +VOID +FreeEnvironmentVariableList ( + IN LIST_ENTRY *List + ) +{ + ENV_VAR_LIST *Node; + + ASSERT (List != NULL); + if (List == NULL) { + return; + } + + for ( Node = (ENV_VAR_LIST *)GetFirstNode (List) + ; !IsListEmpty (List) + ; Node = (ENV_VAR_LIST *)GetFirstNode (List) + ) + { + ASSERT (Node != NULL); + RemoveEntryList (&Node->Link); + if (Node->Key != NULL) { + FreePool (Node->Key); + } + + if (Node->Val != NULL) { + FreePool (Node->Val); + } + + FreePool (Node); + } +} + +/** + Creates a list of all Shell-Guid-based environment variables. + + @param[in, out] ListHead The pointer to pointer to LIST ENTRY object for + storing this list. + + @retval EFI_SUCCESS the list was created successfully. +**/ +EFI_STATUS +GetEnvironmentVariableList ( + IN OUT LIST_ENTRY *ListHead + ) +{ + CHAR16 *VariableName; + UINTN NameSize; + UINTN NameBufferSize; + EFI_STATUS Status; + EFI_GUID Guid; + UINTN ValSize; + UINTN ValBufferSize; + ENV_VAR_LIST *VarList; + + if (ListHead == NULL) { + return (EFI_INVALID_PARAMETER); + } + + Status = EFI_SUCCESS; + + ValBufferSize = INIT_DATA_BUFFER_SIZE; + NameBufferSize = INIT_NAME_BUFFER_SIZE; + VariableName = AllocateZeroPool (NameBufferSize); + if (VariableName == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + *VariableName = CHAR_NULL; + + while (!EFI_ERROR (Status)) { + NameSize = NameBufferSize; + Status = gRT->GetNextVariableName (&NameSize, VariableName, &Guid); + if (Status == EFI_NOT_FOUND) { + Status = EFI_SUCCESS; + break; + } else if (Status == EFI_BUFFER_TOO_SMALL) { + NameBufferSize = NameSize > NameBufferSize * 2 ? NameSize : NameBufferSize * 2; + SHELL_FREE_NON_NULL (VariableName); + VariableName = AllocateZeroPool (NameBufferSize); + if (VariableName == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + NameSize = NameBufferSize; + Status = gRT->GetNextVariableName (&NameSize, VariableName, &Guid); + } + + if (!EFI_ERROR (Status) && CompareGuid (&Guid, &gShellVariableGuid)) { + VarList = AllocateZeroPool (sizeof (ENV_VAR_LIST)); + if (VarList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } else { + ValSize = ValBufferSize; + // + // We need another CHAR16 to save '\0' in VarList->Val. + // + VarList->Val = AllocateZeroPool (ValSize + sizeof (CHAR16)); + if (VarList->Val == NULL) { + SHELL_FREE_NON_NULL (VarList); + Status = EFI_OUT_OF_RESOURCES; + break; + } + + Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES (VariableName, &VarList->Atts, &ValSize, VarList->Val); + if (Status == EFI_BUFFER_TOO_SMALL) { + ValBufferSize = ValSize > ValBufferSize * 2 ? ValSize : ValBufferSize * 2; + SHELL_FREE_NON_NULL (VarList->Val); + // + // We need another CHAR16 to save '\0' in VarList->Val. + // + VarList->Val = AllocateZeroPool (ValBufferSize + sizeof (CHAR16)); + if (VarList->Val == NULL) { + SHELL_FREE_NON_NULL (VarList); + Status = EFI_OUT_OF_RESOURCES; + break; + } + + ValSize = ValBufferSize; + Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES (VariableName, &VarList->Atts, &ValSize, VarList->Val); + } + + if (!EFI_ERROR (Status)) { + VarList->Key = AllocateCopyPool (StrSize (VariableName), VariableName); + if (VarList->Key == NULL) { + SHELL_FREE_NON_NULL (VarList->Val); + SHELL_FREE_NON_NULL (VarList); + Status = EFI_OUT_OF_RESOURCES; + } else { + InsertTailList (ListHead, &VarList->Link); + } + } else { + SHELL_FREE_NON_NULL (VarList->Val); + SHELL_FREE_NON_NULL (VarList); + } + } // if (VarList == NULL) ... else ... + } // compare guid + } // while + + SHELL_FREE_NON_NULL (VariableName); + + if (EFI_ERROR (Status)) { + FreeEnvironmentVariableList (ListHead); + } + + return (Status); +} + +/** + Sets a list of all Shell-Guid-based environment variables. this will + also eliminate all existing shell environment variables (even if they + are not on the list). + + This function will also deallocate the memory from List. + + @param[in] ListHead The pointer to LIST_ENTRY from + GetShellEnvVarList(). + + @retval EFI_SUCCESS the list was Set successfully. +**/ +EFI_STATUS +SetEnvironmentVariableList ( + IN LIST_ENTRY *ListHead + ) +{ + ENV_VAR_LIST VarList; + ENV_VAR_LIST *Node; + EFI_STATUS Status; + UINTN Size; + + InitializeListHead (&VarList.Link); + + // + // Delete all the current environment variables + // + Status = GetEnvironmentVariableList (&VarList.Link); + ASSERT_EFI_ERROR (Status); + + for ( Node = (ENV_VAR_LIST *)GetFirstNode (&VarList.Link) + ; !IsNull (&VarList.Link, &Node->Link) + ; Node = (ENV_VAR_LIST *)GetNextNode (&VarList.Link, &Node->Link) + ) + { + if (Node->Key != NULL) { + Status = SHELL_DELETE_ENVIRONMENT_VARIABLE (Node->Key); + } + + ASSERT_EFI_ERROR (Status); + } + + FreeEnvironmentVariableList (&VarList.Link); + + // + // set all the variables from the list + // + for ( Node = (ENV_VAR_LIST *)GetFirstNode (ListHead) + ; !IsNull (ListHead, &Node->Link) + ; Node = (ENV_VAR_LIST *)GetNextNode (ListHead, &Node->Link) + ) + { + Size = StrSize (Node->Val) - sizeof (CHAR16); + if (Node->Atts & EFI_VARIABLE_NON_VOLATILE) { + Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV (Node->Key, Size, Node->Val); + } else { + Status = SHELL_SET_ENVIRONMENT_VARIABLE_V (Node->Key, Size, Node->Val); + } + + ASSERT_EFI_ERROR (Status); + } + + FreeEnvironmentVariableList (ListHead); + + return (Status); +} + +/** + sets a list of all Shell-Guid-based environment variables. + + @param Environment Points to a NULL-terminated array of environment + variables with the format 'x=y', where x is the + environment variable name and y is the value. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Out of resources. + + @sa SetEnvironmentVariableList +**/ +EFI_STATUS +SetEnvironmentVariables ( + IN CONST CHAR16 **Environment + ) +{ + CONST CHAR16 *CurrentString; + UINTN CurrentCount; + ENV_VAR_LIST *VarList; + ENV_VAR_LIST *Node; + + VarList = NULL; + + if (Environment == NULL) { + return (EFI_INVALID_PARAMETER); + } + + // + // Build a list identical to the ones used for get/set list functions above + // + for ( CurrentCount = 0 + ; + ; CurrentCount++ + ) + { + CurrentString = Environment[CurrentCount]; + if (CurrentString == NULL) { + break; + } + + ASSERT (StrStr (CurrentString, L"=") != NULL); + Node = AllocateZeroPool (sizeof (ENV_VAR_LIST)); + if (Node == NULL) { + SetEnvironmentVariableList (&VarList->Link); + return (EFI_OUT_OF_RESOURCES); + } + + Node->Key = AllocateZeroPool ((StrStr (CurrentString, L"=") - CurrentString + 1) * sizeof (CHAR16)); + if (Node->Key == NULL) { + SHELL_FREE_NON_NULL (Node); + SetEnvironmentVariableList (&VarList->Link); + return (EFI_OUT_OF_RESOURCES); + } + + // + // Copy the string into the Key, leaving the last character allocated as NULL to terminate + // + StrnCpyS ( + Node->Key, + StrStr (CurrentString, L"=") - CurrentString + 1, + CurrentString, + StrStr (CurrentString, L"=") - CurrentString + ); + + // + // ValueSize = TotalSize - already removed size - size for '=' + size for terminator (the last 2 items cancel each other) + // + Node->Val = AllocateCopyPool (StrSize (CurrentString) - StrSize (Node->Key), CurrentString + StrLen (Node->Key) + 1); + if (Node->Val == NULL) { + SHELL_FREE_NON_NULL (Node->Key); + SHELL_FREE_NON_NULL (Node); + SetEnvironmentVariableList (&VarList->Link); + return (EFI_OUT_OF_RESOURCES); + } + + Node->Atts = EFI_VARIABLE_BOOTSERVICE_ACCESS; + + if (VarList == NULL) { + VarList = AllocateZeroPool (sizeof (ENV_VAR_LIST)); + if (VarList == NULL) { + SHELL_FREE_NON_NULL (Node->Key); + SHELL_FREE_NON_NULL (Node->Val); + SHELL_FREE_NON_NULL (Node); + return (EFI_OUT_OF_RESOURCES); + } + + InitializeListHead (&VarList->Link); + } + + InsertTailList (&VarList->Link, &Node->Link); + } // for loop + + // + // set this new list as the set of all environment variables. + // this function also frees the memory and deletes all pre-existing + // shell-guid based environment variables. + // + return (SetEnvironmentVariableList (&VarList->Link)); +} + +/** + Find an environment variable in the gShellEnvVarList. + + @param Key The name of the environment variable. + @param Value The value of the environment variable, the buffer + shoule be freed by the caller. + @param ValueSize The size in bytes of the environment variable + including the tailing CHAR_NELL. + @param Atts The attributes of the variable. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_NOT_FOUND The environment variable is not found in + gShellEnvVarList. + +**/ +EFI_STATUS +ShellFindEnvVarInList ( + IN CONST CHAR16 *Key, + OUT CHAR16 **Value, + OUT UINTN *ValueSize, + OUT UINT32 *Atts OPTIONAL + ) +{ + ENV_VAR_LIST *Node; + + if ((Key == NULL) || (Value == NULL) || (ValueSize == NULL)) { + return SHELL_INVALID_PARAMETER; + } + + for ( Node = (ENV_VAR_LIST *)GetFirstNode (&gShellEnvVarList.Link) + ; !IsNull (&gShellEnvVarList.Link, &Node->Link) + ; Node = (ENV_VAR_LIST *)GetNextNode (&gShellEnvVarList.Link, &Node->Link) + ) + { + if ((Node->Key != NULL) && (StrCmp (Key, Node->Key) == 0)) { + *Value = AllocateCopyPool (StrSize (Node->Val), Node->Val); + *ValueSize = StrSize (Node->Val); + if (Atts != NULL) { + *Atts = Node->Atts; + } + + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Add an environment variable into gShellEnvVarList. + + @param Key The name of the environment variable. + @param Value The value of environment variable. + @param ValueSize The size in bytes of the environment variable + including the tailing CHAR_NULL + @param Atts The attributes of the variable. + + @retval EFI_SUCCESS The environment variable was added to list successfully. + @retval others Some errors happened. + +**/ +EFI_STATUS +ShellAddEnvVarToList ( + IN CONST CHAR16 *Key, + IN CONST CHAR16 *Value, + IN UINTN ValueSize, + IN UINT32 Atts + ) +{ + ENV_VAR_LIST *Node; + CHAR16 *LocalKey; + CHAR16 *LocalValue; + + if ((Key == NULL) || (Value == NULL) || (ValueSize == 0)) { + return EFI_INVALID_PARAMETER; + } + + LocalValue = AllocateCopyPool (ValueSize, Value); + if (LocalValue == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Update the variable value if it exists in gShellEnvVarList. + // + for ( Node = (ENV_VAR_LIST *)GetFirstNode (&gShellEnvVarList.Link) + ; !IsNull (&gShellEnvVarList.Link, &Node->Link) + ; Node = (ENV_VAR_LIST *)GetNextNode (&gShellEnvVarList.Link, &Node->Link) + ) + { + if ((Node->Key != NULL) && (StrCmp (Key, Node->Key) == 0)) { + Node->Atts = Atts; + SHELL_FREE_NON_NULL (Node->Val); + Node->Val = LocalValue; + return EFI_SUCCESS; + } + } + + // + // If the environment variable key doesn't exist in list just insert + // a new node. + // + LocalKey = AllocateCopyPool (StrSize (Key), Key); + if (LocalKey == NULL) { + FreePool (LocalValue); + return EFI_OUT_OF_RESOURCES; + } + + Node = (ENV_VAR_LIST *)AllocateZeroPool (sizeof (ENV_VAR_LIST)); + if (Node == NULL) { + FreePool (LocalKey); + FreePool (LocalValue); + return EFI_OUT_OF_RESOURCES; + } + + Node->Key = LocalKey; + Node->Val = LocalValue; + Node->Atts = Atts; + InsertTailList (&gShellEnvVarList.Link, &Node->Link); + + return EFI_SUCCESS; +} + +/** + Remove a specified environment variable in gShellEnvVarList. + + @param Key The name of the environment variable. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_NOT_FOUND The environment variable is not found in + gShellEnvVarList. +**/ +EFI_STATUS +ShellRemvoeEnvVarFromList ( + IN CONST CHAR16 *Key + ) +{ + ENV_VAR_LIST *Node; + + if (Key == NULL) { + return EFI_INVALID_PARAMETER; + } + + for ( Node = (ENV_VAR_LIST *)GetFirstNode (&gShellEnvVarList.Link) + ; !IsNull (&gShellEnvVarList.Link, &Node->Link) + ; Node = (ENV_VAR_LIST *)GetNextNode (&gShellEnvVarList.Link, &Node->Link) + ) + { + if ((Node->Key != NULL) && (StrCmp (Key, Node->Key) == 0)) { + SHELL_FREE_NON_NULL (Node->Key); + SHELL_FREE_NON_NULL (Node->Val); + RemoveEntryList (&Node->Link); + SHELL_FREE_NON_NULL (Node); + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Initialize the gShellEnvVarList and cache all Shell-Guid-based environment + variables. + +**/ +EFI_STATUS +ShellInitEnvVarList ( + VOID + ) +{ + EFI_STATUS Status; + + InitializeListHead (&gShellEnvVarList.Link); + Status = GetEnvironmentVariableList (&gShellEnvVarList.Link); + + return Status; +} + +/** + Destructe the gShellEnvVarList. + +**/ +VOID +ShellFreeEnvVarList ( + VOID + ) +{ + FreeEnvironmentVariableList (&gShellEnvVarList.Link); + InitializeListHead (&gShellEnvVarList.Link); + + return; +} diff --git a/ShellPkg/Application/Shell/ShellEnvVar.h b/ShellPkg/Application/Shell/ShellEnvVar.h index 2eeecfce32..1e704b6bdd 100644 --- a/ShellPkg/Application/Shell/ShellEnvVar.h +++ b/ShellPkg/Application/Shell/ShellEnvVar.h @@ -1,280 +1,280 @@ -/** @file
- function definitions for shell environment functions.
-
- the following includes are required:
-//#include <Guid/ShellVariableGuid.h>
-//#include <Library/UefiRuntimeServicesTableLib.h>
-
-
- Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#ifndef _SHELL_ENVIRONMENT_VARIABLE_HEADER_
-#define _SHELL_ENVIRONMENT_VARIABLE_HEADER_
-
-typedef struct {
- LIST_ENTRY Link;
- CHAR16 *Key;
- CHAR16 *Val;
- UINT32 Atts;
-} ENV_VAR_LIST;
-
-//
-// The list is used to cache the environment variables.
-//
-extern ENV_VAR_LIST gShellEnvVarList;
-
-/**
- Reports whether an environment variable is Volatile or Non-Volatile.
-
- @param EnvVarName The name of the environment variable in question
- @param Volatile Return TRUE if the environment variable is volatile
-
- @retval EFI_SUCCESS The volatile attribute is returned successfully
- @retval others Some errors happened.
-**/
-EFI_STATUS
-IsVolatileEnv (
- IN CONST CHAR16 *EnvVarName,
- OUT BOOLEAN *Volatile
- );
-
-/**
- Delete a Non-Volatile environment variable.
-
- This will use the Runtime Services call SetVariable to remove a non-volatile variable.
-
- @param EnvVarName The name of the environment variable in question
-
- @retval EFI_SUCCESS The variable was deleted successfully
- @retval other An error occurred
- @sa SetVariable
-**/
-#define SHELL_DELETE_ENVIRONMENT_VARIABLE(EnvVarName) \
- (gRT->SetVariable((CHAR16*)EnvVarName, \
- &gShellVariableGuid, \
- 0, \
- 0, \
- NULL))
-
-/**
- Set a Non-Volatile environment variable.
-
- This will use the Runtime Services call SetVariable to set a non-volatile variable.
-
- @param EnvVarName The name of the environment variable in question
- @param BufferSize UINTN size of Buffer
- @param Buffer Pointer to value to set variable to
-
- @retval EFI_SUCCESS The variable was changed successfully
- @retval other An error occurred
- @sa SetVariable
-**/
-#define SHELL_SET_ENVIRONMENT_VARIABLE_NV(EnvVarName, BufferSize, Buffer) \
- (gRT->SetVariable((CHAR16*)EnvVarName, \
- &gShellVariableGuid, \
- EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS, \
- BufferSize, \
- (VOID*)Buffer))
-
-/**
- Get an environment variable.
-
- This will use the Runtime Services call GetVariable to get a variable.
-
- @param EnvVarName The name of the environment variable in question
- @param BufferSize Pointer to the UINTN size of Buffer
- @param Buffer Pointer buffer to get variable value into
-
- @retval EFI_SUCCESS The variable's value was retrieved successfully
- @retval other An error occurred
- @sa SetVariable
-**/
-#define SHELL_GET_ENVIRONMENT_VARIABLE(EnvVarName, BufferSize, Buffer) \
- (gRT->GetVariable((CHAR16*)EnvVarName, \
- &gShellVariableGuid, \
- 0, \
- BufferSize, \
- Buffer))
-
-/**
- Get an environment variable.
-
- This will use the Runtime Services call GetVariable to get a variable.
-
- @param EnvVarName The name of the environment variable in question
- @param Atts Pointer to the UINT32 for attributes (or NULL)
- @param BufferSize Pointer to the UINTN size of Buffer
- @param Buffer Pointer buffer to get variable value into
-
- @retval EFI_SUCCESS The variable's value was retrieved successfully
- @retval other An error occurred
- @sa SetVariable
-**/
-#define SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(EnvVarName, Atts, BufferSize, Buffer) \
- (gRT->GetVariable((CHAR16*)EnvVarName, \
- &gShellVariableGuid, \
- Atts, \
- BufferSize, \
- Buffer))
-
-/**
- Set a Volatile environment variable.
-
- This will use the Runtime Services call SetVariable to set a volatile variable.
-
- @param EnvVarName The name of the environment variable in question
- @param BufferSize UINTN size of Buffer
- @param Buffer Pointer to value to set variable to
-
- @retval EFI_SUCCESS The variable was changed successfully
- @retval other An error occurred
- @sa SetVariable
-**/
-#define SHELL_SET_ENVIRONMENT_VARIABLE_V(EnvVarName, BufferSize, Buffer) \
- (gRT->SetVariable((CHAR16*)EnvVarName, \
- &gShellVariableGuid, \
- EFI_VARIABLE_BOOTSERVICE_ACCESS, \
- BufferSize, \
- (VOID*)Buffer))
-
-/**
- Creates a list of all Shell-Guid-based environment variables.
-
- @param[in, out] List The pointer to pointer to LIST_ENTRY object for
- storing this list.
-
- @retval EFI_SUCCESS the list was created successfully.
-**/
-EFI_STATUS
-GetEnvironmentVariableList (
- IN OUT LIST_ENTRY *List
- );
-
-/**
- Sets a list of all Shell-Guid-based environment variables. this will
- also eliminate all pre-existing shell environment variables (even if they
- are not on the list).
-
- This function will also deallocate the memory from List.
-
- @param[in] List The pointer to LIST_ENTRY from
- GetShellEnvVarList().
-
- @retval EFI_SUCCESS The list was Set successfully.
-**/
-EFI_STATUS
-SetEnvironmentVariableList (
- IN LIST_ENTRY *List
- );
-
-/**
- sets all Shell-Guid-based environment variables. this will
- also eliminate all pre-existing shell environment variables (even if they
- are not on the list).
-
- @param[in] Environment Points to a NULL-terminated array of environment
- variables with the format 'x=y', where x is the
- environment variable name and y is the value.
-
- @retval EFI_SUCCESS The command executed successfully.
- @retval EFI_INVALID_PARAMETER The parameter is invalid.
- @retval EFI_OUT_OF_RESOURCES Out of resources.
-
- @sa SetEnvironmentVariableList
-**/
-EFI_STATUS
-SetEnvironmentVariables (
- IN CONST CHAR16 **Environment
- );
-
-/**
- free function for ENV_VAR_LIST objects.
-
- @param[in] List The pointer to pointer to list.
-**/
-VOID
-FreeEnvironmentVariableList (
- IN LIST_ENTRY *List
- );
-
-/**
- Find an environment variable in the gShellEnvVarList.
-
- @param Key The name of the environment variable.
- @param Value The value of the environment variable, the buffer
- shoule be freed by the caller.
- @param ValueSize The size in bytes of the environment variable
- including the tailing CHAR_NULL.
- @param Atts The attributes of the variable.
-
- @retval EFI_SUCCESS The command executed successfully.
- @retval EFI_NOT_FOUND The environment variable is not found in
- gShellEnvVarList.
-
-**/
-EFI_STATUS
-ShellFindEnvVarInList (
- IN CONST CHAR16 *Key,
- OUT CHAR16 **Value,
- OUT UINTN *ValueSize,
- OUT UINT32 *Atts OPTIONAL
- );
-
-/**
- Add an environment variable into gShellEnvVarList.
-
- @param Key The name of the environment variable.
- @param Value The value of environment variable.
- @param ValueSize The size in bytes of the environment variable
- including the tailing CHAR_NULL
- @param Atts The attributes of the variable.
-
- @retval EFI_SUCCESS The environment variable was added to list successfully.
- @retval others Some errors happened.
-
-**/
-EFI_STATUS
-ShellAddEnvVarToList (
- IN CONST CHAR16 *Key,
- IN CONST CHAR16 *Value,
- IN UINTN ValueSize,
- IN UINT32 Atts
- );
-
-/**
- Remove a specified environment variable in gShellEnvVarList.
-
- @param Key The name of the environment variable.
-
- @retval EFI_SUCCESS The command executed successfully.
- @retval EFI_NOT_FOUND The environment variable is not found in
- gShellEnvVarList.
-**/
-EFI_STATUS
-ShellRemvoeEnvVarFromList (
- IN CONST CHAR16 *Key
- );
-
-/**
- Initialize the gShellEnvVarList and cache all Shell-Guid-based environment
- variables.
-
-**/
-EFI_STATUS
-ShellInitEnvVarList (
- VOID
- );
-
-/**
- Destructe the gShellEnvVarList.
-
-**/
-VOID
-ShellFreeEnvVarList (
- VOID
- );
-
-#endif //_SHELL_ENVIRONMENT_VARIABLE_HEADER_
+/** @file + function definitions for shell environment functions. + + the following includes are required: +//#include <Guid/ShellVariableGuid.h> +//#include <Library/UefiRuntimeServicesTableLib.h> + + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SHELL_ENVIRONMENT_VARIABLE_HEADER_ +#define _SHELL_ENVIRONMENT_VARIABLE_HEADER_ + +typedef struct { + LIST_ENTRY Link; + CHAR16 *Key; + CHAR16 *Val; + UINT32 Atts; +} ENV_VAR_LIST; + +// +// The list is used to cache the environment variables. +// +extern ENV_VAR_LIST gShellEnvVarList; + +/** + Reports whether an environment variable is Volatile or Non-Volatile. + + @param EnvVarName The name of the environment variable in question + @param Volatile Return TRUE if the environment variable is volatile + + @retval EFI_SUCCESS The volatile attribute is returned successfully + @retval others Some errors happened. +**/ +EFI_STATUS +IsVolatileEnv ( + IN CONST CHAR16 *EnvVarName, + OUT BOOLEAN *Volatile + ); + +/** + Delete a Non-Volatile environment variable. + + This will use the Runtime Services call SetVariable to remove a non-volatile variable. + + @param EnvVarName The name of the environment variable in question + + @retval EFI_SUCCESS The variable was deleted successfully + @retval other An error occurred + @sa SetVariable +**/ +#define SHELL_DELETE_ENVIRONMENT_VARIABLE(EnvVarName) \ + (gRT->SetVariable((CHAR16*)EnvVarName, \ + &gShellVariableGuid, \ + 0, \ + 0, \ + NULL)) + +/** + Set a Non-Volatile environment variable. + + This will use the Runtime Services call SetVariable to set a non-volatile variable. + + @param EnvVarName The name of the environment variable in question + @param BufferSize UINTN size of Buffer + @param Buffer Pointer to value to set variable to + + @retval EFI_SUCCESS The variable was changed successfully + @retval other An error occurred + @sa SetVariable +**/ +#define SHELL_SET_ENVIRONMENT_VARIABLE_NV(EnvVarName, BufferSize, Buffer) \ + (gRT->SetVariable((CHAR16*)EnvVarName, \ + &gShellVariableGuid, \ + EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS, \ + BufferSize, \ + (VOID*)Buffer)) + +/** + Get an environment variable. + + This will use the Runtime Services call GetVariable to get a variable. + + @param EnvVarName The name of the environment variable in question + @param BufferSize Pointer to the UINTN size of Buffer + @param Buffer Pointer buffer to get variable value into + + @retval EFI_SUCCESS The variable's value was retrieved successfully + @retval other An error occurred + @sa SetVariable +**/ +#define SHELL_GET_ENVIRONMENT_VARIABLE(EnvVarName, BufferSize, Buffer) \ + (gRT->GetVariable((CHAR16*)EnvVarName, \ + &gShellVariableGuid, \ + 0, \ + BufferSize, \ + Buffer)) + +/** + Get an environment variable. + + This will use the Runtime Services call GetVariable to get a variable. + + @param EnvVarName The name of the environment variable in question + @param Atts Pointer to the UINT32 for attributes (or NULL) + @param BufferSize Pointer to the UINTN size of Buffer + @param Buffer Pointer buffer to get variable value into + + @retval EFI_SUCCESS The variable's value was retrieved successfully + @retval other An error occurred + @sa SetVariable +**/ +#define SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(EnvVarName, Atts, BufferSize, Buffer) \ + (gRT->GetVariable((CHAR16*)EnvVarName, \ + &gShellVariableGuid, \ + Atts, \ + BufferSize, \ + Buffer)) + +/** + Set a Volatile environment variable. + + This will use the Runtime Services call SetVariable to set a volatile variable. + + @param EnvVarName The name of the environment variable in question + @param BufferSize UINTN size of Buffer + @param Buffer Pointer to value to set variable to + + @retval EFI_SUCCESS The variable was changed successfully + @retval other An error occurred + @sa SetVariable +**/ +#define SHELL_SET_ENVIRONMENT_VARIABLE_V(EnvVarName, BufferSize, Buffer) \ + (gRT->SetVariable((CHAR16*)EnvVarName, \ + &gShellVariableGuid, \ + EFI_VARIABLE_BOOTSERVICE_ACCESS, \ + BufferSize, \ + (VOID*)Buffer)) + +/** + Creates a list of all Shell-Guid-based environment variables. + + @param[in, out] List The pointer to pointer to LIST_ENTRY object for + storing this list. + + @retval EFI_SUCCESS the list was created successfully. +**/ +EFI_STATUS +GetEnvironmentVariableList ( + IN OUT LIST_ENTRY *List + ); + +/** + Sets a list of all Shell-Guid-based environment variables. this will + also eliminate all pre-existing shell environment variables (even if they + are not on the list). + + This function will also deallocate the memory from List. + + @param[in] List The pointer to LIST_ENTRY from + GetShellEnvVarList(). + + @retval EFI_SUCCESS The list was Set successfully. +**/ +EFI_STATUS +SetEnvironmentVariableList ( + IN LIST_ENTRY *List + ); + +/** + sets all Shell-Guid-based environment variables. this will + also eliminate all pre-existing shell environment variables (even if they + are not on the list). + + @param[in] Environment Points to a NULL-terminated array of environment + variables with the format 'x=y', where x is the + environment variable name and y is the value. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Out of resources. + + @sa SetEnvironmentVariableList +**/ +EFI_STATUS +SetEnvironmentVariables ( + IN CONST CHAR16 **Environment + ); + +/** + free function for ENV_VAR_LIST objects. + + @param[in] List The pointer to pointer to list. +**/ +VOID +FreeEnvironmentVariableList ( + IN LIST_ENTRY *List + ); + +/** + Find an environment variable in the gShellEnvVarList. + + @param Key The name of the environment variable. + @param Value The value of the environment variable, the buffer + shoule be freed by the caller. + @param ValueSize The size in bytes of the environment variable + including the tailing CHAR_NULL. + @param Atts The attributes of the variable. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_NOT_FOUND The environment variable is not found in + gShellEnvVarList. + +**/ +EFI_STATUS +ShellFindEnvVarInList ( + IN CONST CHAR16 *Key, + OUT CHAR16 **Value, + OUT UINTN *ValueSize, + OUT UINT32 *Atts OPTIONAL + ); + +/** + Add an environment variable into gShellEnvVarList. + + @param Key The name of the environment variable. + @param Value The value of environment variable. + @param ValueSize The size in bytes of the environment variable + including the tailing CHAR_NULL + @param Atts The attributes of the variable. + + @retval EFI_SUCCESS The environment variable was added to list successfully. + @retval others Some errors happened. + +**/ +EFI_STATUS +ShellAddEnvVarToList ( + IN CONST CHAR16 *Key, + IN CONST CHAR16 *Value, + IN UINTN ValueSize, + IN UINT32 Atts + ); + +/** + Remove a specified environment variable in gShellEnvVarList. + + @param Key The name of the environment variable. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_NOT_FOUND The environment variable is not found in + gShellEnvVarList. +**/ +EFI_STATUS +ShellRemvoeEnvVarFromList ( + IN CONST CHAR16 *Key + ); + +/** + Initialize the gShellEnvVarList and cache all Shell-Guid-based environment + variables. + +**/ +EFI_STATUS +ShellInitEnvVarList ( + VOID + ); + +/** + Destructe the gShellEnvVarList. + +**/ +VOID +ShellFreeEnvVarList ( + VOID + ); + +#endif //_SHELL_ENVIRONMENT_VARIABLE_HEADER_ diff --git a/ShellPkg/Application/Shell/ShellManParser.c b/ShellPkg/Application/Shell/ShellManParser.c index 5c823cd7f5..a500bc5ca1 100644 --- a/ShellPkg/Application/Shell/ShellManParser.c +++ b/ShellPkg/Application/Shell/ShellManParser.c @@ -1,784 +1,784 @@ -/** @file
- Provides interface to shell MAN file parser.
-
- Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
- Copyright 2015 Dell Inc.
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "Shell.h"
-
-#define SHELL_MAN_HII_GUID \
-{ \
- 0xf62ccd0c, 0x2449, 0x453c, { 0x8a, 0xcb, 0x8c, 0xc5, 0x7c, 0xf0, 0x2a, 0x97 } \
-}
-
-EFI_HII_HANDLE mShellManHiiHandle = NULL;
-EFI_HANDLE mShellManDriverHandle = NULL;
-
-SHELL_MAN_HII_VENDOR_DEVICE_PATH mShellManHiiDevicePath = {
- {
- {
- HARDWARE_DEVICE_PATH,
- HW_VENDOR_DP,
- {
- (UINT8)(sizeof (VENDOR_DEVICE_PATH)),
- (UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8)
- }
- },
- SHELL_MAN_HII_GUID
- },
- {
- END_DEVICE_PATH_TYPE,
- END_ENTIRE_DEVICE_PATH_SUBTYPE,
- {
- (UINT8)(END_DEVICE_PATH_LENGTH),
- (UINT8)((END_DEVICE_PATH_LENGTH) >> 8)
- }
- }
-};
-
-/**
- Verifies that the filename has .EFI on the end.
-
- allocates a new buffer and copies the name (appending .EFI if necessary).
- Caller to free the buffer.
-
- @param[in] NameString original name string
-
- @return the new filename with .efi as the extension.
-**/
-CHAR16 *
-GetExecuatableFileName (
- IN CONST CHAR16 *NameString
- )
-{
- CHAR16 *Buffer;
- CHAR16 *SuffixStr;
-
- if (NameString == NULL) {
- return (NULL);
- }
-
- //
- // Fix the file name
- //
- if (StrnCmp (NameString+StrLen (NameString)-StrLen (L".efi"), L".efi", StrLen (L".efi")) == 0) {
- Buffer = AllocateCopyPool (StrSize (NameString), NameString);
- } else if (StrnCmp (NameString+StrLen (NameString)-StrLen (L".man"), L".man", StrLen (L".man")) == 0) {
- Buffer = AllocateCopyPool (StrSize (NameString), NameString);
- if (Buffer != NULL) {
- SuffixStr = Buffer+StrLen (Buffer)-StrLen (L".man");
- StrnCpyS (SuffixStr, StrSize (L".man")/sizeof (CHAR16), L".efi", StrLen (L".efi"));
- }
- } else {
- Buffer = AllocateZeroPool (StrSize (NameString) + StrLen (L".efi")*sizeof (CHAR16));
- if (Buffer != NULL) {
- StrnCpyS (
- Buffer,
- (StrSize (NameString) + StrLen (L".efi")*sizeof (CHAR16))/sizeof (CHAR16),
- NameString,
- StrLen (NameString)
- );
- StrnCatS (
- Buffer,
- (StrSize (NameString) + StrLen (L".efi")*sizeof (CHAR16))/sizeof (CHAR16),
- L".efi",
- StrLen (L".efi")
- );
- }
- }
-
- return (Buffer);
-}
-
-/**
- Verifies that the filename has .MAN on the end.
-
- allocates a new buffer and copies the name (appending .MAN if necessary)
-
- ASSERT if ManFileName is NULL
-
- @param[in] ManFileName original filename
-
- @return the new filename with .man as the extension.
-**/
-CHAR16 *
-GetManFileName (
- IN CONST CHAR16 *ManFileName
- )
-{
- CHAR16 *Buffer;
-
- if (ManFileName == NULL) {
- return (NULL);
- }
-
- //
- // Fix the file name
- //
- if (StrnCmp (ManFileName+StrLen (ManFileName)-4, L".man", 4) == 0) {
- Buffer = AllocateCopyPool (StrSize (ManFileName), ManFileName);
- } else {
- Buffer = AllocateZeroPool (StrSize (ManFileName) + 4*sizeof (CHAR16));
- if (Buffer != NULL) {
- StrnCpyS (
- Buffer,
- (StrSize (ManFileName) + 4*sizeof (CHAR16))/sizeof (CHAR16),
- ManFileName,
- StrLen (ManFileName)
- );
- StrnCatS (
- Buffer,
- (StrSize (ManFileName) + 4*sizeof (CHAR16))/sizeof (CHAR16),
- L".man",
- 4
- );
- }
- }
-
- return (Buffer);
-}
-
-/**
- Search the path environment variable for possible locations and test for
- which one contains a man file with the name specified. If a valid file is found
- stop searching and return the (opened) SHELL_FILE_HANDLE for that file.
-
- @param[in] FileName Name of the file to find and open.
- @param[out] Handle Pointer to the handle of the found file. The
- value of this is undefined for return values
- except EFI_SUCCESS.
-
- @retval EFI_SUCCESS The file was found. Handle is a valid SHELL_FILE_HANDLE
- @retval EFI_INVALID_PARAMETER A parameter had an invalid value.
- @retval EFI_NOT_FOUND The file was not found.
-**/
-EFI_STATUS
-SearchPathForFile (
- IN CONST CHAR16 *FileName,
- OUT SHELL_FILE_HANDLE *Handle
- )
-{
- CHAR16 *FullFileName;
- EFI_STATUS Status;
-
- if ( (FileName == NULL)
- || (Handle == NULL)
- || (StrLen (FileName) == 0)
- )
- {
- return (EFI_INVALID_PARAMETER);
- }
-
- FullFileName = ShellFindFilePath (FileName);
- if (FullFileName == NULL) {
- return (EFI_NOT_FOUND);
- }
-
- //
- // now open that file
- //
- Status = EfiShellOpenFileByName (FullFileName, Handle, EFI_FILE_MODE_READ);
- FreePool (FullFileName);
-
- return (Status);
-}
-
-/**
- Parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
- detailed help for any sub section specified in the comma separated list of
- sections provided. If the end of the file or a .TH section is found then
- return.
-
- Upon a successful return the caller is responsible to free the memory in *HelpText
-
- @param[in] Handle FileHandle to read from
- @param[in] Sections name of command's sub sections to find
- @param[out] HelpText pointer to pointer to string where text goes.
- @param[out] HelpSize pointer to size of allocated HelpText (may be updated)
- @param[in] Ascii TRUE if the file is ASCII, FALSE otherwise.
-
- @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
- @retval EFI_SUCCESS the section was found and its description stored in
- an allocated buffer.
-**/
-EFI_STATUS
-ManFileFindSections (
- IN SHELL_FILE_HANDLE Handle,
- IN CONST CHAR16 *Sections,
- OUT CHAR16 **HelpText,
- OUT UINTN *HelpSize,
- IN BOOLEAN Ascii
- )
-{
- EFI_STATUS Status;
- CHAR16 *ReadLine;
- UINTN Size;
- BOOLEAN CurrentlyReading;
- CHAR16 *SectionName;
- UINTN SectionLen;
- BOOLEAN Found;
-
- if ( (Handle == NULL)
- || (HelpText == NULL)
- || (HelpSize == NULL)
- )
- {
- return (EFI_INVALID_PARAMETER);
- }
-
- Status = EFI_SUCCESS;
- CurrentlyReading = FALSE;
- Size = 1024;
- Found = FALSE;
-
- ReadLine = AllocateZeroPool (Size);
- if (ReadLine == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- for ( ; !ShellFileHandleEof (Handle); Size = 1024) {
- Status = ShellFileHandleReadLine (Handle, ReadLine, &Size, TRUE, &Ascii);
- if (ReadLine[0] == L'#') {
- //
- // Skip comment lines
- //
- continue;
- }
-
- //
- // ignore too small of buffer...
- //
- if (Status == EFI_BUFFER_TOO_SMALL) {
- Status = EFI_SUCCESS;
- }
-
- if (EFI_ERROR (Status)) {
- break;
- } else if (StrnCmp (ReadLine, L".TH", 3) == 0) {
- //
- // we hit the end of this commands section so stop.
- //
- break;
- } else if (StrnCmp (ReadLine, L".SH", 3) == 0) {
- if (Sections == NULL) {
- CurrentlyReading = TRUE;
- continue;
- }
-
- //
- // we found a section
- //
- if (CurrentlyReading) {
- CurrentlyReading = FALSE;
- }
-
- //
- // is this a section we want to read in?
- //
- for ( SectionName = ReadLine + 3
- ; *SectionName == L' '
- ; SectionName++)
- {
- }
-
- SectionLen = StrLen (SectionName);
- SectionName = StrStr (Sections, SectionName);
- if (SectionName == NULL) {
- continue;
- }
-
- if ((*(SectionName + SectionLen) == CHAR_NULL) || (*(SectionName + SectionLen) == L',')) {
- CurrentlyReading = TRUE;
- }
- } else if (CurrentlyReading) {
- Found = TRUE;
- //
- // copy and save the current line.
- //
- ASSERT ((*HelpText == NULL && *HelpSize == 0) || (*HelpText != NULL));
- StrnCatGrow (HelpText, HelpSize, ReadLine, 0);
- StrnCatGrow (HelpText, HelpSize, L"\r\n", 0);
- }
- }
-
- FreePool (ReadLine);
- if (!Found && !EFI_ERROR (Status)) {
- return (EFI_NOT_FOUND);
- }
-
- return (Status);
-}
-
-/**
- Parses a line from a MAN file to see if it is the Title Header. If it is, then
- if the "Brief Description" is desired, allocate a buffer for it and return a
- copy. Upon a successful return the caller is responsible to free the memory in
- *BriefDesc
-
- Uses a simple state machine that allows "unlimited" whitespace before and after the
- ".TH", compares Command and the MAN file command name without respect to case, and
- allows "unlimited" whitespace and '0' and '1' characters before the Short Description.
- The PCRE regex describing this functionality is: ^\s*\.TH\s+(\S)\s[\s01]*(.*)$
- where group 1 is the Command Name and group 2 is the Short Description.
-
- @param[in] Command name of command whose MAN file we think Line came from
- @param[in] Line Pointer to a line from the MAN file
- @param[out] BriefDesc pointer to pointer to string where description goes.
- @param[out] BriefSize pointer to size of allocated BriefDesc
- @param[out] Found TRUE if the Title Header was found and it belongs to Command
-
- @retval TRUE Line contained the Title Header
- @retval FALSE Line did not contain the Title Header
-**/
-BOOLEAN
-IsTitleHeader (
- IN CONST CHAR16 *Command,
- IN CHAR16 *Line,
- OUT CHAR16 **BriefDesc OPTIONAL,
- OUT UINTN *BriefSize OPTIONAL,
- OUT BOOLEAN *Found
- )
-{
- // The states of a simple state machine used to recognize a title header line
- // and to extract the Short Description, if desired.
- typedef enum {
- LookForThMacro, LookForCommandName, CompareCommands, GetBriefDescription, Final
- } STATEVALUES;
-
- STATEVALUES State;
- UINTN CommandIndex; // Indexes Command as we compare its chars to the MAN file.
- BOOLEAN ReturnValue; // TRUE if this the Title Header line of *some* MAN file.
- BOOLEAN ReturnFound; // TRUE if this the Title Header line of *the desired* MAN file.
-
- ReturnValue = FALSE;
- ReturnFound = FALSE;
- CommandIndex = 0;
- State = LookForThMacro;
-
- do {
- if (*Line == L'\0') {
- break;
- }
-
- switch (State) {
- // Handle "^\s*.TH\s"
- // Go to state LookForCommandName if the title header macro is present; otherwise,
- // eat white space. If we see something other than white space, this is not a
- // title header line.
- case LookForThMacro:
- if ((StrnCmp (L".TH ", Line, 4) == 0) || (StrnCmp (L".TH\t", Line, 4) == 0)) {
- Line += 4;
- State = LookForCommandName;
- } else if ((*Line == L' ') || (*Line == L'\t')) {
- Line++;
- } else {
- State = Final;
- }
-
- break;
-
- // Handle "\s*"
- // Eat any "extra" whitespace after the title header macro (we have already seen
- // at least one white space character). Go to state CompareCommands when a
- // non-white space is seen.
- case LookForCommandName:
- if ((*Line == L' ') || (*Line == L'\t')) {
- Line++;
- } else {
- ReturnValue = TRUE; // This is *some* command's title header line.
- State = CompareCommands;
- // Do not increment Line; it points to the first character of the command
- // name on the title header line.
- }
-
- break;
-
- // Handle "(\S)\s"
- // Compare Command to the title header command name, ignoring case. When we
- // reach the end of the command (i.e. we see white space), the next state
- // depends on whether the caller wants a copy of the Brief Description.
- case CompareCommands:
- if ((*Line == L' ') || (*Line == L'\t')) {
- ReturnFound = TRUE; // This is the desired command's title header line.
- State = (BriefDesc == NULL) ? Final : GetBriefDescription;
- } else if (CharToUpper (*Line) != CharToUpper (*(Command + CommandIndex++))) {
- State = Final;
- }
-
- Line++;
- break;
-
- // Handle "[\s01]*(.*)$"
- // Skip whitespace, '0', and '1' characters, if any, prior to the brief description.
- // Return the description to the caller.
- case GetBriefDescription:
- if ((*Line != L' ') && (*Line != L'\t') && (*Line != L'0') && (*Line != L'1')) {
- *BriefSize = StrSize (Line);
- *BriefDesc = AllocateZeroPool (*BriefSize);
- if (*BriefDesc != NULL) {
- StrCpyS (*BriefDesc, (*BriefSize)/sizeof (CHAR16), Line);
- }
-
- State = Final;
- }
-
- Line++;
- break;
-
- default:
- break;
- }
- } while (State < Final);
-
- *Found = ReturnFound;
- return ReturnValue;
-}
-
-/**
- Parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
- "Brief Description" for the .TH section as specified by Command. If the
- command section is not found return EFI_NOT_FOUND.
-
- Upon a successful return the caller is responsible to free the memory in *BriefDesc
-
- @param[in] Handle FileHandle to read from
- @param[in] Command name of command's section to find as entered on the
- command line (may be a relative or absolute path or
- be in any case: upper, lower, or mixed in numerous ways!).
- @param[out] BriefDesc pointer to pointer to string where description goes.
- @param[out] BriefSize pointer to size of allocated BriefDesc
- @param[in, out] Ascii TRUE if the file is ASCII, FALSE otherwise, will be
- set if the file handle is at the 0 position.
-
- @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
- @retval EFI_SUCCESS the section was found and its description stored in
- an allocated buffer if requested.
-**/
-EFI_STATUS
-ManFileFindTitleSection (
- IN SHELL_FILE_HANDLE Handle,
- IN CONST CHAR16 *Command,
- OUT CHAR16 **BriefDesc OPTIONAL,
- OUT UINTN *BriefSize OPTIONAL,
- IN OUT BOOLEAN *Ascii
- )
-{
- EFI_STATUS Status;
- CHAR16 *ReadLine;
- UINTN Size;
- BOOLEAN Found;
- UINTN Start;
-
- if ( (Handle == NULL)
- || (Command == NULL)
- || ((BriefDesc != NULL) && (BriefSize == NULL))
- )
- {
- return (EFI_INVALID_PARAMETER);
- }
-
- Status = EFI_SUCCESS;
- Size = 1024;
- Found = FALSE;
-
- ReadLine = AllocateZeroPool (Size);
- if (ReadLine == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- //
- // Do not pass any leading path information that may be present to IsTitleHeader().
- //
- Start = StrLen (Command);
- while ( (Start != 0)
- && (*(Command + Start - 1) != L'\\')
- && (*(Command + Start - 1) != L'/')
- && (*(Command + Start - 1) != L':'))
- {
- --Start;
- }
-
- for ( ; !ShellFileHandleEof (Handle); Size = 1024) {
- Status = ShellFileHandleReadLine (Handle, ReadLine, &Size, TRUE, Ascii);
- //
- // ignore too small of buffer...
- //
- if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) {
- break;
- }
-
- Status = EFI_NOT_FOUND;
- if (IsTitleHeader (Command+Start, ReadLine, BriefDesc, BriefSize, &Found)) {
- Status = Found ? EFI_SUCCESS : EFI_NOT_FOUND;
- break;
- }
- }
-
- FreePool (ReadLine);
- return (Status);
-}
-
-/**
- This function returns the help information for the specified command. The help text
- will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B)
-
- If Sections is specified, then each section name listed will be compared in a casesensitive
- manner, to the section names described in Appendix B. If the section exists,
- it will be appended to the returned help text. If the section does not exist, no
- information will be returned. If Sections is NULL, then all help text information
- available will be returned.
-
- if BriefDesc is NULL, then the breif description will not be saved separately,
- but placed first in the main HelpText.
-
- @param[in] ManFileName Points to the NULL-terminated UEFI Shell MAN file name.
- @param[in] Command Points to the NULL-terminated UEFI Shell command name.
- @param[in] Sections Points to the NULL-terminated comma-delimited
- section names to return. If NULL, then all
- sections will be returned.
- @param[out] BriefDesc On return, points to a callee-allocated buffer
- containing brief description text.
- @param[out] HelpText On return, points to a callee-allocated buffer
- containing all specified help text.
-
- @retval EFI_SUCCESS The help text was returned.
- @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
- returned help text.
- @retval EFI_INVALID_PARAMETER HelpText is NULL.
- @retval EFI_INVALID_PARAMETER ManFileName is invalid.
- @retval EFI_NOT_FOUND There is no help text available for Command.
-**/
-EFI_STATUS
-ProcessManFile (
- IN CONST CHAR16 *ManFileName,
- IN CONST CHAR16 *Command,
- IN CONST CHAR16 *Sections OPTIONAL,
- OUT CHAR16 **BriefDesc OPTIONAL,
- OUT CHAR16 **HelpText
- )
-{
- CHAR16 *TempString;
- SHELL_FILE_HANDLE FileHandle;
- EFI_HANDLE CmdFileImgHandle;
- EFI_STATUS Status;
- UINTN HelpSize;
- UINTN BriefSize;
- UINTN StringIdWalker;
- BOOLEAN Ascii;
- CHAR16 *CmdFileName;
- CHAR16 *CmdFilePathName;
- EFI_DEVICE_PATH_PROTOCOL *FileDevPath;
- EFI_DEVICE_PATH_PROTOCOL *DevPath;
- EFI_HII_PACKAGE_LIST_HEADER *PackageListHeader;
-
- if ( (ManFileName == NULL)
- || (Command == NULL)
- || (HelpText == NULL)
- )
- {
- return (EFI_INVALID_PARAMETER);
- }
-
- HelpSize = 0;
- BriefSize = 0;
- StringIdWalker = 0;
- TempString = NULL;
- Ascii = FALSE;
- CmdFileName = NULL;
- CmdFilePathName = NULL;
- CmdFileImgHandle = NULL;
- PackageListHeader = NULL;
- FileDevPath = NULL;
- DevPath = NULL;
-
- //
- // See if it's in HII first
- //
- TempString = ShellCommandGetCommandHelp (Command);
- if (TempString != NULL) {
- FileHandle = ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (TRUE), NULL);
- HelpSize = StrLen (TempString) * sizeof (CHAR16);
- ShellWriteFile (FileHandle, &HelpSize, TempString);
- ShellSetFilePosition (FileHandle, 0);
- HelpSize = 0;
- BriefSize = 0;
- Status = ManFileFindTitleSection (FileHandle, Command, BriefDesc, &BriefSize, &Ascii);
- if (!EFI_ERROR (Status) && (HelpText != NULL)) {
- Status = ManFileFindSections (FileHandle, Sections, HelpText, &HelpSize, Ascii);
- }
-
- ShellCloseFile (&FileHandle);
- } else {
- //
- // If the image is a external app, check .MAN file first.
- //
- FileHandle = NULL;
- TempString = GetManFileName (ManFileName);
- if (TempString == NULL) {
- return (EFI_INVALID_PARAMETER);
- }
-
- Status = SearchPathForFile (TempString, &FileHandle);
- if (EFI_ERROR (Status)) {
- FileDevPath = FileDevicePath (NULL, TempString);
- DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, FileDevPath);
- Status = InternalOpenFileDevicePath (DevPath, &FileHandle, EFI_FILE_MODE_READ, 0);
- SHELL_FREE_NON_NULL (FileDevPath);
- SHELL_FREE_NON_NULL (DevPath);
- }
-
- if (!EFI_ERROR (Status)) {
- HelpSize = 0;
- BriefSize = 0;
- Status = ManFileFindTitleSection (FileHandle, Command, BriefDesc, &BriefSize, &Ascii);
- if (!EFI_ERROR (Status) && (HelpText != NULL)) {
- Status = ManFileFindSections (FileHandle, Sections, HelpText, &HelpSize, Ascii);
- }
-
- ShellInfoObject.NewEfiShellProtocol->CloseFile (FileHandle);
- if (!EFI_ERROR (Status)) {
- //
- // Get help text from .MAN file success.
- //
- goto Done;
- }
- }
-
- //
- // Load the app image to check EFI_HII_PACKAGE_LIST_PROTOCOL.
- //
- CmdFileName = GetExecuatableFileName (TempString);
- if (CmdFileName == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto Done;
- }
-
- //
- // If the file in CWD then use the file name, else use the full
- // path name.
- //
- CmdFilePathName = ShellFindFilePath (CmdFileName);
- if (CmdFilePathName == NULL) {
- Status = EFI_NOT_FOUND;
- goto Done;
- }
-
- DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath (CmdFilePathName);
- Status = gBS->LoadImage (FALSE, gImageHandle, DevPath, NULL, 0, &CmdFileImgHandle);
- if (EFI_ERROR (Status)) {
- //
- // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created
- // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.
- // If the caller doesn't have the option to defer the execution of an image, we should
- // unload image for the EFI_SECURITY_VIOLATION to avoid the resource leak.
- //
- if (Status == EFI_SECURITY_VIOLATION) {
- gBS->UnloadImage (CmdFileImgHandle);
- }
-
- *HelpText = NULL;
- goto Done;
- }
-
- Status = gBS->OpenProtocol (
- CmdFileImgHandle,
- &gEfiHiiPackageListProtocolGuid,
- (VOID **)&PackageListHeader,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- if (EFI_ERROR (Status)) {
- *HelpText = NULL;
- goto Done;
- }
-
- //
- // If get package list on image handle, install it on HiiDatabase.
- //
- Status = gBS->InstallProtocolInterface (
- &mShellManDriverHandle,
- &gEfiDevicePathProtocolGuid,
- EFI_NATIVE_INTERFACE,
- &mShellManHiiDevicePath
- );
- if (EFI_ERROR (Status)) {
- goto Done;
- }
-
- Status = gHiiDatabase->NewPackageList (
- gHiiDatabase,
- PackageListHeader,
- mShellManDriverHandle,
- &mShellManHiiHandle
- );
- if (EFI_ERROR (Status)) {
- goto Done;
- }
-
- StringIdWalker = 1;
- do {
- SHELL_FREE_NON_NULL (TempString);
- if (BriefDesc != NULL) {
- SHELL_FREE_NON_NULL (*BriefDesc);
- }
-
- TempString = HiiGetString (mShellManHiiHandle, (EFI_STRING_ID)StringIdWalker, NULL);
- if (TempString == NULL) {
- Status = EFI_NOT_FOUND;
- goto Done;
- }
-
- FileHandle = ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (TRUE), NULL);
- HelpSize = StrLen (TempString) * sizeof (CHAR16);
- ShellWriteFile (FileHandle, &HelpSize, TempString);
- ShellSetFilePosition (FileHandle, 0);
- HelpSize = 0;
- BriefSize = 0;
- Status = ManFileFindTitleSection (FileHandle, Command, BriefDesc, &BriefSize, &Ascii);
- if (!EFI_ERROR (Status) && (HelpText != NULL)) {
- Status = ManFileFindSections (FileHandle, Sections, HelpText, &HelpSize, Ascii);
- }
-
- ShellCloseFile (&FileHandle);
- if (!EFI_ERROR (Status)) {
- //
- // Found what we need and return
- //
- goto Done;
- }
-
- StringIdWalker += 1;
- } while (StringIdWalker < 0xFFFF && TempString != NULL);
- }
-
-Done:
- if (mShellManDriverHandle != NULL) {
- gBS->UninstallProtocolInterface (
- mShellManDriverHandle,
- &gEfiDevicePathProtocolGuid,
- &mShellManHiiDevicePath
- );
- mShellManDriverHandle = NULL;
- }
-
- if (mShellManHiiHandle != NULL) {
- HiiRemovePackages (mShellManHiiHandle);
- mShellManHiiHandle = NULL;
- }
-
- if (CmdFileImgHandle != NULL) {
- Status = gBS->UnloadImage (CmdFileImgHandle);
- }
-
- SHELL_FREE_NON_NULL (TempString);
- SHELL_FREE_NON_NULL (CmdFileName);
- SHELL_FREE_NON_NULL (CmdFilePathName);
- SHELL_FREE_NON_NULL (FileDevPath);
- SHELL_FREE_NON_NULL (DevPath);
-
- return (Status);
-}
+/** @file + Provides interface to shell MAN file parser. + + Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR> + Copyright 2015 Dell Inc. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Shell.h" + +#define SHELL_MAN_HII_GUID \ +{ \ + 0xf62ccd0c, 0x2449, 0x453c, { 0x8a, 0xcb, 0x8c, 0xc5, 0x7c, 0xf0, 0x2a, 0x97 } \ +} + +EFI_HII_HANDLE mShellManHiiHandle = NULL; +EFI_HANDLE mShellManDriverHandle = NULL; + +SHELL_MAN_HII_VENDOR_DEVICE_PATH mShellManHiiDevicePath = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8)(sizeof (VENDOR_DEVICE_PATH)), + (UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + SHELL_MAN_HII_GUID + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + (UINT8)(END_DEVICE_PATH_LENGTH), + (UINT8)((END_DEVICE_PATH_LENGTH) >> 8) + } + } +}; + +/** + Verifies that the filename has .EFI on the end. + + allocates a new buffer and copies the name (appending .EFI if necessary). + Caller to free the buffer. + + @param[in] NameString original name string + + @return the new filename with .efi as the extension. +**/ +CHAR16 * +GetExecuatableFileName ( + IN CONST CHAR16 *NameString + ) +{ + CHAR16 *Buffer; + CHAR16 *SuffixStr; + + if (NameString == NULL) { + return (NULL); + } + + // + // Fix the file name + // + if (StrnCmp (NameString+StrLen (NameString)-StrLen (L".efi"), L".efi", StrLen (L".efi")) == 0) { + Buffer = AllocateCopyPool (StrSize (NameString), NameString); + } else if (StrnCmp (NameString+StrLen (NameString)-StrLen (L".man"), L".man", StrLen (L".man")) == 0) { + Buffer = AllocateCopyPool (StrSize (NameString), NameString); + if (Buffer != NULL) { + SuffixStr = Buffer+StrLen (Buffer)-StrLen (L".man"); + StrnCpyS (SuffixStr, StrSize (L".man")/sizeof (CHAR16), L".efi", StrLen (L".efi")); + } + } else { + Buffer = AllocateZeroPool (StrSize (NameString) + StrLen (L".efi")*sizeof (CHAR16)); + if (Buffer != NULL) { + StrnCpyS ( + Buffer, + (StrSize (NameString) + StrLen (L".efi")*sizeof (CHAR16))/sizeof (CHAR16), + NameString, + StrLen (NameString) + ); + StrnCatS ( + Buffer, + (StrSize (NameString) + StrLen (L".efi")*sizeof (CHAR16))/sizeof (CHAR16), + L".efi", + StrLen (L".efi") + ); + } + } + + return (Buffer); +} + +/** + Verifies that the filename has .MAN on the end. + + allocates a new buffer and copies the name (appending .MAN if necessary) + + ASSERT if ManFileName is NULL + + @param[in] ManFileName original filename + + @return the new filename with .man as the extension. +**/ +CHAR16 * +GetManFileName ( + IN CONST CHAR16 *ManFileName + ) +{ + CHAR16 *Buffer; + + if (ManFileName == NULL) { + return (NULL); + } + + // + // Fix the file name + // + if (StrnCmp (ManFileName+StrLen (ManFileName)-4, L".man", 4) == 0) { + Buffer = AllocateCopyPool (StrSize (ManFileName), ManFileName); + } else { + Buffer = AllocateZeroPool (StrSize (ManFileName) + 4*sizeof (CHAR16)); + if (Buffer != NULL) { + StrnCpyS ( + Buffer, + (StrSize (ManFileName) + 4*sizeof (CHAR16))/sizeof (CHAR16), + ManFileName, + StrLen (ManFileName) + ); + StrnCatS ( + Buffer, + (StrSize (ManFileName) + 4*sizeof (CHAR16))/sizeof (CHAR16), + L".man", + 4 + ); + } + } + + return (Buffer); +} + +/** + Search the path environment variable for possible locations and test for + which one contains a man file with the name specified. If a valid file is found + stop searching and return the (opened) SHELL_FILE_HANDLE for that file. + + @param[in] FileName Name of the file to find and open. + @param[out] Handle Pointer to the handle of the found file. The + value of this is undefined for return values + except EFI_SUCCESS. + + @retval EFI_SUCCESS The file was found. Handle is a valid SHELL_FILE_HANDLE + @retval EFI_INVALID_PARAMETER A parameter had an invalid value. + @retval EFI_NOT_FOUND The file was not found. +**/ +EFI_STATUS +SearchPathForFile ( + IN CONST CHAR16 *FileName, + OUT SHELL_FILE_HANDLE *Handle + ) +{ + CHAR16 *FullFileName; + EFI_STATUS Status; + + if ( (FileName == NULL) + || (Handle == NULL) + || (StrLen (FileName) == 0) + ) + { + return (EFI_INVALID_PARAMETER); + } + + FullFileName = ShellFindFilePath (FileName); + if (FullFileName == NULL) { + return (EFI_NOT_FOUND); + } + + // + // now open that file + // + Status = EfiShellOpenFileByName (FullFileName, Handle, EFI_FILE_MODE_READ); + FreePool (FullFileName); + + return (Status); +} + +/** + Parses through the MAN file specified by SHELL_FILE_HANDLE and returns the + detailed help for any sub section specified in the comma separated list of + sections provided. If the end of the file or a .TH section is found then + return. + + Upon a successful return the caller is responsible to free the memory in *HelpText + + @param[in] Handle FileHandle to read from + @param[in] Sections name of command's sub sections to find + @param[out] HelpText pointer to pointer to string where text goes. + @param[out] HelpSize pointer to size of allocated HelpText (may be updated) + @param[in] Ascii TRUE if the file is ASCII, FALSE otherwise. + + @retval EFI_OUT_OF_RESOURCES a memory allocation failed. + @retval EFI_SUCCESS the section was found and its description stored in + an allocated buffer. +**/ +EFI_STATUS +ManFileFindSections ( + IN SHELL_FILE_HANDLE Handle, + IN CONST CHAR16 *Sections, + OUT CHAR16 **HelpText, + OUT UINTN *HelpSize, + IN BOOLEAN Ascii + ) +{ + EFI_STATUS Status; + CHAR16 *ReadLine; + UINTN Size; + BOOLEAN CurrentlyReading; + CHAR16 *SectionName; + UINTN SectionLen; + BOOLEAN Found; + + if ( (Handle == NULL) + || (HelpText == NULL) + || (HelpSize == NULL) + ) + { + return (EFI_INVALID_PARAMETER); + } + + Status = EFI_SUCCESS; + CurrentlyReading = FALSE; + Size = 1024; + Found = FALSE; + + ReadLine = AllocateZeroPool (Size); + if (ReadLine == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + for ( ; !ShellFileHandleEof (Handle); Size = 1024) { + Status = ShellFileHandleReadLine (Handle, ReadLine, &Size, TRUE, &Ascii); + if (ReadLine[0] == L'#') { + // + // Skip comment lines + // + continue; + } + + // + // ignore too small of buffer... + // + if (Status == EFI_BUFFER_TOO_SMALL) { + Status = EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + break; + } else if (StrnCmp (ReadLine, L".TH", 3) == 0) { + // + // we hit the end of this commands section so stop. + // + break; + } else if (StrnCmp (ReadLine, L".SH", 3) == 0) { + if (Sections == NULL) { + CurrentlyReading = TRUE; + continue; + } + + // + // we found a section + // + if (CurrentlyReading) { + CurrentlyReading = FALSE; + } + + // + // is this a section we want to read in? + // + for ( SectionName = ReadLine + 3 + ; *SectionName == L' ' + ; SectionName++) + { + } + + SectionLen = StrLen (SectionName); + SectionName = StrStr (Sections, SectionName); + if (SectionName == NULL) { + continue; + } + + if ((*(SectionName + SectionLen) == CHAR_NULL) || (*(SectionName + SectionLen) == L',')) { + CurrentlyReading = TRUE; + } + } else if (CurrentlyReading) { + Found = TRUE; + // + // copy and save the current line. + // + ASSERT ((*HelpText == NULL && *HelpSize == 0) || (*HelpText != NULL)); + StrnCatGrow (HelpText, HelpSize, ReadLine, 0); + StrnCatGrow (HelpText, HelpSize, L"\r\n", 0); + } + } + + FreePool (ReadLine); + if (!Found && !EFI_ERROR (Status)) { + return (EFI_NOT_FOUND); + } + + return (Status); +} + +/** + Parses a line from a MAN file to see if it is the Title Header. If it is, then + if the "Brief Description" is desired, allocate a buffer for it and return a + copy. Upon a successful return the caller is responsible to free the memory in + *BriefDesc + + Uses a simple state machine that allows "unlimited" whitespace before and after the + ".TH", compares Command and the MAN file command name without respect to case, and + allows "unlimited" whitespace and '0' and '1' characters before the Short Description. + The PCRE regex describing this functionality is: ^\s*\.TH\s+(\S)\s[\s01]*(.*)$ + where group 1 is the Command Name and group 2 is the Short Description. + + @param[in] Command name of command whose MAN file we think Line came from + @param[in] Line Pointer to a line from the MAN file + @param[out] BriefDesc pointer to pointer to string where description goes. + @param[out] BriefSize pointer to size of allocated BriefDesc + @param[out] Found TRUE if the Title Header was found and it belongs to Command + + @retval TRUE Line contained the Title Header + @retval FALSE Line did not contain the Title Header +**/ +BOOLEAN +IsTitleHeader ( + IN CONST CHAR16 *Command, + IN CHAR16 *Line, + OUT CHAR16 **BriefDesc OPTIONAL, + OUT UINTN *BriefSize OPTIONAL, + OUT BOOLEAN *Found + ) +{ + // The states of a simple state machine used to recognize a title header line + // and to extract the Short Description, if desired. + typedef enum { + LookForThMacro, LookForCommandName, CompareCommands, GetBriefDescription, Final + } STATEVALUES; + + STATEVALUES State; + UINTN CommandIndex; // Indexes Command as we compare its chars to the MAN file. + BOOLEAN ReturnValue; // TRUE if this the Title Header line of *some* MAN file. + BOOLEAN ReturnFound; // TRUE if this the Title Header line of *the desired* MAN file. + + ReturnValue = FALSE; + ReturnFound = FALSE; + CommandIndex = 0; + State = LookForThMacro; + + do { + if (*Line == L'\0') { + break; + } + + switch (State) { + // Handle "^\s*.TH\s" + // Go to state LookForCommandName if the title header macro is present; otherwise, + // eat white space. If we see something other than white space, this is not a + // title header line. + case LookForThMacro: + if ((StrnCmp (L".TH ", Line, 4) == 0) || (StrnCmp (L".TH\t", Line, 4) == 0)) { + Line += 4; + State = LookForCommandName; + } else if ((*Line == L' ') || (*Line == L'\t')) { + Line++; + } else { + State = Final; + } + + break; + + // Handle "\s*" + // Eat any "extra" whitespace after the title header macro (we have already seen + // at least one white space character). Go to state CompareCommands when a + // non-white space is seen. + case LookForCommandName: + if ((*Line == L' ') || (*Line == L'\t')) { + Line++; + } else { + ReturnValue = TRUE; // This is *some* command's title header line. + State = CompareCommands; + // Do not increment Line; it points to the first character of the command + // name on the title header line. + } + + break; + + // Handle "(\S)\s" + // Compare Command to the title header command name, ignoring case. When we + // reach the end of the command (i.e. we see white space), the next state + // depends on whether the caller wants a copy of the Brief Description. + case CompareCommands: + if ((*Line == L' ') || (*Line == L'\t')) { + ReturnFound = TRUE; // This is the desired command's title header line. + State = (BriefDesc == NULL) ? Final : GetBriefDescription; + } else if (CharToUpper (*Line) != CharToUpper (*(Command + CommandIndex++))) { + State = Final; + } + + Line++; + break; + + // Handle "[\s01]*(.*)$" + // Skip whitespace, '0', and '1' characters, if any, prior to the brief description. + // Return the description to the caller. + case GetBriefDescription: + if ((*Line != L' ') && (*Line != L'\t') && (*Line != L'0') && (*Line != L'1')) { + *BriefSize = StrSize (Line); + *BriefDesc = AllocateZeroPool (*BriefSize); + if (*BriefDesc != NULL) { + StrCpyS (*BriefDesc, (*BriefSize)/sizeof (CHAR16), Line); + } + + State = Final; + } + + Line++; + break; + + default: + break; + } + } while (State < Final); + + *Found = ReturnFound; + return ReturnValue; +} + +/** + Parses through the MAN file specified by SHELL_FILE_HANDLE and returns the + "Brief Description" for the .TH section as specified by Command. If the + command section is not found return EFI_NOT_FOUND. + + Upon a successful return the caller is responsible to free the memory in *BriefDesc + + @param[in] Handle FileHandle to read from + @param[in] Command name of command's section to find as entered on the + command line (may be a relative or absolute path or + be in any case: upper, lower, or mixed in numerous ways!). + @param[out] BriefDesc pointer to pointer to string where description goes. + @param[out] BriefSize pointer to size of allocated BriefDesc + @param[in, out] Ascii TRUE if the file is ASCII, FALSE otherwise, will be + set if the file handle is at the 0 position. + + @retval EFI_OUT_OF_RESOURCES a memory allocation failed. + @retval EFI_SUCCESS the section was found and its description stored in + an allocated buffer if requested. +**/ +EFI_STATUS +ManFileFindTitleSection ( + IN SHELL_FILE_HANDLE Handle, + IN CONST CHAR16 *Command, + OUT CHAR16 **BriefDesc OPTIONAL, + OUT UINTN *BriefSize OPTIONAL, + IN OUT BOOLEAN *Ascii + ) +{ + EFI_STATUS Status; + CHAR16 *ReadLine; + UINTN Size; + BOOLEAN Found; + UINTN Start; + + if ( (Handle == NULL) + || (Command == NULL) + || ((BriefDesc != NULL) && (BriefSize == NULL)) + ) + { + return (EFI_INVALID_PARAMETER); + } + + Status = EFI_SUCCESS; + Size = 1024; + Found = FALSE; + + ReadLine = AllocateZeroPool (Size); + if (ReadLine == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + // + // Do not pass any leading path information that may be present to IsTitleHeader(). + // + Start = StrLen (Command); + while ( (Start != 0) + && (*(Command + Start - 1) != L'\\') + && (*(Command + Start - 1) != L'/') + && (*(Command + Start - 1) != L':')) + { + --Start; + } + + for ( ; !ShellFileHandleEof (Handle); Size = 1024) { + Status = ShellFileHandleReadLine (Handle, ReadLine, &Size, TRUE, Ascii); + // + // ignore too small of buffer... + // + if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) { + break; + } + + Status = EFI_NOT_FOUND; + if (IsTitleHeader (Command+Start, ReadLine, BriefDesc, BriefSize, &Found)) { + Status = Found ? EFI_SUCCESS : EFI_NOT_FOUND; + break; + } + } + + FreePool (ReadLine); + return (Status); +} + +/** + This function returns the help information for the specified command. The help text + will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B) + + If Sections is specified, then each section name listed will be compared in a casesensitive + manner, to the section names described in Appendix B. If the section exists, + it will be appended to the returned help text. If the section does not exist, no + information will be returned. If Sections is NULL, then all help text information + available will be returned. + + if BriefDesc is NULL, then the breif description will not be saved separately, + but placed first in the main HelpText. + + @param[in] ManFileName Points to the NULL-terminated UEFI Shell MAN file name. + @param[in] Command Points to the NULL-terminated UEFI Shell command name. + @param[in] Sections Points to the NULL-terminated comma-delimited + section names to return. If NULL, then all + sections will be returned. + @param[out] BriefDesc On return, points to a callee-allocated buffer + containing brief description text. + @param[out] HelpText On return, points to a callee-allocated buffer + containing all specified help text. + + @retval EFI_SUCCESS The help text was returned. + @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the + returned help text. + @retval EFI_INVALID_PARAMETER HelpText is NULL. + @retval EFI_INVALID_PARAMETER ManFileName is invalid. + @retval EFI_NOT_FOUND There is no help text available for Command. +**/ +EFI_STATUS +ProcessManFile ( + IN CONST CHAR16 *ManFileName, + IN CONST CHAR16 *Command, + IN CONST CHAR16 *Sections OPTIONAL, + OUT CHAR16 **BriefDesc OPTIONAL, + OUT CHAR16 **HelpText + ) +{ + CHAR16 *TempString; + SHELL_FILE_HANDLE FileHandle; + EFI_HANDLE CmdFileImgHandle; + EFI_STATUS Status; + UINTN HelpSize; + UINTN BriefSize; + UINTN StringIdWalker; + BOOLEAN Ascii; + CHAR16 *CmdFileName; + CHAR16 *CmdFilePathName; + EFI_DEVICE_PATH_PROTOCOL *FileDevPath; + EFI_DEVICE_PATH_PROTOCOL *DevPath; + EFI_HII_PACKAGE_LIST_HEADER *PackageListHeader; + + if ( (ManFileName == NULL) + || (Command == NULL) + || (HelpText == NULL) + ) + { + return (EFI_INVALID_PARAMETER); + } + + HelpSize = 0; + BriefSize = 0; + StringIdWalker = 0; + TempString = NULL; + Ascii = FALSE; + CmdFileName = NULL; + CmdFilePathName = NULL; + CmdFileImgHandle = NULL; + PackageListHeader = NULL; + FileDevPath = NULL; + DevPath = NULL; + + // + // See if it's in HII first + // + TempString = ShellCommandGetCommandHelp (Command); + if (TempString != NULL) { + FileHandle = ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (TRUE), NULL); + HelpSize = StrLen (TempString) * sizeof (CHAR16); + ShellWriteFile (FileHandle, &HelpSize, TempString); + ShellSetFilePosition (FileHandle, 0); + HelpSize = 0; + BriefSize = 0; + Status = ManFileFindTitleSection (FileHandle, Command, BriefDesc, &BriefSize, &Ascii); + if (!EFI_ERROR (Status) && (HelpText != NULL)) { + Status = ManFileFindSections (FileHandle, Sections, HelpText, &HelpSize, Ascii); + } + + ShellCloseFile (&FileHandle); + } else { + // + // If the image is a external app, check .MAN file first. + // + FileHandle = NULL; + TempString = GetManFileName (ManFileName); + if (TempString == NULL) { + return (EFI_INVALID_PARAMETER); + } + + Status = SearchPathForFile (TempString, &FileHandle); + if (EFI_ERROR (Status)) { + FileDevPath = FileDevicePath (NULL, TempString); + DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, FileDevPath); + Status = InternalOpenFileDevicePath (DevPath, &FileHandle, EFI_FILE_MODE_READ, 0); + SHELL_FREE_NON_NULL (FileDevPath); + SHELL_FREE_NON_NULL (DevPath); + } + + if (!EFI_ERROR (Status)) { + HelpSize = 0; + BriefSize = 0; + Status = ManFileFindTitleSection (FileHandle, Command, BriefDesc, &BriefSize, &Ascii); + if (!EFI_ERROR (Status) && (HelpText != NULL)) { + Status = ManFileFindSections (FileHandle, Sections, HelpText, &HelpSize, Ascii); + } + + ShellInfoObject.NewEfiShellProtocol->CloseFile (FileHandle); + if (!EFI_ERROR (Status)) { + // + // Get help text from .MAN file success. + // + goto Done; + } + } + + // + // Load the app image to check EFI_HII_PACKAGE_LIST_PROTOCOL. + // + CmdFileName = GetExecuatableFileName (TempString); + if (CmdFileName == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // If the file in CWD then use the file name, else use the full + // path name. + // + CmdFilePathName = ShellFindFilePath (CmdFileName); + if (CmdFilePathName == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + + DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath (CmdFilePathName); + Status = gBS->LoadImage (FALSE, gImageHandle, DevPath, NULL, 0, &CmdFileImgHandle); + if (EFI_ERROR (Status)) { + // + // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created + // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now. + // If the caller doesn't have the option to defer the execution of an image, we should + // unload image for the EFI_SECURITY_VIOLATION to avoid the resource leak. + // + if (Status == EFI_SECURITY_VIOLATION) { + gBS->UnloadImage (CmdFileImgHandle); + } + + *HelpText = NULL; + goto Done; + } + + Status = gBS->OpenProtocol ( + CmdFileImgHandle, + &gEfiHiiPackageListProtocolGuid, + (VOID **)&PackageListHeader, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + *HelpText = NULL; + goto Done; + } + + // + // If get package list on image handle, install it on HiiDatabase. + // + Status = gBS->InstallProtocolInterface ( + &mShellManDriverHandle, + &gEfiDevicePathProtocolGuid, + EFI_NATIVE_INTERFACE, + &mShellManHiiDevicePath + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = gHiiDatabase->NewPackageList ( + gHiiDatabase, + PackageListHeader, + mShellManDriverHandle, + &mShellManHiiHandle + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + StringIdWalker = 1; + do { + SHELL_FREE_NON_NULL (TempString); + if (BriefDesc != NULL) { + SHELL_FREE_NON_NULL (*BriefDesc); + } + + TempString = HiiGetString (mShellManHiiHandle, (EFI_STRING_ID)StringIdWalker, NULL); + if (TempString == NULL) { + Status = EFI_NOT_FOUND; + goto Done; + } + + FileHandle = ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (TRUE), NULL); + HelpSize = StrLen (TempString) * sizeof (CHAR16); + ShellWriteFile (FileHandle, &HelpSize, TempString); + ShellSetFilePosition (FileHandle, 0); + HelpSize = 0; + BriefSize = 0; + Status = ManFileFindTitleSection (FileHandle, Command, BriefDesc, &BriefSize, &Ascii); + if (!EFI_ERROR (Status) && (HelpText != NULL)) { + Status = ManFileFindSections (FileHandle, Sections, HelpText, &HelpSize, Ascii); + } + + ShellCloseFile (&FileHandle); + if (!EFI_ERROR (Status)) { + // + // Found what we need and return + // + goto Done; + } + + StringIdWalker += 1; + } while (StringIdWalker < 0xFFFF && TempString != NULL); + } + +Done: + if (mShellManDriverHandle != NULL) { + gBS->UninstallProtocolInterface ( + mShellManDriverHandle, + &gEfiDevicePathProtocolGuid, + &mShellManHiiDevicePath + ); + mShellManDriverHandle = NULL; + } + + if (mShellManHiiHandle != NULL) { + HiiRemovePackages (mShellManHiiHandle); + mShellManHiiHandle = NULL; + } + + if (CmdFileImgHandle != NULL) { + Status = gBS->UnloadImage (CmdFileImgHandle); + } + + SHELL_FREE_NON_NULL (TempString); + SHELL_FREE_NON_NULL (CmdFileName); + SHELL_FREE_NON_NULL (CmdFilePathName); + SHELL_FREE_NON_NULL (FileDevPath); + SHELL_FREE_NON_NULL (DevPath); + + return (Status); +} diff --git a/ShellPkg/Application/Shell/ShellManParser.h b/ShellPkg/Application/Shell/ShellManParser.h index 388ae2aad9..8522bc3d78 100644 --- a/ShellPkg/Application/Shell/ShellManParser.h +++ b/ShellPkg/Application/Shell/ShellManParser.h @@ -1,77 +1,77 @@ -/** @file
- Provides interface to shell MAN file parser.
-
- Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#ifndef _SHELL_MAN_FILE_PARSER_HEADER_
-#define _SHELL_MAN_FILE_PARSER_HEADER_
-
-/**
- This function returns the help information for the specified command. The help text
- will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B)
-
- If Sections is specified, then each section name listed will be compared in a casesensitive
- manner, to the section names described in Appendix B. If the section exists,
- it will be appended to the returned help text. If the section does not exist, no
- information will be returned. If Sections is NULL, then all help text information
- available will be returned.
-
- if BriefDesc is NULL, then the breif description will not be savedd separately,
- but placed first in the main HelpText.
-
- @param[in] ManFileName Points to the NULL-terminated UEFI Shell MAN file name.
- @param[in] Command Points to the NULL-terminated UEFI Shell command name.
- @param[in] Sections Points to the NULL-terminated comma-delimited
- section names to return. If NULL, then all
- sections will be returned.
- @param[out] BriefDesc On return, points to a callee-allocated buffer
- containing brief description text.
- @param[out] HelpText On return, points to a callee-allocated buffer
- containing all specified help text.
-
- @retval EFI_SUCCESS The help text was returned.
- @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
- returned help text.
- @retval EFI_INVALID_PARAMETER HelpText is NULL
- @retval EFI_NOT_FOUND There is no help text available for Command.
-**/
-EFI_STATUS
-ProcessManFile (
- IN CONST CHAR16 *ManFileName,
- IN CONST CHAR16 *Command,
- IN CONST CHAR16 *Sections OPTIONAL,
- OUT CHAR16 **BriefDesc,
- OUT CHAR16 **HelpText
- );
-
-/**
- parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
- detailed help for any sub section specified in the comma separated list of
- sections provided. If the end of the file or a .TH section is found then
- return.
-
- Upon a successful return the caller is responsible to free the memory in *HelpText
-
- @param[in] Handle FileHandle to read from
- @param[in] Sections name of command's sub sections to find
- @param[out] HelpText pointer to pointer to string where text goes.
- @param[out] HelpSize pointer to size of allocated HelpText (may be updated)
- @param[in] Ascii TRUE if the file is ASCII, FALSE otherwise.
-
- @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
- @retval EFI_SUCCESS the section was found and its description stored in
- an allocated buffer.
-**/
-EFI_STATUS
-ManFileFindSections (
- IN SHELL_FILE_HANDLE Handle,
- IN CONST CHAR16 *Sections,
- OUT CHAR16 **HelpText,
- OUT UINTN *HelpSize,
- IN BOOLEAN Ascii
- );
-
-#endif //_SHELL_MAN_FILE_PARSER_HEADER_
+/** @file + Provides interface to shell MAN file parser. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SHELL_MAN_FILE_PARSER_HEADER_ +#define _SHELL_MAN_FILE_PARSER_HEADER_ + +/** + This function returns the help information for the specified command. The help text + will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B) + + If Sections is specified, then each section name listed will be compared in a casesensitive + manner, to the section names described in Appendix B. If the section exists, + it will be appended to the returned help text. If the section does not exist, no + information will be returned. If Sections is NULL, then all help text information + available will be returned. + + if BriefDesc is NULL, then the breif description will not be savedd separately, + but placed first in the main HelpText. + + @param[in] ManFileName Points to the NULL-terminated UEFI Shell MAN file name. + @param[in] Command Points to the NULL-terminated UEFI Shell command name. + @param[in] Sections Points to the NULL-terminated comma-delimited + section names to return. If NULL, then all + sections will be returned. + @param[out] BriefDesc On return, points to a callee-allocated buffer + containing brief description text. + @param[out] HelpText On return, points to a callee-allocated buffer + containing all specified help text. + + @retval EFI_SUCCESS The help text was returned. + @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the + returned help text. + @retval EFI_INVALID_PARAMETER HelpText is NULL + @retval EFI_NOT_FOUND There is no help text available for Command. +**/ +EFI_STATUS +ProcessManFile ( + IN CONST CHAR16 *ManFileName, + IN CONST CHAR16 *Command, + IN CONST CHAR16 *Sections OPTIONAL, + OUT CHAR16 **BriefDesc, + OUT CHAR16 **HelpText + ); + +/** + parses through the MAN file specified by SHELL_FILE_HANDLE and returns the + detailed help for any sub section specified in the comma separated list of + sections provided. If the end of the file or a .TH section is found then + return. + + Upon a successful return the caller is responsible to free the memory in *HelpText + + @param[in] Handle FileHandle to read from + @param[in] Sections name of command's sub sections to find + @param[out] HelpText pointer to pointer to string where text goes. + @param[out] HelpSize pointer to size of allocated HelpText (may be updated) + @param[in] Ascii TRUE if the file is ASCII, FALSE otherwise. + + @retval EFI_OUT_OF_RESOURCES a memory allocation failed. + @retval EFI_SUCCESS the section was found and its description stored in + an allocated buffer. +**/ +EFI_STATUS +ManFileFindSections ( + IN SHELL_FILE_HANDLE Handle, + IN CONST CHAR16 *Sections, + OUT CHAR16 **HelpText, + OUT UINTN *HelpSize, + IN BOOLEAN Ascii + ); + +#endif //_SHELL_MAN_FILE_PARSER_HEADER_ diff --git a/ShellPkg/Application/Shell/ShellParametersProtocol.c b/ShellPkg/Application/Shell/ShellParametersProtocol.c index 64d67d9f13..7cd2f9088b 100644 --- a/ShellPkg/Application/Shell/ShellParametersProtocol.c +++ b/ShellPkg/Application/Shell/ShellParametersProtocol.c @@ -1,1504 +1,1504 @@ -/** @file
- Member functions of EFI_SHELL_PARAMETERS_PROTOCOL and functions for creation,
- manipulation, and initialization of EFI_SHELL_PARAMETERS_PROTOCOL.
-
- (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
- Copyright (C) 2014, Red Hat, Inc.
- (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR>
- Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "Shell.h"
-
-BOOLEAN AsciiRedirection = FALSE;
-
-/**
- Return the next parameter's end from a command line string.
-
- @param[in] String the string to parse
-**/
-CONST CHAR16 *
-FindEndOfParameter (
- IN CONST CHAR16 *String
- )
-{
- CONST CHAR16 *First;
- CONST CHAR16 *CloseQuote;
-
- First = FindFirstCharacter (String, L" \"", L'^');
-
- //
- // nothing, all one parameter remaining
- //
- if (*First == CHAR_NULL) {
- return (First);
- }
-
- //
- // If space before a quote (or neither found, i.e. both CHAR_NULL),
- // then that's the end.
- //
- if (*First == L' ') {
- return (First);
- }
-
- CloseQuote = FindFirstCharacter (First+1, L"\"", L'^');
-
- //
- // We did not find a terminator...
- //
- if (*CloseQuote == CHAR_NULL) {
- return (NULL);
- }
-
- return (FindEndOfParameter (CloseQuote+1));
-}
-
-/**
- Return the next parameter from a command line string.
-
- This function moves the next parameter from Walker into TempParameter and moves
- Walker up past that parameter for recursive calling. When the final parameter
- is moved *Walker will be set to NULL;
-
- Temp Parameter must be large enough to hold the parameter before calling this
- function.
-
- This will also remove all remaining ^ characters after processing.
-
- @param[in, out] Walker pointer to string of command line. Adjusted to
- remaining command line on return
- @param[in, out] TempParameter pointer to string of command line item extracted.
- @param[in] Length buffer size of TempParameter.
- @param[in] StripQuotation if TRUE then strip the quotation marks surrounding
- the parameters.
-
- @return EFI_INVALID_PARAMETER A required parameter was NULL or pointed to a NULL or empty string.
- @return EFI_NOT_FOUND A closing " could not be found on the specified string
-**/
-EFI_STATUS
-GetNextParameter (
- IN OUT CHAR16 **Walker,
- IN OUT CHAR16 **TempParameter,
- IN CONST UINTN Length,
- IN BOOLEAN StripQuotation
- )
-{
- CONST CHAR16 *NextDelim;
-
- if ( (Walker == NULL)
- || (*Walker == NULL)
- || (TempParameter == NULL)
- || (*TempParameter == NULL)
- )
- {
- return (EFI_INVALID_PARAMETER);
- }
-
- //
- // make sure we dont have any leading spaces
- //
- while ((*Walker)[0] == L' ') {
- (*Walker)++;
- }
-
- //
- // make sure we still have some params now...
- //
- if (StrLen (*Walker) == 0) {
- DEBUG_CODE_BEGIN ();
- *Walker = NULL;
- DEBUG_CODE_END ();
- return (EFI_INVALID_PARAMETER);
- }
-
- NextDelim = FindEndOfParameter (*Walker);
-
- if (NextDelim == NULL) {
- DEBUG_CODE_BEGIN ();
- *Walker = NULL;
- DEBUG_CODE_END ();
- return (EFI_NOT_FOUND);
- }
-
- StrnCpyS (*TempParameter, Length / sizeof (CHAR16), (*Walker), NextDelim - *Walker);
-
- //
- // Add a CHAR_NULL if we didn't get one via the copy
- //
- if (*NextDelim != CHAR_NULL) {
- (*TempParameter)[NextDelim - *Walker] = CHAR_NULL;
- }
-
- //
- // Update Walker for the next iteration through the function
- //
- *Walker = (CHAR16 *)NextDelim;
-
- //
- // Remove any non-escaped quotes in the string
- // Remove any remaining escape characters in the string
- //
- for (NextDelim = FindFirstCharacter (*TempParameter, L"\"^", CHAR_NULL)
- ; *NextDelim != CHAR_NULL
- ; NextDelim = FindFirstCharacter (NextDelim, L"\"^", CHAR_NULL)
- )
- {
- if (*NextDelim == L'^') {
- //
- // eliminate the escape ^
- //
- CopyMem ((CHAR16 *)NextDelim, NextDelim + 1, StrSize (NextDelim + 1));
- NextDelim++;
- } else if (*NextDelim == L'\"') {
- //
- // eliminate the unescaped quote
- //
- if (StripQuotation) {
- CopyMem ((CHAR16 *)NextDelim, NextDelim + 1, StrSize (NextDelim + 1));
- } else {
- NextDelim++;
- }
- }
- }
-
- return EFI_SUCCESS;
-}
-
-/**
- Function to populate Argc and Argv.
-
- This function parses the CommandLine and divides it into standard C style Argc/Argv
- parameters for inclusion in EFI_SHELL_PARAMETERS_PROTOCOL. this supports space
- delimited and quote surrounded parameter definition.
-
- All special character processing (alias, environment variable, redirection,
- etc... must be complete before calling this API.
-
- @param[in] CommandLine String of command line to parse
- @param[in] StripQuotation if TRUE then strip the quotation marks surrounding
- the parameters.
- @param[in, out] Argv pointer to array of strings; one for each parameter
- @param[in, out] Argc pointer to number of strings in Argv array
-
- @return EFI_SUCCESS the operation was successful
- @return EFI_INVALID_PARAMETER some parameters are invalid
- @return EFI_OUT_OF_RESOURCES a memory allocation failed.
-**/
-EFI_STATUS
-ParseCommandLineToArgs (
- IN CONST CHAR16 *CommandLine,
- IN BOOLEAN StripQuotation,
- IN OUT CHAR16 ***Argv,
- IN OUT UINTN *Argc
- )
-{
- UINTN Count;
- CHAR16 *TempParameter;
- CHAR16 *Walker;
- CHAR16 *NewParam;
- CHAR16 *NewCommandLine;
- UINTN Size;
- EFI_STATUS Status;
-
- ASSERT (Argc != NULL);
- ASSERT (Argv != NULL);
-
- if ((CommandLine == NULL) || (StrLen (CommandLine) == 0)) {
- (*Argc) = 0;
- (*Argv) = NULL;
- return (EFI_SUCCESS);
- }
-
- NewCommandLine = AllocateCopyPool (StrSize (CommandLine), CommandLine);
- if (NewCommandLine == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- TrimSpaces (&NewCommandLine);
- Size = StrSize (NewCommandLine);
- TempParameter = AllocateZeroPool (Size);
- if (TempParameter == NULL) {
- SHELL_FREE_NON_NULL (NewCommandLine);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- for ( Count = 0,
- Walker = (CHAR16 *)NewCommandLine
- ; Walker != NULL && *Walker != CHAR_NULL
- ; Count++
- )
- {
- if (EFI_ERROR (GetNextParameter (&Walker, &TempParameter, Size, TRUE))) {
- break;
- }
- }
-
- //
- // lets allocate the pointer array
- //
- (*Argv) = AllocateZeroPool ((Count)*sizeof (CHAR16 *));
- if (*Argv == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto Done;
- }
-
- *Argc = 0;
- Walker = (CHAR16 *)NewCommandLine;
- while (Walker != NULL && *Walker != CHAR_NULL) {
- SetMem16 (TempParameter, Size, CHAR_NULL);
- if (EFI_ERROR (GetNextParameter (&Walker, &TempParameter, Size, StripQuotation))) {
- Status = EFI_INVALID_PARAMETER;
- goto Done;
- }
-
- NewParam = AllocateCopyPool (StrSize (TempParameter), TempParameter);
- if (NewParam == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto Done;
- }
-
- ((CHAR16 **)(*Argv))[(*Argc)] = NewParam;
- (*Argc)++;
- }
-
- ASSERT (Count >= (*Argc));
- Status = EFI_SUCCESS;
-
-Done:
- SHELL_FREE_NON_NULL (TempParameter);
- SHELL_FREE_NON_NULL (NewCommandLine);
- return (Status);
-}
-
-/**
- creates a new EFI_SHELL_PARAMETERS_PROTOCOL instance and populates it and then
- installs it on our handle and if there is an existing version of the protocol
- that one is cached for removal later.
-
- @param[in, out] NewShellParameters on a successful return, a pointer to pointer
- to the newly installed interface.
- @param[in, out] RootShellInstance on a successful return, pointer to boolean.
- TRUE if this is the root shell instance.
-
- @retval EFI_SUCCESS the operation completed successfully.
- @return other the operation failed.
- @sa ReinstallProtocolInterface
- @sa InstallProtocolInterface
- @sa ParseCommandLineToArgs
-**/
-EFI_STATUS
-CreatePopulateInstallShellParametersProtocol (
- IN OUT EFI_SHELL_PARAMETERS_PROTOCOL **NewShellParameters,
- IN OUT BOOLEAN *RootShellInstance
- )
-{
- EFI_STATUS Status;
- EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
- CHAR16 *FullCommandLine;
- UINTN Size;
-
- Size = 0;
- FullCommandLine = NULL;
- LoadedImage = NULL;
-
- //
- // Assert for valid parameters
- //
- ASSERT (NewShellParameters != NULL);
- ASSERT (RootShellInstance != NULL);
-
- //
- // See if we have a shell parameters placed on us
- //
- Status = gBS->OpenProtocol (
- gImageHandle,
- &gEfiShellParametersProtocolGuid,
- (VOID **)&ShellInfoObject.OldShellParameters,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- //
- // if we don't then we must be the root shell (error is expected)
- //
- if (EFI_ERROR (Status)) {
- *RootShellInstance = TRUE;
- }
-
- //
- // Allocate the new structure
- //
- *NewShellParameters = AllocateZeroPool (sizeof (EFI_SHELL_PARAMETERS_PROTOCOL));
- if ((*NewShellParameters) == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- //
- // get loaded image protocol
- //
- Status = gBS->OpenProtocol (
- gImageHandle,
- &gEfiLoadedImageProtocolGuid,
- (VOID **)&LoadedImage,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- ASSERT_EFI_ERROR (Status);
- //
- // Build the full command line
- //
- Status = SHELL_GET_ENVIRONMENT_VARIABLE (L"ShellOpt", &Size, FullCommandLine);
- if (Status == EFI_BUFFER_TOO_SMALL) {
- FullCommandLine = AllocateZeroPool (Size + LoadedImage->LoadOptionsSize);
- Status = SHELL_GET_ENVIRONMENT_VARIABLE (L"ShellOpt", &Size, FullCommandLine);
- }
-
- if (Status == EFI_NOT_FOUND) {
- //
- // no parameters via environment... ok
- //
- } else {
- if (EFI_ERROR (Status)) {
- return (Status);
- }
- }
-
- if ((Size == 0) && (LoadedImage->LoadOptionsSize != 0)) {
- ASSERT (FullCommandLine == NULL);
- //
- // Now we need to include a NULL terminator in the size.
- //
- Size = LoadedImage->LoadOptionsSize + sizeof (FullCommandLine[0]);
- FullCommandLine = AllocateZeroPool (Size);
- }
-
- if (FullCommandLine != NULL) {
- CopyMem (FullCommandLine, LoadedImage->LoadOptions, LoadedImage->LoadOptionsSize);
- //
- // Populate Argc and Argv
- //
- Status = ParseCommandLineToArgs (
- FullCommandLine,
- TRUE,
- &(*NewShellParameters)->Argv,
- &(*NewShellParameters)->Argc
- );
-
- FreePool (FullCommandLine);
-
- ASSERT_EFI_ERROR (Status);
- } else {
- (*NewShellParameters)->Argv = NULL;
- (*NewShellParameters)->Argc = 0;
- }
-
- //
- // Populate the 3 faked file systems...
- //
- if (*RootShellInstance) {
- (*NewShellParameters)->StdIn = &FileInterfaceStdIn;
- (*NewShellParameters)->StdOut = &FileInterfaceStdOut;
- (*NewShellParameters)->StdErr = &FileInterfaceStdErr;
- Status = gBS->InstallProtocolInterface (
- &gImageHandle,
- &gEfiShellParametersProtocolGuid,
- EFI_NATIVE_INTERFACE,
- (VOID *)(*NewShellParameters)
- );
- } else {
- //
- // copy from the existing ones
- //
- (*NewShellParameters)->StdIn = ShellInfoObject.OldShellParameters->StdIn;
- (*NewShellParameters)->StdOut = ShellInfoObject.OldShellParameters->StdOut;
- (*NewShellParameters)->StdErr = ShellInfoObject.OldShellParameters->StdErr;
- Status = gBS->ReinstallProtocolInterface (
- gImageHandle,
- &gEfiShellParametersProtocolGuid,
- (VOID *)ShellInfoObject.OldShellParameters,
- (VOID *)(*NewShellParameters)
- );
- }
-
- return (Status);
-}
-
-/**
- frees all memory used by creation and installation of shell parameters protocol
- and if there was an old version installed it will restore that one.
-
- @param NewShellParameters the interface of EFI_SHELL_PARAMETERS_PROTOCOL that is
- being cleaned up.
-
- @retval EFI_SUCCESS the cleanup was successful
- @return other the cleanup failed
- @sa ReinstallProtocolInterface
- @sa UninstallProtocolInterface
-**/
-EFI_STATUS
-CleanUpShellParametersProtocol (
- IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *NewShellParameters
- )
-{
- EFI_STATUS Status;
- UINTN LoopCounter;
-
- //
- // If the old exists we need to restore it
- //
- if (ShellInfoObject.OldShellParameters != NULL) {
- Status = gBS->ReinstallProtocolInterface (
- gImageHandle,
- &gEfiShellParametersProtocolGuid,
- (VOID *)NewShellParameters,
- (VOID *)ShellInfoObject.OldShellParameters
- );
- DEBUG_CODE (
- ShellInfoObject.OldShellParameters = NULL;
- );
- } else {
- //
- // No old one, just uninstall us...
- //
- Status = gBS->UninstallProtocolInterface (
- gImageHandle,
- &gEfiShellParametersProtocolGuid,
- (VOID *)NewShellParameters
- );
- }
-
- if (NewShellParameters->Argv != NULL) {
- for ( LoopCounter = 0
- ; LoopCounter < NewShellParameters->Argc
- ; LoopCounter++
- )
- {
- FreePool (NewShellParameters->Argv[LoopCounter]);
- }
-
- FreePool (NewShellParameters->Argv);
- }
-
- FreePool (NewShellParameters);
- return (Status);
-}
-
-/**
- Determine if a file name represents a unicode file.
-
- @param[in] FileName Pointer to the filename to open.
-
- @retval EFI_SUCCESS The file is a unicode file.
- @return An error upon failure.
-**/
-EFI_STATUS
-IsUnicodeFile (
- IN CONST CHAR16 *FileName
- )
-{
- SHELL_FILE_HANDLE Handle;
- EFI_STATUS Status;
- UINT64 OriginalFilePosition;
- UINTN CharSize;
- CHAR16 CharBuffer;
-
- Status = gEfiShellProtocol->OpenFileByName (FileName, &Handle, EFI_FILE_MODE_READ);
- if (EFI_ERROR (Status)) {
- return (Status);
- }
-
- gEfiShellProtocol->GetFilePosition (Handle, &OriginalFilePosition);
- gEfiShellProtocol->SetFilePosition (Handle, 0);
- CharSize = sizeof (CHAR16);
- Status = gEfiShellProtocol->ReadFile (Handle, &CharSize, &CharBuffer);
- if (EFI_ERROR (Status) || (CharBuffer != gUnicodeFileTag)) {
- Status = EFI_BUFFER_TOO_SMALL;
- }
-
- gEfiShellProtocol->SetFilePosition (Handle, OriginalFilePosition);
- gEfiShellProtocol->CloseFile (Handle);
- return (Status);
-}
-
-/**
- Strips out quotes sections of a string.
-
- All of the characters between quotes is replaced with spaces.
-
- @param[in, out] TheString A pointer to the string to update.
-**/
-VOID
-StripQuotes (
- IN OUT CHAR16 *TheString
- )
-{
- BOOLEAN RemoveNow;
-
- for (RemoveNow = FALSE; TheString != NULL && *TheString != CHAR_NULL; TheString++) {
- if ((*TheString == L'^') && (*(TheString + 1) == L'\"')) {
- TheString++;
- } else if (*TheString == L'\"') {
- RemoveNow = (BOOLEAN) !RemoveNow;
- } else if (RemoveNow) {
- *TheString = L' ';
- }
- }
-}
-
-/**
- Calculate the 32-bit CRC in a EFI table using the service provided by the
- gRuntime service.
-
- @param Hdr Pointer to an EFI standard header
-
-**/
-VOID
-CalculateEfiHdrCrc (
- IN OUT EFI_TABLE_HEADER *Hdr
- )
-{
- UINT32 Crc;
-
- Hdr->CRC32 = 0;
-
- //
- // If gBS->CalculateCrce32 () == CoreEfiNotAvailableYet () then
- // Crc will come back as zero if we set it to zero here
- //
- Crc = 0;
- gBS->CalculateCrc32 ((UINT8 *)Hdr, Hdr->HeaderSize, &Crc);
- Hdr->CRC32 = Crc;
-}
-
-/**
- Fix a string to only have the file name, removing starting at the first space of whatever is quoted.
-
- @param[in] FileName The filename to start with.
-
- @retval NULL FileName was invalid.
- @return The modified FileName.
-**/
-CHAR16 *
-FixFileName (
- IN CHAR16 *FileName
- )
-{
- CHAR16 *Copy;
- CHAR16 *TempLocation;
-
- if (FileName == NULL) {
- return (NULL);
- }
-
- if (FileName[0] == L'\"') {
- Copy = FileName+1;
- if ((TempLocation = StrStr (Copy, L"\"")) != NULL) {
- TempLocation[0] = CHAR_NULL;
- }
- } else {
- Copy = FileName;
- while (Copy[0] == L' ') {
- Copy++;
- }
-
- if ((TempLocation = StrStr (Copy, L" ")) != NULL) {
- TempLocation[0] = CHAR_NULL;
- }
- }
-
- if (Copy[0] == CHAR_NULL) {
- return (NULL);
- }
-
- return (Copy);
-}
-
-/**
- Fix a string to only have the environment variable name, removing starting at the first space of whatever is quoted and removing the leading and trailing %.
-
- @param[in] FileName The filename to start with.
-
- @retval NULL FileName was invalid.
- @return The modified FileName.
-**/
-CHAR16 *
-FixVarName (
- IN CHAR16 *FileName
- )
-{
- CHAR16 *Copy;
- CHAR16 *TempLocation;
-
- Copy = FileName;
-
- if (FileName[0] == L'%') {
- Copy = FileName+1;
- if ((TempLocation = StrStr (Copy, L"%")) != NULL) {
- TempLocation[0] = CHAR_NULL;
- }
- }
-
- return (FixFileName (Copy));
-}
-
-/**
- Write the unicode file tag to the specified file.
-
- It is the caller's responsibility to ensure that
- ShellInfoObject.NewEfiShellProtocol has been initialized before calling this
- function.
-
- @param[in] FileHandle The file to write the unicode file tag to.
-
- @return Status code from ShellInfoObject.NewEfiShellProtocol->WriteFile.
-**/
-EFI_STATUS
-WriteFileTag (
- IN SHELL_FILE_HANDLE FileHandle
- )
-{
- CHAR16 FileTag;
- UINTN Size;
- EFI_STATUS Status;
-
- FileTag = gUnicodeFileTag;
- Size = sizeof FileTag;
- Status = ShellInfoObject.NewEfiShellProtocol->WriteFile (
- FileHandle,
- &Size,
- &FileTag
- );
- ASSERT (EFI_ERROR (Status) || Size == sizeof FileTag);
- return Status;
-}
-
-/**
- Function will replace the current StdIn and StdOut in the ShellParameters protocol
- structure by parsing NewCommandLine. The current values are returned to the
- user.
-
- This will also update the system table.
-
- @param[in, out] ShellParameters Pointer to parameter structure to modify.
- @param[in] NewCommandLine The new command line to parse and use.
- @param[out] OldStdIn Pointer to old StdIn.
- @param[out] OldStdOut Pointer to old StdOut.
- @param[out] OldStdErr Pointer to old StdErr.
- @param[out] SystemTableInfo Pointer to old system table information.
-
- @retval EFI_SUCCESS Operation was successful, Argv and Argc are valid.
- @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
-**/
-EFI_STATUS
-UpdateStdInStdOutStdErr (
- IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
- IN CHAR16 *NewCommandLine,
- OUT SHELL_FILE_HANDLE *OldStdIn,
- OUT SHELL_FILE_HANDLE *OldStdOut,
- OUT SHELL_FILE_HANDLE *OldStdErr,
- OUT SYSTEM_TABLE_INFO *SystemTableInfo
- )
-{
- CHAR16 *CommandLineCopy;
- CHAR16 *CommandLineWalker;
- CHAR16 *StdErrFileName;
- CHAR16 *StdOutFileName;
- CHAR16 *StdInFileName;
- CHAR16 *StdInVarName;
- CHAR16 *StdOutVarName;
- CHAR16 *StdErrVarName;
- EFI_STATUS Status;
- SHELL_FILE_HANDLE TempHandle;
- UINT64 FileSize;
- BOOLEAN OutUnicode;
- BOOLEAN InUnicode;
- BOOLEAN ErrUnicode;
- BOOLEAN OutAppend;
- BOOLEAN ErrAppend;
- UINTN Size;
- SPLIT_LIST *Split;
- CHAR16 *FirstLocation;
- BOOLEAN Volatile;
-
- OutUnicode = TRUE;
- InUnicode = TRUE;
- AsciiRedirection = FALSE;
- ErrUnicode = TRUE;
- StdInVarName = NULL;
- StdOutVarName = NULL;
- StdErrVarName = NULL;
- StdErrFileName = NULL;
- StdInFileName = NULL;
- StdOutFileName = NULL;
- ErrAppend = FALSE;
- OutAppend = FALSE;
- CommandLineCopy = NULL;
- FirstLocation = NULL;
-
- if ((ShellParameters == NULL) || (SystemTableInfo == NULL) || (OldStdIn == NULL) || (OldStdOut == NULL) || (OldStdErr == NULL)) {
- return (EFI_INVALID_PARAMETER);
- }
-
- SystemTableInfo->ConIn = gST->ConIn;
- SystemTableInfo->ConInHandle = gST->ConsoleInHandle;
- SystemTableInfo->ConOut = gST->ConOut;
- SystemTableInfo->ConOutHandle = gST->ConsoleOutHandle;
- SystemTableInfo->ErrOut = gST->StdErr;
- SystemTableInfo->ErrOutHandle = gST->StandardErrorHandle;
- *OldStdIn = ShellParameters->StdIn;
- *OldStdOut = ShellParameters->StdOut;
- *OldStdErr = ShellParameters->StdErr;
-
- if (NewCommandLine == NULL) {
- return (EFI_SUCCESS);
- }
-
- CommandLineCopy = StrnCatGrow (&CommandLineCopy, NULL, NewCommandLine, 0);
- if (CommandLineCopy == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- Status = EFI_SUCCESS;
- Split = NULL;
- FirstLocation = CommandLineCopy + StrLen (CommandLineCopy);
-
- StripQuotes (CommandLineCopy);
-
- if (!IsListEmpty (&ShellInfoObject.SplitList.Link)) {
- Split = (SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link);
- if ((Split != NULL) && (Split->SplitStdIn != NULL)) {
- ShellParameters->StdIn = Split->SplitStdIn;
- }
-
- if ((Split != NULL) && (Split->SplitStdOut != NULL)) {
- ShellParameters->StdOut = Split->SplitStdOut;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 2>>v ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 12, L' ');
- StdErrVarName = CommandLineWalker += 6;
- ErrAppend = TRUE;
- if (StrStr (CommandLineWalker, L" 2>>v ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 1>>v ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 12, L' ');
- StdOutVarName = CommandLineWalker += 6;
- OutAppend = TRUE;
- if (StrStr (CommandLineWalker, L" 1>>v ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- } else if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" >>v ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 10, L' ');
- StdOutVarName = CommandLineWalker += 5;
- OutAppend = TRUE;
- if (StrStr (CommandLineWalker, L" >>v ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- } else if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" >v ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 8, L' ');
- StdOutVarName = CommandLineWalker += 4;
- OutAppend = FALSE;
- if (StrStr (CommandLineWalker, L" >v ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 1>>a ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 12, L' ');
- StdOutFileName = CommandLineWalker += 6;
- OutAppend = TRUE;
- OutUnicode = FALSE;
- if (StrStr (CommandLineWalker, L" 1>>a ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 1>> ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 10, L' ');
- if (StdOutFileName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdOutFileName = CommandLineWalker += 5;
- OutAppend = TRUE;
- }
-
- if (StrStr (CommandLineWalker, L" 1>> ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" >> ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 8, L' ');
- if (StdOutFileName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdOutFileName = CommandLineWalker += 4;
- OutAppend = TRUE;
- }
-
- if (StrStr (CommandLineWalker, L" >> ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" >>a ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 10, L' ');
- if (StdOutFileName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdOutFileName = CommandLineWalker += 5;
- OutAppend = TRUE;
- OutUnicode = FALSE;
- }
-
- if (StrStr (CommandLineWalker, L" >>a ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 1>a ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 10, L' ');
- if (StdOutFileName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdOutFileName = CommandLineWalker += 5;
- OutAppend = FALSE;
- OutUnicode = FALSE;
- }
-
- if (StrStr (CommandLineWalker, L" 1>a ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" >a ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 8, L' ');
- if (StdOutFileName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdOutFileName = CommandLineWalker += 4;
- OutAppend = FALSE;
- OutUnicode = FALSE;
- }
-
- if (StrStr (CommandLineWalker, L" >a ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 2>> ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 10, L' ');
- if (StdErrFileName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdErrFileName = CommandLineWalker += 5;
- ErrAppend = TRUE;
- }
-
- if (StrStr (CommandLineWalker, L" 2>> ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 2>v ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 10, L' ');
- if (StdErrVarName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdErrVarName = CommandLineWalker += 5;
- ErrAppend = FALSE;
- }
-
- if (StrStr (CommandLineWalker, L" 2>v ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 1>v ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 10, L' ');
- if (StdOutVarName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdOutVarName = CommandLineWalker += 5;
- OutAppend = FALSE;
- }
-
- if (StrStr (CommandLineWalker, L" 1>v ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 2>a ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 10, L' ');
- if (StdErrFileName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdErrFileName = CommandLineWalker += 5;
- ErrAppend = FALSE;
- ErrUnicode = FALSE;
- }
-
- if (StrStr (CommandLineWalker, L" 2>a ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 2> ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 8, L' ');
- if (StdErrFileName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdErrFileName = CommandLineWalker += 4;
- ErrAppend = FALSE;
- }
-
- if (StrStr (CommandLineWalker, L" 2> ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 1> ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 8, L' ');
- if (StdOutFileName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdOutFileName = CommandLineWalker += 4;
- OutAppend = FALSE;
- }
-
- if (StrStr (CommandLineWalker, L" 1> ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" > ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 6, L' ');
- if (StdOutFileName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdOutFileName = CommandLineWalker += 3;
- OutAppend = FALSE;
- }
-
- if (StrStr (CommandLineWalker, L" > ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" < ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 6, L' ');
- if (StdInFileName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdInFileName = CommandLineWalker += 3;
- }
-
- if (StrStr (CommandLineWalker, L" < ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" <a ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 8, L' ');
- if (StdInFileName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdInFileName = CommandLineWalker += 4;
- InUnicode = FALSE;
- AsciiRedirection = TRUE;
- }
-
- if (StrStr (CommandLineWalker, L" <a ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" <v ")) != NULL)) {
- FirstLocation = MIN (CommandLineWalker, FirstLocation);
- SetMem16 (CommandLineWalker, 8, L' ');
- if (StdInVarName != NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- StdInVarName = CommandLineWalker += 4;
- }
-
- if (StrStr (CommandLineWalker, L" <v ") != NULL) {
- Status = EFI_NOT_FOUND;
- }
- }
-
- //
- // re-populate the string to support any filenames that were in quotes.
- //
- StrnCpyS (CommandLineCopy, StrSize (CommandLineCopy)/sizeof (CHAR16), NewCommandLine, StrLen (NewCommandLine));
-
- if ( (FirstLocation != CommandLineCopy + StrLen (CommandLineCopy))
- && (((UINTN)FirstLocation - (UINTN)CommandLineCopy)/sizeof (CHAR16) < StrLen (NewCommandLine))
- )
- {
- *(NewCommandLine + ((UINTN)FirstLocation - (UINTN)CommandLineCopy)/sizeof (CHAR16)) = CHAR_NULL;
- }
-
- if (!EFI_ERROR (Status)) {
- if (StdErrFileName != NULL) {
- if ((StdErrFileName = FixFileName (StdErrFileName)) == NULL) {
- Status = EFI_INVALID_PARAMETER;
- }
- }
-
- if (StdOutFileName != NULL) {
- if ((StdOutFileName = FixFileName (StdOutFileName)) == NULL) {
- Status = EFI_INVALID_PARAMETER;
- }
- }
-
- if (StdInFileName != NULL) {
- if ((StdInFileName = FixFileName (StdInFileName)) == NULL) {
- Status = EFI_INVALID_PARAMETER;
- }
- }
-
- if (StdErrVarName != NULL) {
- if ((StdErrVarName = FixVarName (StdErrVarName)) == NULL) {
- Status = EFI_INVALID_PARAMETER;
- }
- }
-
- if (StdOutVarName != NULL) {
- if ((StdOutVarName = FixVarName (StdOutVarName)) == NULL) {
- Status = EFI_INVALID_PARAMETER;
- }
- }
-
- if (StdInVarName != NULL) {
- if ((StdInVarName = FixVarName (StdInVarName)) == NULL) {
- Status = EFI_INVALID_PARAMETER;
- }
- }
-
- //
- // Verify not the same and not duplicating something from a split
- //
- if (
- //
- // Check that no 2 filenames are the same
- //
- ((StdErrFileName != NULL) && (StdOutFileName != NULL) && (StringNoCaseCompare (&StdErrFileName, &StdOutFileName) == 0))
- || ((StdErrFileName != NULL) && (StdInFileName != NULL) && (StringNoCaseCompare (&StdErrFileName, &StdInFileName) == 0))
- || ((StdOutFileName != NULL) && (StdInFileName != NULL) && (StringNoCaseCompare (&StdOutFileName, &StdInFileName) == 0))
- //
- // Check that no 2 variable names are the same
- //
- || ((StdErrVarName != NULL) && (StdInVarName != NULL) && (StringNoCaseCompare (&StdErrVarName, &StdInVarName) == 0))
- || ((StdOutVarName != NULL) && (StdInVarName != NULL) && (StringNoCaseCompare (&StdOutVarName, &StdInVarName) == 0))
- || ((StdErrVarName != NULL) && (StdOutVarName != NULL) && (StringNoCaseCompare (&StdErrVarName, &StdOutVarName) == 0))
- //
- // When a split (using | operator) is in place some are not allowed
- //
- || ((Split != NULL) && (Split->SplitStdIn != NULL) && ((StdInVarName != NULL) || (StdInFileName != NULL)))
- || ((Split != NULL) && (Split->SplitStdOut != NULL) && ((StdOutVarName != NULL) || (StdOutFileName != NULL)))
- //
- // Check that nothing is trying to be output to 2 locations.
- //
- || ((StdErrFileName != NULL) && (StdErrVarName != NULL))
- || ((StdOutFileName != NULL) && (StdOutVarName != NULL))
- || ((StdInFileName != NULL) && (StdInVarName != NULL))
- //
- // Check for no volatile environment variables
- //
- || ((StdErrVarName != NULL) && !EFI_ERROR (IsVolatileEnv (StdErrVarName, &Volatile)) && !Volatile)
- || ((StdOutVarName != NULL) && !EFI_ERROR (IsVolatileEnv (StdOutVarName, &Volatile)) && !Volatile)
- //
- // Cant redirect during a reconnect operation.
- //
- || ( (StrStr (NewCommandLine, L"connect -r") != NULL)
- && ((StdOutVarName != NULL) || (StdOutFileName != NULL) || (StdErrFileName != NULL) || (StdErrVarName != NULL)))
- //
- // Check that filetypes (Unicode/Ascii) do not change during an append
- //
- || ((StdOutFileName != NULL) && OutUnicode && OutAppend && (!EFI_ERROR (ShellFileExists (StdOutFileName)) && EFI_ERROR (IsUnicodeFile (StdOutFileName))))
- || ((StdErrFileName != NULL) && ErrUnicode && ErrAppend && (!EFI_ERROR (ShellFileExists (StdErrFileName)) && EFI_ERROR (IsUnicodeFile (StdErrFileName))))
- || ((StdOutFileName != NULL) && !OutUnicode && OutAppend && (!EFI_ERROR (ShellFileExists (StdOutFileName)) && !EFI_ERROR (IsUnicodeFile (StdOutFileName))))
- || ((StdErrFileName != NULL) && !ErrUnicode && ErrAppend && (!EFI_ERROR (ShellFileExists (StdErrFileName)) && !EFI_ERROR (IsUnicodeFile (StdErrFileName))))
- )
- {
- Status = EFI_INVALID_PARAMETER;
- ShellParameters->StdIn = *OldStdIn;
- ShellParameters->StdOut = *OldStdOut;
- ShellParameters->StdErr = *OldStdErr;
- } else if (!EFI_ERROR (Status)) {
- //
- // Open the Std<Whatever> and we should not have conflicts here...
- //
-
- //
- // StdErr to a file
- //
- if (StdErrFileName != NULL) {
- if (!ErrAppend) {
- //
- // delete existing file.
- //
- ShellInfoObject.NewEfiShellProtocol->DeleteFileByName (StdErrFileName);
- }
-
- Status = ShellOpenFileByName (StdErrFileName, &TempHandle, EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ|EFI_FILE_MODE_CREATE, 0);
- if (!ErrAppend && ErrUnicode && !EFI_ERROR (Status)) {
- Status = WriteFileTag (TempHandle);
- }
-
- if (!ErrUnicode && !EFI_ERROR (Status)) {
- TempHandle = CreateFileInterfaceFile (TempHandle, FALSE);
- ASSERT (TempHandle != NULL);
- }
-
- if (!EFI_ERROR (Status)) {
- ShellParameters->StdErr = TempHandle;
- gST->StdErr = CreateSimpleTextOutOnFile (TempHandle, &gST->StandardErrorHandle, gST->StdErr);
- }
- }
-
- //
- // StdOut to a file
- //
- if (!EFI_ERROR (Status) && (StdOutFileName != NULL)) {
- if (!OutAppend) {
- //
- // delete existing file.
- //
- ShellInfoObject.NewEfiShellProtocol->DeleteFileByName (StdOutFileName);
- }
-
- Status = ShellOpenFileByName (StdOutFileName, &TempHandle, EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ|EFI_FILE_MODE_CREATE, 0);
- if (TempHandle == NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- if (gUnicodeCollation->MetaiMatch (gUnicodeCollation, StdOutFileName, L"NUL")) {
- // no-op
- } else if (!OutAppend && OutUnicode && !EFI_ERROR (Status)) {
- Status = WriteFileTag (TempHandle);
- } else if (OutAppend) {
- Status = ShellInfoObject.NewEfiShellProtocol->GetFileSize (TempHandle, &FileSize);
- if (!EFI_ERROR (Status)) {
- //
- // When appending to a new unicode file, write the file tag.
- // Otherwise (ie. when appending to a new ASCII file, or an
- // existent file with any encoding), just seek to the end.
- //
- Status = (FileSize == 0 && OutUnicode) ?
- WriteFileTag (TempHandle) :
- ShellInfoObject.NewEfiShellProtocol->SetFilePosition (
- TempHandle,
- FileSize
- );
- }
- }
-
- if (!OutUnicode && !EFI_ERROR (Status)) {
- TempHandle = CreateFileInterfaceFile (TempHandle, FALSE);
- ASSERT (TempHandle != NULL);
- }
-
- if (!EFI_ERROR (Status)) {
- ShellParameters->StdOut = TempHandle;
- gST->ConOut = CreateSimpleTextOutOnFile (TempHandle, &gST->ConsoleOutHandle, gST->ConOut);
- }
- }
- }
-
- //
- // StdOut to a var
- //
- if (!EFI_ERROR (Status) && (StdOutVarName != NULL)) {
- if (!OutAppend) {
- //
- // delete existing variable.
- //
- SHELL_SET_ENVIRONMENT_VARIABLE_V (StdOutVarName, 0, L"");
- }
-
- TempHandle = CreateFileInterfaceEnv (StdOutVarName);
- ASSERT (TempHandle != NULL);
- ShellParameters->StdOut = TempHandle;
- gST->ConOut = CreateSimpleTextOutOnFile (TempHandle, &gST->ConsoleOutHandle, gST->ConOut);
- }
-
- //
- // StdErr to a var
- //
- if (!EFI_ERROR (Status) && (StdErrVarName != NULL)) {
- if (!ErrAppend) {
- //
- // delete existing variable.
- //
- SHELL_SET_ENVIRONMENT_VARIABLE_V (StdErrVarName, 0, L"");
- }
-
- TempHandle = CreateFileInterfaceEnv (StdErrVarName);
- ASSERT (TempHandle != NULL);
- ShellParameters->StdErr = TempHandle;
- gST->StdErr = CreateSimpleTextOutOnFile (TempHandle, &gST->StandardErrorHandle, gST->StdErr);
- }
-
- //
- // StdIn from a var
- //
- if (!EFI_ERROR (Status) && (StdInVarName != NULL)) {
- TempHandle = CreateFileInterfaceEnv (StdInVarName);
- if (TempHandle == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- } else {
- if (!InUnicode) {
- TempHandle = CreateFileInterfaceFile (TempHandle, FALSE);
- }
-
- Size = 0;
- if ((TempHandle == NULL) || (((EFI_FILE_PROTOCOL *)TempHandle)->Read (TempHandle, &Size, NULL) != EFI_BUFFER_TOO_SMALL)) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- ShellParameters->StdIn = TempHandle;
- gST->ConIn = CreateSimpleTextInOnFile (TempHandle, &gST->ConsoleInHandle);
- }
- }
- }
-
- //
- // StdIn from a file
- //
- if (!EFI_ERROR (Status) && (StdInFileName != NULL)) {
- Status = ShellOpenFileByName (
- StdInFileName,
- &TempHandle,
- EFI_FILE_MODE_READ,
- 0
- );
- if (!EFI_ERROR (Status)) {
- if (!InUnicode) {
- //
- // Create the ASCII->Unicode conversion layer
- //
- TempHandle = CreateFileInterfaceFile (TempHandle, FALSE);
- }
-
- ShellParameters->StdIn = TempHandle;
- gST->ConIn = CreateSimpleTextInOnFile (TempHandle, &gST->ConsoleInHandle);
- }
- }
- }
- }
-
- FreePool (CommandLineCopy);
-
- CalculateEfiHdrCrc (&gST->Hdr);
-
- if ((gST->ConIn == NULL) || (gST->ConOut == NULL)) {
- Status = EFI_OUT_OF_RESOURCES;
- }
-
- if (Status == EFI_NOT_FOUND) {
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle);
- } else if (EFI_ERROR (Status)) {
- ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);
- }
-
- return (Status);
-}
-
-/**
- Function will replace the current StdIn and StdOut in the ShellParameters protocol
- structure with StdIn and StdOut. The current values are de-allocated.
-
- @param[in, out] ShellParameters Pointer to parameter structure to modify.
- @param[in] OldStdIn Pointer to old StdIn.
- @param[in] OldStdOut Pointer to old StdOut.
- @param[in] OldStdErr Pointer to old StdErr.
- @param[in] SystemTableInfo Pointer to old system table information.
-**/
-EFI_STATUS
-RestoreStdInStdOutStdErr (
- IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
- IN SHELL_FILE_HANDLE *OldStdIn,
- IN SHELL_FILE_HANDLE *OldStdOut,
- IN SHELL_FILE_HANDLE *OldStdErr,
- IN SYSTEM_TABLE_INFO *SystemTableInfo
- )
-{
- SPLIT_LIST *Split;
-
- if ( (ShellParameters == NULL)
- || (OldStdIn == NULL)
- || (OldStdOut == NULL)
- || (OldStdErr == NULL)
- || (SystemTableInfo == NULL))
- {
- return (EFI_INVALID_PARAMETER);
- }
-
- if (!IsListEmpty (&ShellInfoObject.SplitList.Link)) {
- Split = (SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link);
- } else {
- Split = NULL;
- }
-
- if (ShellParameters->StdIn != *OldStdIn) {
- if (((Split != NULL) && (Split->SplitStdIn != ShellParameters->StdIn)) || (Split == NULL)) {
- gEfiShellProtocol->CloseFile (ShellParameters->StdIn);
- }
-
- ShellParameters->StdIn = *OldStdIn;
- }
-
- if (ShellParameters->StdOut != *OldStdOut) {
- if (((Split != NULL) && (Split->SplitStdOut != ShellParameters->StdOut)) || (Split == NULL)) {
- gEfiShellProtocol->CloseFile (ShellParameters->StdOut);
- }
-
- ShellParameters->StdOut = *OldStdOut;
- }
-
- if (ShellParameters->StdErr != *OldStdErr) {
- gEfiShellProtocol->CloseFile (ShellParameters->StdErr);
- ShellParameters->StdErr = *OldStdErr;
- }
-
- if (gST->ConIn != SystemTableInfo->ConIn) {
- CloseSimpleTextInOnFile (gST->ConIn);
- gST->ConIn = SystemTableInfo->ConIn;
- gST->ConsoleInHandle = SystemTableInfo->ConInHandle;
- }
-
- if (gST->ConOut != SystemTableInfo->ConOut) {
- CloseSimpleTextOutOnFile (gST->ConOut);
- gST->ConOut = SystemTableInfo->ConOut;
- gST->ConsoleOutHandle = SystemTableInfo->ConOutHandle;
- }
-
- if (gST->StdErr != SystemTableInfo->ErrOut) {
- CloseSimpleTextOutOnFile (gST->StdErr);
- gST->StdErr = SystemTableInfo->ErrOut;
- gST->StandardErrorHandle = SystemTableInfo->ErrOutHandle;
- }
-
- CalculateEfiHdrCrc (&gST->Hdr);
-
- return (EFI_SUCCESS);
-}
-
-/**
- Function will replace the current Argc and Argv in the ShellParameters protocol
- structure by parsing NewCommandLine. The current values are returned to the
- user.
-
- If OldArgv or OldArgc is NULL then that value is not returned.
-
- @param[in, out] ShellParameters Pointer to parameter structure to modify.
- @param[in] NewCommandLine The new command line to parse and use.
- @param[in] Type The type of operation.
- @param[out] OldArgv Pointer to old list of parameters.
- @param[out] OldArgc Pointer to old number of items in Argv list.
-
-
- @retval EFI_SUCCESS Operation was successful, Argv and Argc are valid.
- @return EFI_INVALID_PARAMETER Some parameters are invalid.
- @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
-**/
-EFI_STATUS
-UpdateArgcArgv (
- IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
- IN CONST CHAR16 *NewCommandLine,
- IN SHELL_OPERATION_TYPES Type,
- OUT CHAR16 ***OldArgv OPTIONAL,
- OUT UINTN *OldArgc OPTIONAL
- )
-{
- BOOLEAN StripParamQuotation;
-
- ASSERT (ShellParameters != NULL);
- StripParamQuotation = TRUE;
-
- if (OldArgc != NULL) {
- *OldArgc = ShellParameters->Argc;
- }
-
- if (OldArgc != NULL) {
- *OldArgv = ShellParameters->Argv;
- }
-
- if (Type == Script_File_Name) {
- StripParamQuotation = FALSE;
- }
-
- return ParseCommandLineToArgs (
- NewCommandLine,
- StripParamQuotation,
- &(ShellParameters->Argv),
- &(ShellParameters->Argc)
- );
-}
-
-/**
- Function will replace the current Argc and Argv in the ShellParameters protocol
- structure with Argv and Argc. The current values are de-allocated and the
- OldArgv must not be deallocated by the caller.
-
- @param[in, out] ShellParameters pointer to parameter structure to modify
- @param[in] OldArgv pointer to old list of parameters
- @param[in] OldArgc pointer to old number of items in Argv list
-**/
-VOID
-RestoreArgcArgv (
- IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
- IN CHAR16 ***OldArgv,
- IN UINTN *OldArgc
- )
-{
- UINTN LoopCounter;
-
- ASSERT (ShellParameters != NULL);
- ASSERT (OldArgv != NULL);
- ASSERT (OldArgc != NULL);
-
- if (ShellParameters->Argv != NULL) {
- for ( LoopCounter = 0
- ; LoopCounter < ShellParameters->Argc
- ; LoopCounter++
- )
- {
- FreePool (ShellParameters->Argv[LoopCounter]);
- }
-
- FreePool (ShellParameters->Argv);
- }
-
- ShellParameters->Argv = *OldArgv;
- *OldArgv = NULL;
- ShellParameters->Argc = *OldArgc;
- *OldArgc = 0;
-}
+/** @file + Member functions of EFI_SHELL_PARAMETERS_PROTOCOL and functions for creation, + manipulation, and initialization of EFI_SHELL_PARAMETERS_PROTOCOL. + + (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> + Copyright (C) 2014, Red Hat, Inc. + (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR> + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Shell.h" + +BOOLEAN AsciiRedirection = FALSE; + +/** + Return the next parameter's end from a command line string. + + @param[in] String the string to parse +**/ +CONST CHAR16 * +FindEndOfParameter ( + IN CONST CHAR16 *String + ) +{ + CONST CHAR16 *First; + CONST CHAR16 *CloseQuote; + + First = FindFirstCharacter (String, L" \"", L'^'); + + // + // nothing, all one parameter remaining + // + if (*First == CHAR_NULL) { + return (First); + } + + // + // If space before a quote (or neither found, i.e. both CHAR_NULL), + // then that's the end. + // + if (*First == L' ') { + return (First); + } + + CloseQuote = FindFirstCharacter (First+1, L"\"", L'^'); + + // + // We did not find a terminator... + // + if (*CloseQuote == CHAR_NULL) { + return (NULL); + } + + return (FindEndOfParameter (CloseQuote+1)); +} + +/** + Return the next parameter from a command line string. + + This function moves the next parameter from Walker into TempParameter and moves + Walker up past that parameter for recursive calling. When the final parameter + is moved *Walker will be set to NULL; + + Temp Parameter must be large enough to hold the parameter before calling this + function. + + This will also remove all remaining ^ characters after processing. + + @param[in, out] Walker pointer to string of command line. Adjusted to + remaining command line on return + @param[in, out] TempParameter pointer to string of command line item extracted. + @param[in] Length buffer size of TempParameter. + @param[in] StripQuotation if TRUE then strip the quotation marks surrounding + the parameters. + + @return EFI_INVALID_PARAMETER A required parameter was NULL or pointed to a NULL or empty string. + @return EFI_NOT_FOUND A closing " could not be found on the specified string +**/ +EFI_STATUS +GetNextParameter ( + IN OUT CHAR16 **Walker, + IN OUT CHAR16 **TempParameter, + IN CONST UINTN Length, + IN BOOLEAN StripQuotation + ) +{ + CONST CHAR16 *NextDelim; + + if ( (Walker == NULL) + || (*Walker == NULL) + || (TempParameter == NULL) + || (*TempParameter == NULL) + ) + { + return (EFI_INVALID_PARAMETER); + } + + // + // make sure we dont have any leading spaces + // + while ((*Walker)[0] == L' ') { + (*Walker)++; + } + + // + // make sure we still have some params now... + // + if (StrLen (*Walker) == 0) { + DEBUG_CODE_BEGIN (); + *Walker = NULL; + DEBUG_CODE_END (); + return (EFI_INVALID_PARAMETER); + } + + NextDelim = FindEndOfParameter (*Walker); + + if (NextDelim == NULL) { + DEBUG_CODE_BEGIN (); + *Walker = NULL; + DEBUG_CODE_END (); + return (EFI_NOT_FOUND); + } + + StrnCpyS (*TempParameter, Length / sizeof (CHAR16), (*Walker), NextDelim - *Walker); + + // + // Add a CHAR_NULL if we didn't get one via the copy + // + if (*NextDelim != CHAR_NULL) { + (*TempParameter)[NextDelim - *Walker] = CHAR_NULL; + } + + // + // Update Walker for the next iteration through the function + // + *Walker = (CHAR16 *)NextDelim; + + // + // Remove any non-escaped quotes in the string + // Remove any remaining escape characters in the string + // + for (NextDelim = FindFirstCharacter (*TempParameter, L"\"^", CHAR_NULL) + ; *NextDelim != CHAR_NULL + ; NextDelim = FindFirstCharacter (NextDelim, L"\"^", CHAR_NULL) + ) + { + if (*NextDelim == L'^') { + // + // eliminate the escape ^ + // + CopyMem ((CHAR16 *)NextDelim, NextDelim + 1, StrSize (NextDelim + 1)); + NextDelim++; + } else if (*NextDelim == L'\"') { + // + // eliminate the unescaped quote + // + if (StripQuotation) { + CopyMem ((CHAR16 *)NextDelim, NextDelim + 1, StrSize (NextDelim + 1)); + } else { + NextDelim++; + } + } + } + + return EFI_SUCCESS; +} + +/** + Function to populate Argc and Argv. + + This function parses the CommandLine and divides it into standard C style Argc/Argv + parameters for inclusion in EFI_SHELL_PARAMETERS_PROTOCOL. this supports space + delimited and quote surrounded parameter definition. + + All special character processing (alias, environment variable, redirection, + etc... must be complete before calling this API. + + @param[in] CommandLine String of command line to parse + @param[in] StripQuotation if TRUE then strip the quotation marks surrounding + the parameters. + @param[in, out] Argv pointer to array of strings; one for each parameter + @param[in, out] Argc pointer to number of strings in Argv array + + @return EFI_SUCCESS the operation was successful + @return EFI_INVALID_PARAMETER some parameters are invalid + @return EFI_OUT_OF_RESOURCES a memory allocation failed. +**/ +EFI_STATUS +ParseCommandLineToArgs ( + IN CONST CHAR16 *CommandLine, + IN BOOLEAN StripQuotation, + IN OUT CHAR16 ***Argv, + IN OUT UINTN *Argc + ) +{ + UINTN Count; + CHAR16 *TempParameter; + CHAR16 *Walker; + CHAR16 *NewParam; + CHAR16 *NewCommandLine; + UINTN Size; + EFI_STATUS Status; + + ASSERT (Argc != NULL); + ASSERT (Argv != NULL); + + if ((CommandLine == NULL) || (StrLen (CommandLine) == 0)) { + (*Argc) = 0; + (*Argv) = NULL; + return (EFI_SUCCESS); + } + + NewCommandLine = AllocateCopyPool (StrSize (CommandLine), CommandLine); + if (NewCommandLine == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + TrimSpaces (&NewCommandLine); + Size = StrSize (NewCommandLine); + TempParameter = AllocateZeroPool (Size); + if (TempParameter == NULL) { + SHELL_FREE_NON_NULL (NewCommandLine); + return (EFI_OUT_OF_RESOURCES); + } + + for ( Count = 0, + Walker = (CHAR16 *)NewCommandLine + ; Walker != NULL && *Walker != CHAR_NULL + ; Count++ + ) + { + if (EFI_ERROR (GetNextParameter (&Walker, &TempParameter, Size, TRUE))) { + break; + } + } + + // + // lets allocate the pointer array + // + (*Argv) = AllocateZeroPool ((Count)*sizeof (CHAR16 *)); + if (*Argv == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + *Argc = 0; + Walker = (CHAR16 *)NewCommandLine; + while (Walker != NULL && *Walker != CHAR_NULL) { + SetMem16 (TempParameter, Size, CHAR_NULL); + if (EFI_ERROR (GetNextParameter (&Walker, &TempParameter, Size, StripQuotation))) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + NewParam = AllocateCopyPool (StrSize (TempParameter), TempParameter); + if (NewParam == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + ((CHAR16 **)(*Argv))[(*Argc)] = NewParam; + (*Argc)++; + } + + ASSERT (Count >= (*Argc)); + Status = EFI_SUCCESS; + +Done: + SHELL_FREE_NON_NULL (TempParameter); + SHELL_FREE_NON_NULL (NewCommandLine); + return (Status); +} + +/** + creates a new EFI_SHELL_PARAMETERS_PROTOCOL instance and populates it and then + installs it on our handle and if there is an existing version of the protocol + that one is cached for removal later. + + @param[in, out] NewShellParameters on a successful return, a pointer to pointer + to the newly installed interface. + @param[in, out] RootShellInstance on a successful return, pointer to boolean. + TRUE if this is the root shell instance. + + @retval EFI_SUCCESS the operation completed successfully. + @return other the operation failed. + @sa ReinstallProtocolInterface + @sa InstallProtocolInterface + @sa ParseCommandLineToArgs +**/ +EFI_STATUS +CreatePopulateInstallShellParametersProtocol ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL **NewShellParameters, + IN OUT BOOLEAN *RootShellInstance + ) +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + CHAR16 *FullCommandLine; + UINTN Size; + + Size = 0; + FullCommandLine = NULL; + LoadedImage = NULL; + + // + // Assert for valid parameters + // + ASSERT (NewShellParameters != NULL); + ASSERT (RootShellInstance != NULL); + + // + // See if we have a shell parameters placed on us + // + Status = gBS->OpenProtocol ( + gImageHandle, + &gEfiShellParametersProtocolGuid, + (VOID **)&ShellInfoObject.OldShellParameters, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + // + // if we don't then we must be the root shell (error is expected) + // + if (EFI_ERROR (Status)) { + *RootShellInstance = TRUE; + } + + // + // Allocate the new structure + // + *NewShellParameters = AllocateZeroPool (sizeof (EFI_SHELL_PARAMETERS_PROTOCOL)); + if ((*NewShellParameters) == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + // + // get loaded image protocol + // + Status = gBS->OpenProtocol ( + gImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + ASSERT_EFI_ERROR (Status); + // + // Build the full command line + // + Status = SHELL_GET_ENVIRONMENT_VARIABLE (L"ShellOpt", &Size, FullCommandLine); + if (Status == EFI_BUFFER_TOO_SMALL) { + FullCommandLine = AllocateZeroPool (Size + LoadedImage->LoadOptionsSize); + Status = SHELL_GET_ENVIRONMENT_VARIABLE (L"ShellOpt", &Size, FullCommandLine); + } + + if (Status == EFI_NOT_FOUND) { + // + // no parameters via environment... ok + // + } else { + if (EFI_ERROR (Status)) { + return (Status); + } + } + + if ((Size == 0) && (LoadedImage->LoadOptionsSize != 0)) { + ASSERT (FullCommandLine == NULL); + // + // Now we need to include a NULL terminator in the size. + // + Size = LoadedImage->LoadOptionsSize + sizeof (FullCommandLine[0]); + FullCommandLine = AllocateZeroPool (Size); + } + + if (FullCommandLine != NULL) { + CopyMem (FullCommandLine, LoadedImage->LoadOptions, LoadedImage->LoadOptionsSize); + // + // Populate Argc and Argv + // + Status = ParseCommandLineToArgs ( + FullCommandLine, + TRUE, + &(*NewShellParameters)->Argv, + &(*NewShellParameters)->Argc + ); + + FreePool (FullCommandLine); + + ASSERT_EFI_ERROR (Status); + } else { + (*NewShellParameters)->Argv = NULL; + (*NewShellParameters)->Argc = 0; + } + + // + // Populate the 3 faked file systems... + // + if (*RootShellInstance) { + (*NewShellParameters)->StdIn = &FileInterfaceStdIn; + (*NewShellParameters)->StdOut = &FileInterfaceStdOut; + (*NewShellParameters)->StdErr = &FileInterfaceStdErr; + Status = gBS->InstallProtocolInterface ( + &gImageHandle, + &gEfiShellParametersProtocolGuid, + EFI_NATIVE_INTERFACE, + (VOID *)(*NewShellParameters) + ); + } else { + // + // copy from the existing ones + // + (*NewShellParameters)->StdIn = ShellInfoObject.OldShellParameters->StdIn; + (*NewShellParameters)->StdOut = ShellInfoObject.OldShellParameters->StdOut; + (*NewShellParameters)->StdErr = ShellInfoObject.OldShellParameters->StdErr; + Status = gBS->ReinstallProtocolInterface ( + gImageHandle, + &gEfiShellParametersProtocolGuid, + (VOID *)ShellInfoObject.OldShellParameters, + (VOID *)(*NewShellParameters) + ); + } + + return (Status); +} + +/** + frees all memory used by creation and installation of shell parameters protocol + and if there was an old version installed it will restore that one. + + @param NewShellParameters the interface of EFI_SHELL_PARAMETERS_PROTOCOL that is + being cleaned up. + + @retval EFI_SUCCESS the cleanup was successful + @return other the cleanup failed + @sa ReinstallProtocolInterface + @sa UninstallProtocolInterface +**/ +EFI_STATUS +CleanUpShellParametersProtocol ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *NewShellParameters + ) +{ + EFI_STATUS Status; + UINTN LoopCounter; + + // + // If the old exists we need to restore it + // + if (ShellInfoObject.OldShellParameters != NULL) { + Status = gBS->ReinstallProtocolInterface ( + gImageHandle, + &gEfiShellParametersProtocolGuid, + (VOID *)NewShellParameters, + (VOID *)ShellInfoObject.OldShellParameters + ); + DEBUG_CODE ( + ShellInfoObject.OldShellParameters = NULL; + ); + } else { + // + // No old one, just uninstall us... + // + Status = gBS->UninstallProtocolInterface ( + gImageHandle, + &gEfiShellParametersProtocolGuid, + (VOID *)NewShellParameters + ); + } + + if (NewShellParameters->Argv != NULL) { + for ( LoopCounter = 0 + ; LoopCounter < NewShellParameters->Argc + ; LoopCounter++ + ) + { + FreePool (NewShellParameters->Argv[LoopCounter]); + } + + FreePool (NewShellParameters->Argv); + } + + FreePool (NewShellParameters); + return (Status); +} + +/** + Determine if a file name represents a unicode file. + + @param[in] FileName Pointer to the filename to open. + + @retval EFI_SUCCESS The file is a unicode file. + @return An error upon failure. +**/ +EFI_STATUS +IsUnicodeFile ( + IN CONST CHAR16 *FileName + ) +{ + SHELL_FILE_HANDLE Handle; + EFI_STATUS Status; + UINT64 OriginalFilePosition; + UINTN CharSize; + CHAR16 CharBuffer; + + Status = gEfiShellProtocol->OpenFileByName (FileName, &Handle, EFI_FILE_MODE_READ); + if (EFI_ERROR (Status)) { + return (Status); + } + + gEfiShellProtocol->GetFilePosition (Handle, &OriginalFilePosition); + gEfiShellProtocol->SetFilePosition (Handle, 0); + CharSize = sizeof (CHAR16); + Status = gEfiShellProtocol->ReadFile (Handle, &CharSize, &CharBuffer); + if (EFI_ERROR (Status) || (CharBuffer != gUnicodeFileTag)) { + Status = EFI_BUFFER_TOO_SMALL; + } + + gEfiShellProtocol->SetFilePosition (Handle, OriginalFilePosition); + gEfiShellProtocol->CloseFile (Handle); + return (Status); +} + +/** + Strips out quotes sections of a string. + + All of the characters between quotes is replaced with spaces. + + @param[in, out] TheString A pointer to the string to update. +**/ +VOID +StripQuotes ( + IN OUT CHAR16 *TheString + ) +{ + BOOLEAN RemoveNow; + + for (RemoveNow = FALSE; TheString != NULL && *TheString != CHAR_NULL; TheString++) { + if ((*TheString == L'^') && (*(TheString + 1) == L'\"')) { + TheString++; + } else if (*TheString == L'\"') { + RemoveNow = (BOOLEAN) !RemoveNow; + } else if (RemoveNow) { + *TheString = L' '; + } + } +} + +/** + Calculate the 32-bit CRC in a EFI table using the service provided by the + gRuntime service. + + @param Hdr Pointer to an EFI standard header + +**/ +VOID +CalculateEfiHdrCrc ( + IN OUT EFI_TABLE_HEADER *Hdr + ) +{ + UINT32 Crc; + + Hdr->CRC32 = 0; + + // + // If gBS->CalculateCrce32 () == CoreEfiNotAvailableYet () then + // Crc will come back as zero if we set it to zero here + // + Crc = 0; + gBS->CalculateCrc32 ((UINT8 *)Hdr, Hdr->HeaderSize, &Crc); + Hdr->CRC32 = Crc; +} + +/** + Fix a string to only have the file name, removing starting at the first space of whatever is quoted. + + @param[in] FileName The filename to start with. + + @retval NULL FileName was invalid. + @return The modified FileName. +**/ +CHAR16 * +FixFileName ( + IN CHAR16 *FileName + ) +{ + CHAR16 *Copy; + CHAR16 *TempLocation; + + if (FileName == NULL) { + return (NULL); + } + + if (FileName[0] == L'\"') { + Copy = FileName+1; + if ((TempLocation = StrStr (Copy, L"\"")) != NULL) { + TempLocation[0] = CHAR_NULL; + } + } else { + Copy = FileName; + while (Copy[0] == L' ') { + Copy++; + } + + if ((TempLocation = StrStr (Copy, L" ")) != NULL) { + TempLocation[0] = CHAR_NULL; + } + } + + if (Copy[0] == CHAR_NULL) { + return (NULL); + } + + return (Copy); +} + +/** + Fix a string to only have the environment variable name, removing starting at the first space of whatever is quoted and removing the leading and trailing %. + + @param[in] FileName The filename to start with. + + @retval NULL FileName was invalid. + @return The modified FileName. +**/ +CHAR16 * +FixVarName ( + IN CHAR16 *FileName + ) +{ + CHAR16 *Copy; + CHAR16 *TempLocation; + + Copy = FileName; + + if (FileName[0] == L'%') { + Copy = FileName+1; + if ((TempLocation = StrStr (Copy, L"%")) != NULL) { + TempLocation[0] = CHAR_NULL; + } + } + + return (FixFileName (Copy)); +} + +/** + Write the unicode file tag to the specified file. + + It is the caller's responsibility to ensure that + ShellInfoObject.NewEfiShellProtocol has been initialized before calling this + function. + + @param[in] FileHandle The file to write the unicode file tag to. + + @return Status code from ShellInfoObject.NewEfiShellProtocol->WriteFile. +**/ +EFI_STATUS +WriteFileTag ( + IN SHELL_FILE_HANDLE FileHandle + ) +{ + CHAR16 FileTag; + UINTN Size; + EFI_STATUS Status; + + FileTag = gUnicodeFileTag; + Size = sizeof FileTag; + Status = ShellInfoObject.NewEfiShellProtocol->WriteFile ( + FileHandle, + &Size, + &FileTag + ); + ASSERT (EFI_ERROR (Status) || Size == sizeof FileTag); + return Status; +} + +/** + Function will replace the current StdIn and StdOut in the ShellParameters protocol + structure by parsing NewCommandLine. The current values are returned to the + user. + + This will also update the system table. + + @param[in, out] ShellParameters Pointer to parameter structure to modify. + @param[in] NewCommandLine The new command line to parse and use. + @param[out] OldStdIn Pointer to old StdIn. + @param[out] OldStdOut Pointer to old StdOut. + @param[out] OldStdErr Pointer to old StdErr. + @param[out] SystemTableInfo Pointer to old system table information. + + @retval EFI_SUCCESS Operation was successful, Argv and Argc are valid. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +UpdateStdInStdOutStdErr ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters, + IN CHAR16 *NewCommandLine, + OUT SHELL_FILE_HANDLE *OldStdIn, + OUT SHELL_FILE_HANDLE *OldStdOut, + OUT SHELL_FILE_HANDLE *OldStdErr, + OUT SYSTEM_TABLE_INFO *SystemTableInfo + ) +{ + CHAR16 *CommandLineCopy; + CHAR16 *CommandLineWalker; + CHAR16 *StdErrFileName; + CHAR16 *StdOutFileName; + CHAR16 *StdInFileName; + CHAR16 *StdInVarName; + CHAR16 *StdOutVarName; + CHAR16 *StdErrVarName; + EFI_STATUS Status; + SHELL_FILE_HANDLE TempHandle; + UINT64 FileSize; + BOOLEAN OutUnicode; + BOOLEAN InUnicode; + BOOLEAN ErrUnicode; + BOOLEAN OutAppend; + BOOLEAN ErrAppend; + UINTN Size; + SPLIT_LIST *Split; + CHAR16 *FirstLocation; + BOOLEAN Volatile; + + OutUnicode = TRUE; + InUnicode = TRUE; + AsciiRedirection = FALSE; + ErrUnicode = TRUE; + StdInVarName = NULL; + StdOutVarName = NULL; + StdErrVarName = NULL; + StdErrFileName = NULL; + StdInFileName = NULL; + StdOutFileName = NULL; + ErrAppend = FALSE; + OutAppend = FALSE; + CommandLineCopy = NULL; + FirstLocation = NULL; + + if ((ShellParameters == NULL) || (SystemTableInfo == NULL) || (OldStdIn == NULL) || (OldStdOut == NULL) || (OldStdErr == NULL)) { + return (EFI_INVALID_PARAMETER); + } + + SystemTableInfo->ConIn = gST->ConIn; + SystemTableInfo->ConInHandle = gST->ConsoleInHandle; + SystemTableInfo->ConOut = gST->ConOut; + SystemTableInfo->ConOutHandle = gST->ConsoleOutHandle; + SystemTableInfo->ErrOut = gST->StdErr; + SystemTableInfo->ErrOutHandle = gST->StandardErrorHandle; + *OldStdIn = ShellParameters->StdIn; + *OldStdOut = ShellParameters->StdOut; + *OldStdErr = ShellParameters->StdErr; + + if (NewCommandLine == NULL) { + return (EFI_SUCCESS); + } + + CommandLineCopy = StrnCatGrow (&CommandLineCopy, NULL, NewCommandLine, 0); + if (CommandLineCopy == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + Status = EFI_SUCCESS; + Split = NULL; + FirstLocation = CommandLineCopy + StrLen (CommandLineCopy); + + StripQuotes (CommandLineCopy); + + if (!IsListEmpty (&ShellInfoObject.SplitList.Link)) { + Split = (SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link); + if ((Split != NULL) && (Split->SplitStdIn != NULL)) { + ShellParameters->StdIn = Split->SplitStdIn; + } + + if ((Split != NULL) && (Split->SplitStdOut != NULL)) { + ShellParameters->StdOut = Split->SplitStdOut; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 2>>v ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 12, L' '); + StdErrVarName = CommandLineWalker += 6; + ErrAppend = TRUE; + if (StrStr (CommandLineWalker, L" 2>>v ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 1>>v ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 12, L' '); + StdOutVarName = CommandLineWalker += 6; + OutAppend = TRUE; + if (StrStr (CommandLineWalker, L" 1>>v ") != NULL) { + Status = EFI_NOT_FOUND; + } + } else if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" >>v ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 10, L' '); + StdOutVarName = CommandLineWalker += 5; + OutAppend = TRUE; + if (StrStr (CommandLineWalker, L" >>v ") != NULL) { + Status = EFI_NOT_FOUND; + } + } else if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" >v ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 8, L' '); + StdOutVarName = CommandLineWalker += 4; + OutAppend = FALSE; + if (StrStr (CommandLineWalker, L" >v ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 1>>a ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 12, L' '); + StdOutFileName = CommandLineWalker += 6; + OutAppend = TRUE; + OutUnicode = FALSE; + if (StrStr (CommandLineWalker, L" 1>>a ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 1>> ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 10, L' '); + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 5; + OutAppend = TRUE; + } + + if (StrStr (CommandLineWalker, L" 1>> ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" >> ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 8, L' '); + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 4; + OutAppend = TRUE; + } + + if (StrStr (CommandLineWalker, L" >> ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" >>a ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 10, L' '); + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 5; + OutAppend = TRUE; + OutUnicode = FALSE; + } + + if (StrStr (CommandLineWalker, L" >>a ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 1>a ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 10, L' '); + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 5; + OutAppend = FALSE; + OutUnicode = FALSE; + } + + if (StrStr (CommandLineWalker, L" 1>a ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" >a ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 8, L' '); + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 4; + OutAppend = FALSE; + OutUnicode = FALSE; + } + + if (StrStr (CommandLineWalker, L" >a ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 2>> ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 10, L' '); + if (StdErrFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdErrFileName = CommandLineWalker += 5; + ErrAppend = TRUE; + } + + if (StrStr (CommandLineWalker, L" 2>> ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 2>v ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 10, L' '); + if (StdErrVarName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdErrVarName = CommandLineWalker += 5; + ErrAppend = FALSE; + } + + if (StrStr (CommandLineWalker, L" 2>v ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 1>v ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 10, L' '); + if (StdOutVarName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutVarName = CommandLineWalker += 5; + OutAppend = FALSE; + } + + if (StrStr (CommandLineWalker, L" 1>v ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 2>a ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 10, L' '); + if (StdErrFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdErrFileName = CommandLineWalker += 5; + ErrAppend = FALSE; + ErrUnicode = FALSE; + } + + if (StrStr (CommandLineWalker, L" 2>a ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 2> ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 8, L' '); + if (StdErrFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdErrFileName = CommandLineWalker += 4; + ErrAppend = FALSE; + } + + if (StrStr (CommandLineWalker, L" 2> ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" 1> ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 8, L' '); + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 4; + OutAppend = FALSE; + } + + if (StrStr (CommandLineWalker, L" 1> ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" > ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 6, L' '); + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 3; + OutAppend = FALSE; + } + + if (StrStr (CommandLineWalker, L" > ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" < ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 6, L' '); + if (StdInFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdInFileName = CommandLineWalker += 3; + } + + if (StrStr (CommandLineWalker, L" < ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" <a ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 8, L' '); + if (StdInFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdInFileName = CommandLineWalker += 4; + InUnicode = FALSE; + AsciiRedirection = TRUE; + } + + if (StrStr (CommandLineWalker, L" <a ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + if (!EFI_ERROR (Status) && ((CommandLineWalker = StrStr (CommandLineCopy, L" <v ")) != NULL)) { + FirstLocation = MIN (CommandLineWalker, FirstLocation); + SetMem16 (CommandLineWalker, 8, L' '); + if (StdInVarName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdInVarName = CommandLineWalker += 4; + } + + if (StrStr (CommandLineWalker, L" <v ") != NULL) { + Status = EFI_NOT_FOUND; + } + } + + // + // re-populate the string to support any filenames that were in quotes. + // + StrnCpyS (CommandLineCopy, StrSize (CommandLineCopy)/sizeof (CHAR16), NewCommandLine, StrLen (NewCommandLine)); + + if ( (FirstLocation != CommandLineCopy + StrLen (CommandLineCopy)) + && (((UINTN)FirstLocation - (UINTN)CommandLineCopy)/sizeof (CHAR16) < StrLen (NewCommandLine)) + ) + { + *(NewCommandLine + ((UINTN)FirstLocation - (UINTN)CommandLineCopy)/sizeof (CHAR16)) = CHAR_NULL; + } + + if (!EFI_ERROR (Status)) { + if (StdErrFileName != NULL) { + if ((StdErrFileName = FixFileName (StdErrFileName)) == NULL) { + Status = EFI_INVALID_PARAMETER; + } + } + + if (StdOutFileName != NULL) { + if ((StdOutFileName = FixFileName (StdOutFileName)) == NULL) { + Status = EFI_INVALID_PARAMETER; + } + } + + if (StdInFileName != NULL) { + if ((StdInFileName = FixFileName (StdInFileName)) == NULL) { + Status = EFI_INVALID_PARAMETER; + } + } + + if (StdErrVarName != NULL) { + if ((StdErrVarName = FixVarName (StdErrVarName)) == NULL) { + Status = EFI_INVALID_PARAMETER; + } + } + + if (StdOutVarName != NULL) { + if ((StdOutVarName = FixVarName (StdOutVarName)) == NULL) { + Status = EFI_INVALID_PARAMETER; + } + } + + if (StdInVarName != NULL) { + if ((StdInVarName = FixVarName (StdInVarName)) == NULL) { + Status = EFI_INVALID_PARAMETER; + } + } + + // + // Verify not the same and not duplicating something from a split + // + if ( + // + // Check that no 2 filenames are the same + // + ((StdErrFileName != NULL) && (StdOutFileName != NULL) && (StringNoCaseCompare (&StdErrFileName, &StdOutFileName) == 0)) + || ((StdErrFileName != NULL) && (StdInFileName != NULL) && (StringNoCaseCompare (&StdErrFileName, &StdInFileName) == 0)) + || ((StdOutFileName != NULL) && (StdInFileName != NULL) && (StringNoCaseCompare (&StdOutFileName, &StdInFileName) == 0)) + // + // Check that no 2 variable names are the same + // + || ((StdErrVarName != NULL) && (StdInVarName != NULL) && (StringNoCaseCompare (&StdErrVarName, &StdInVarName) == 0)) + || ((StdOutVarName != NULL) && (StdInVarName != NULL) && (StringNoCaseCompare (&StdOutVarName, &StdInVarName) == 0)) + || ((StdErrVarName != NULL) && (StdOutVarName != NULL) && (StringNoCaseCompare (&StdErrVarName, &StdOutVarName) == 0)) + // + // When a split (using | operator) is in place some are not allowed + // + || ((Split != NULL) && (Split->SplitStdIn != NULL) && ((StdInVarName != NULL) || (StdInFileName != NULL))) + || ((Split != NULL) && (Split->SplitStdOut != NULL) && ((StdOutVarName != NULL) || (StdOutFileName != NULL))) + // + // Check that nothing is trying to be output to 2 locations. + // + || ((StdErrFileName != NULL) && (StdErrVarName != NULL)) + || ((StdOutFileName != NULL) && (StdOutVarName != NULL)) + || ((StdInFileName != NULL) && (StdInVarName != NULL)) + // + // Check for no volatile environment variables + // + || ((StdErrVarName != NULL) && !EFI_ERROR (IsVolatileEnv (StdErrVarName, &Volatile)) && !Volatile) + || ((StdOutVarName != NULL) && !EFI_ERROR (IsVolatileEnv (StdOutVarName, &Volatile)) && !Volatile) + // + // Cant redirect during a reconnect operation. + // + || ( (StrStr (NewCommandLine, L"connect -r") != NULL) + && ((StdOutVarName != NULL) || (StdOutFileName != NULL) || (StdErrFileName != NULL) || (StdErrVarName != NULL))) + // + // Check that filetypes (Unicode/Ascii) do not change during an append + // + || ((StdOutFileName != NULL) && OutUnicode && OutAppend && (!EFI_ERROR (ShellFileExists (StdOutFileName)) && EFI_ERROR (IsUnicodeFile (StdOutFileName)))) + || ((StdErrFileName != NULL) && ErrUnicode && ErrAppend && (!EFI_ERROR (ShellFileExists (StdErrFileName)) && EFI_ERROR (IsUnicodeFile (StdErrFileName)))) + || ((StdOutFileName != NULL) && !OutUnicode && OutAppend && (!EFI_ERROR (ShellFileExists (StdOutFileName)) && !EFI_ERROR (IsUnicodeFile (StdOutFileName)))) + || ((StdErrFileName != NULL) && !ErrUnicode && ErrAppend && (!EFI_ERROR (ShellFileExists (StdErrFileName)) && !EFI_ERROR (IsUnicodeFile (StdErrFileName)))) + ) + { + Status = EFI_INVALID_PARAMETER; + ShellParameters->StdIn = *OldStdIn; + ShellParameters->StdOut = *OldStdOut; + ShellParameters->StdErr = *OldStdErr; + } else if (!EFI_ERROR (Status)) { + // + // Open the Std<Whatever> and we should not have conflicts here... + // + + // + // StdErr to a file + // + if (StdErrFileName != NULL) { + if (!ErrAppend) { + // + // delete existing file. + // + ShellInfoObject.NewEfiShellProtocol->DeleteFileByName (StdErrFileName); + } + + Status = ShellOpenFileByName (StdErrFileName, &TempHandle, EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ|EFI_FILE_MODE_CREATE, 0); + if (!ErrAppend && ErrUnicode && !EFI_ERROR (Status)) { + Status = WriteFileTag (TempHandle); + } + + if (!ErrUnicode && !EFI_ERROR (Status)) { + TempHandle = CreateFileInterfaceFile (TempHandle, FALSE); + ASSERT (TempHandle != NULL); + } + + if (!EFI_ERROR (Status)) { + ShellParameters->StdErr = TempHandle; + gST->StdErr = CreateSimpleTextOutOnFile (TempHandle, &gST->StandardErrorHandle, gST->StdErr); + } + } + + // + // StdOut to a file + // + if (!EFI_ERROR (Status) && (StdOutFileName != NULL)) { + if (!OutAppend) { + // + // delete existing file. + // + ShellInfoObject.NewEfiShellProtocol->DeleteFileByName (StdOutFileName); + } + + Status = ShellOpenFileByName (StdOutFileName, &TempHandle, EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ|EFI_FILE_MODE_CREATE, 0); + if (TempHandle == NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + if (gUnicodeCollation->MetaiMatch (gUnicodeCollation, StdOutFileName, L"NUL")) { + // no-op + } else if (!OutAppend && OutUnicode && !EFI_ERROR (Status)) { + Status = WriteFileTag (TempHandle); + } else if (OutAppend) { + Status = ShellInfoObject.NewEfiShellProtocol->GetFileSize (TempHandle, &FileSize); + if (!EFI_ERROR (Status)) { + // + // When appending to a new unicode file, write the file tag. + // Otherwise (ie. when appending to a new ASCII file, or an + // existent file with any encoding), just seek to the end. + // + Status = (FileSize == 0 && OutUnicode) ? + WriteFileTag (TempHandle) : + ShellInfoObject.NewEfiShellProtocol->SetFilePosition ( + TempHandle, + FileSize + ); + } + } + + if (!OutUnicode && !EFI_ERROR (Status)) { + TempHandle = CreateFileInterfaceFile (TempHandle, FALSE); + ASSERT (TempHandle != NULL); + } + + if (!EFI_ERROR (Status)) { + ShellParameters->StdOut = TempHandle; + gST->ConOut = CreateSimpleTextOutOnFile (TempHandle, &gST->ConsoleOutHandle, gST->ConOut); + } + } + } + + // + // StdOut to a var + // + if (!EFI_ERROR (Status) && (StdOutVarName != NULL)) { + if (!OutAppend) { + // + // delete existing variable. + // + SHELL_SET_ENVIRONMENT_VARIABLE_V (StdOutVarName, 0, L""); + } + + TempHandle = CreateFileInterfaceEnv (StdOutVarName); + ASSERT (TempHandle != NULL); + ShellParameters->StdOut = TempHandle; + gST->ConOut = CreateSimpleTextOutOnFile (TempHandle, &gST->ConsoleOutHandle, gST->ConOut); + } + + // + // StdErr to a var + // + if (!EFI_ERROR (Status) && (StdErrVarName != NULL)) { + if (!ErrAppend) { + // + // delete existing variable. + // + SHELL_SET_ENVIRONMENT_VARIABLE_V (StdErrVarName, 0, L""); + } + + TempHandle = CreateFileInterfaceEnv (StdErrVarName); + ASSERT (TempHandle != NULL); + ShellParameters->StdErr = TempHandle; + gST->StdErr = CreateSimpleTextOutOnFile (TempHandle, &gST->StandardErrorHandle, gST->StdErr); + } + + // + // StdIn from a var + // + if (!EFI_ERROR (Status) && (StdInVarName != NULL)) { + TempHandle = CreateFileInterfaceEnv (StdInVarName); + if (TempHandle == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } else { + if (!InUnicode) { + TempHandle = CreateFileInterfaceFile (TempHandle, FALSE); + } + + Size = 0; + if ((TempHandle == NULL) || (((EFI_FILE_PROTOCOL *)TempHandle)->Read (TempHandle, &Size, NULL) != EFI_BUFFER_TOO_SMALL)) { + Status = EFI_INVALID_PARAMETER; + } else { + ShellParameters->StdIn = TempHandle; + gST->ConIn = CreateSimpleTextInOnFile (TempHandle, &gST->ConsoleInHandle); + } + } + } + + // + // StdIn from a file + // + if (!EFI_ERROR (Status) && (StdInFileName != NULL)) { + Status = ShellOpenFileByName ( + StdInFileName, + &TempHandle, + EFI_FILE_MODE_READ, + 0 + ); + if (!EFI_ERROR (Status)) { + if (!InUnicode) { + // + // Create the ASCII->Unicode conversion layer + // + TempHandle = CreateFileInterfaceFile (TempHandle, FALSE); + } + + ShellParameters->StdIn = TempHandle; + gST->ConIn = CreateSimpleTextInOnFile (TempHandle, &gST->ConsoleInHandle); + } + } + } + } + + FreePool (CommandLineCopy); + + CalculateEfiHdrCrc (&gST->Hdr); + + if ((gST->ConIn == NULL) || (gST->ConOut == NULL)) { + Status = EFI_OUT_OF_RESOURCES; + } + + if (Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle); + } else if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle); + } + + return (Status); +} + +/** + Function will replace the current StdIn and StdOut in the ShellParameters protocol + structure with StdIn and StdOut. The current values are de-allocated. + + @param[in, out] ShellParameters Pointer to parameter structure to modify. + @param[in] OldStdIn Pointer to old StdIn. + @param[in] OldStdOut Pointer to old StdOut. + @param[in] OldStdErr Pointer to old StdErr. + @param[in] SystemTableInfo Pointer to old system table information. +**/ +EFI_STATUS +RestoreStdInStdOutStdErr ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters, + IN SHELL_FILE_HANDLE *OldStdIn, + IN SHELL_FILE_HANDLE *OldStdOut, + IN SHELL_FILE_HANDLE *OldStdErr, + IN SYSTEM_TABLE_INFO *SystemTableInfo + ) +{ + SPLIT_LIST *Split; + + if ( (ShellParameters == NULL) + || (OldStdIn == NULL) + || (OldStdOut == NULL) + || (OldStdErr == NULL) + || (SystemTableInfo == NULL)) + { + return (EFI_INVALID_PARAMETER); + } + + if (!IsListEmpty (&ShellInfoObject.SplitList.Link)) { + Split = (SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link); + } else { + Split = NULL; + } + + if (ShellParameters->StdIn != *OldStdIn) { + if (((Split != NULL) && (Split->SplitStdIn != ShellParameters->StdIn)) || (Split == NULL)) { + gEfiShellProtocol->CloseFile (ShellParameters->StdIn); + } + + ShellParameters->StdIn = *OldStdIn; + } + + if (ShellParameters->StdOut != *OldStdOut) { + if (((Split != NULL) && (Split->SplitStdOut != ShellParameters->StdOut)) || (Split == NULL)) { + gEfiShellProtocol->CloseFile (ShellParameters->StdOut); + } + + ShellParameters->StdOut = *OldStdOut; + } + + if (ShellParameters->StdErr != *OldStdErr) { + gEfiShellProtocol->CloseFile (ShellParameters->StdErr); + ShellParameters->StdErr = *OldStdErr; + } + + if (gST->ConIn != SystemTableInfo->ConIn) { + CloseSimpleTextInOnFile (gST->ConIn); + gST->ConIn = SystemTableInfo->ConIn; + gST->ConsoleInHandle = SystemTableInfo->ConInHandle; + } + + if (gST->ConOut != SystemTableInfo->ConOut) { + CloseSimpleTextOutOnFile (gST->ConOut); + gST->ConOut = SystemTableInfo->ConOut; + gST->ConsoleOutHandle = SystemTableInfo->ConOutHandle; + } + + if (gST->StdErr != SystemTableInfo->ErrOut) { + CloseSimpleTextOutOnFile (gST->StdErr); + gST->StdErr = SystemTableInfo->ErrOut; + gST->StandardErrorHandle = SystemTableInfo->ErrOutHandle; + } + + CalculateEfiHdrCrc (&gST->Hdr); + + return (EFI_SUCCESS); +} + +/** + Function will replace the current Argc and Argv in the ShellParameters protocol + structure by parsing NewCommandLine. The current values are returned to the + user. + + If OldArgv or OldArgc is NULL then that value is not returned. + + @param[in, out] ShellParameters Pointer to parameter structure to modify. + @param[in] NewCommandLine The new command line to parse and use. + @param[in] Type The type of operation. + @param[out] OldArgv Pointer to old list of parameters. + @param[out] OldArgc Pointer to old number of items in Argv list. + + + @retval EFI_SUCCESS Operation was successful, Argv and Argc are valid. + @return EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +UpdateArgcArgv ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters, + IN CONST CHAR16 *NewCommandLine, + IN SHELL_OPERATION_TYPES Type, + OUT CHAR16 ***OldArgv OPTIONAL, + OUT UINTN *OldArgc OPTIONAL + ) +{ + BOOLEAN StripParamQuotation; + + ASSERT (ShellParameters != NULL); + StripParamQuotation = TRUE; + + if (OldArgc != NULL) { + *OldArgc = ShellParameters->Argc; + } + + if (OldArgc != NULL) { + *OldArgv = ShellParameters->Argv; + } + + if (Type == Script_File_Name) { + StripParamQuotation = FALSE; + } + + return ParseCommandLineToArgs ( + NewCommandLine, + StripParamQuotation, + &(ShellParameters->Argv), + &(ShellParameters->Argc) + ); +} + +/** + Function will replace the current Argc and Argv in the ShellParameters protocol + structure with Argv and Argc. The current values are de-allocated and the + OldArgv must not be deallocated by the caller. + + @param[in, out] ShellParameters pointer to parameter structure to modify + @param[in] OldArgv pointer to old list of parameters + @param[in] OldArgc pointer to old number of items in Argv list +**/ +VOID +RestoreArgcArgv ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters, + IN CHAR16 ***OldArgv, + IN UINTN *OldArgc + ) +{ + UINTN LoopCounter; + + ASSERT (ShellParameters != NULL); + ASSERT (OldArgv != NULL); + ASSERT (OldArgc != NULL); + + if (ShellParameters->Argv != NULL) { + for ( LoopCounter = 0 + ; LoopCounter < ShellParameters->Argc + ; LoopCounter++ + ) + { + FreePool (ShellParameters->Argv[LoopCounter]); + } + + FreePool (ShellParameters->Argv); + } + + ShellParameters->Argv = *OldArgv; + *OldArgv = NULL; + ShellParameters->Argc = *OldArgc; + *OldArgc = 0; +} diff --git a/ShellPkg/Application/Shell/ShellParametersProtocol.h b/ShellPkg/Application/Shell/ShellParametersProtocol.h index 2d12fa90b3..3408a91d37 100644 --- a/ShellPkg/Application/Shell/ShellParametersProtocol.h +++ b/ShellPkg/Application/Shell/ShellParametersProtocol.h @@ -1,210 +1,210 @@ -/** @file
- Member functions of EFI_SHELL_PARAMETERS_PROTOCOL and functions for creation,
- manipulation, and initialization of EFI_SHELL_PARAMETERS_PROTOCOL.
-
- Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#ifndef _SHELL_PARAMETERS_PROTOCOL_PROVIDER_HEADER_
-#define _SHELL_PARAMETERS_PROTOCOL_PROVIDER_HEADER_
-
-#include "Shell.h"
-
-typedef enum {
- Internal_Command,
- Script_File_Name,
- Efi_Application,
- File_Sys_Change,
- Unknown_Invalid
-} SHELL_OPERATION_TYPES;
-
-/**
- creates a new EFI_SHELL_PARAMETERS_PROTOCOL instance and populates it and then
- installs it on our handle and if there is an existing version of the protocol
- that one is cached for removal later.
-
- @param[in, out] NewShellParameters on a successful return, a pointer to pointer
- to the newly installed interface.
- @param[in, out] RootShellInstance on a successful return, pointer to boolean.
- TRUE if this is the root shell instance.
-
- @retval EFI_SUCCESS the operation completed successfully.
- @return other the operation failed.
- @sa ReinstallProtocolInterface
- @sa InstallProtocolInterface
- @sa ParseCommandLineToArgs
-**/
-EFI_STATUS
-CreatePopulateInstallShellParametersProtocol (
- IN OUT EFI_SHELL_PARAMETERS_PROTOCOL **NewShellParameters,
- IN OUT BOOLEAN *RootShellInstance
- );
-
-/**
- frees all memory used by creation and installation of shell parameters protocol
- and if there was an old version installed it will restore that one.
-
- @param NewShellParameters the interface of EFI_SHELL_PARAMETERS_PROTOCOL that is
- being cleaned up.
-
- @retval EFI_SUCCESS the cleanup was successful
- @return other the cleanup failed
- @sa ReinstallProtocolInterface
- @sa UninstallProtocolInterface
-**/
-EFI_STATUS
-CleanUpShellParametersProtocol (
- IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *NewShellParameters
- );
-
-/**
- Function will replace the current Argc and Argv in the ShellParameters protocol
- structure by parsing NewCommandLine. The current values are returned to the
- user.
-
- @param[in, out] ShellParameters pointer to parameter structure to modify
- @param[in] NewCommandLine the new command line to parse and use
- @param[in] Type the type of operation.
- @param[out] OldArgv pointer to old list of parameters
- @param[out] OldArgc pointer to old number of items in Argv list
-
- @retval EFI_SUCCESS operation was successful, Argv and Argc are valid
- @return EFI_INVALID_PARAMETER some parameters are invalid
- @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
-**/
-EFI_STATUS
-UpdateArgcArgv (
- IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
- IN CONST CHAR16 *NewCommandLine,
- IN SHELL_OPERATION_TYPES Type,
- OUT CHAR16 ***OldArgv,
- OUT UINTN *OldArgc
- );
-
-/**
- Function will replace the current Argc and Argv in the ShellParameters protocol
- structure with Argv and Argc. The current values are de-allocated and the
- OldArgv must not be deallocated by the caller.
-
- @param[in, out] ShellParameters pointer to parameter structure to modify
- @param[in] OldArgv pointer to old list of parameters
- @param[in] OldArgc pointer to old number of items in Argv list
-**/
-VOID
-RestoreArgcArgv (
- IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
- IN CHAR16 ***OldArgv,
- IN UINTN *OldArgc
- );
-
-typedef struct {
- EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn;
- EFI_HANDLE ConInHandle;
- EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
- EFI_HANDLE ConOutHandle;
- EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ErrOut;
- EFI_HANDLE ErrOutHandle;
-} SYSTEM_TABLE_INFO;
-
-/**
- Function will replace the current StdIn and StdOut in the ShellParameters protocol
- structure by parsing NewCommandLine. The current values are returned to the
- user.
-
- This will also update the system table.
-
- @param[in, out] ShellParameters Pointer to parameter structure to modify.
- @param[in] NewCommandLine The new command line to parse and use.
- @param[out] OldStdIn Pointer to old StdIn.
- @param[out] OldStdOut Pointer to old StdOut.
- @param[out] OldStdErr Pointer to old StdErr.
- @param[out] SystemTableInfo Pointer to old system table information.
-
- @retval EFI_SUCCESS Operation was successful, Argv and Argc are valid.
- @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
-**/
-EFI_STATUS
-UpdateStdInStdOutStdErr (
- IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
- IN CHAR16 *NewCommandLine,
- OUT SHELL_FILE_HANDLE *OldStdIn,
- OUT SHELL_FILE_HANDLE *OldStdOut,
- OUT SHELL_FILE_HANDLE *OldStdErr,
- OUT SYSTEM_TABLE_INFO *SystemTableInfo
- );
-
-/**
- Function will replace the current StdIn and StdOut in the ShellParameters protocol
- structure with StdIn and StdOut. The current values are de-allocated.
-
- @param[in, out] ShellParameters Pointer to parameter structure to modify.
- @param[in] OldStdIn Pointer to old StdIn.
- @param[in] OldStdOut Pointer to old StdOut.
- @param[in] OldStdErr Pointer to old StdErr.
- @param[in] SystemTableInfo Pointer to old system table information.
-**/
-EFI_STATUS
-RestoreStdInStdOutStdErr (
- IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
- IN SHELL_FILE_HANDLE *OldStdIn,
- IN SHELL_FILE_HANDLE *OldStdOut,
- IN SHELL_FILE_HANDLE *OldStdErr,
- IN SYSTEM_TABLE_INFO *SystemTableInfo
- );
-
-/**
- function to populate Argc and Argv.
-
- This function parses the CommandLine and divides it into standard C style Argc/Argv
- parameters for inclusion in EFI_SHELL_PARAMETERS_PROTOCOL. this supports space
- delimited and quote surrounded parameter definition.
-
- @param[in] CommandLine String of command line to parse
- @param[in] StripQuotation if TRUE then strip the quotation marks surrounding
- the parameters.
- @param[in, out] Argv pointer to array of strings; one for each parameter
- @param[in, out] Argc pointer to number of strings in Argv array
-
- @return EFI_SUCCESS the operation was successful
- @return EFI_INVALID_PARAMETER some parameters are invalid
- @return EFI_OUT_OF_RESOURCES a memory allocation failed.
-**/
-EFI_STATUS
-ParseCommandLineToArgs (
- IN CONST CHAR16 *CommandLine,
- IN BOOLEAN StripQuotation,
- IN OUT CHAR16 ***Argv,
- IN OUT UINTN *Argc
- );
-
-/**
- return the next parameter from a command line string;
-
- This function moves the next parameter from Walker into TempParameter and moves
- Walker up past that parameter for recursive calling. When the final parameter
- is moved *Walker will be set to NULL;
-
- Temp Parameter must be large enough to hold the parameter before calling this
- function.
-
- @param[in, out] Walker pointer to string of command line. Adjusted to
- remaining command line on return
- @param[in, out] TempParameter pointer to string of command line item extracted.
- @param[in] Length Length of (*TempParameter) in bytes
- @param[in] StripQuotation if TRUE then strip the quotation marks surrounding
- the parameters.
-
- @return EFI_INVALID_PARAMETER A required parameter was NULL or pointed to a NULL or empty string.
- @return EFI_NOT_FOUND A closing " could not be found on the specified string
-**/
-EFI_STATUS
-GetNextParameter (
- IN OUT CHAR16 **Walker,
- IN OUT CHAR16 **TempParameter,
- IN CONST UINTN Length,
- IN BOOLEAN StripQuotation
- );
-
-#endif //_SHELL_PARAMETERS_PROTOCOL_PROVIDER_HEADER_
+/** @file + Member functions of EFI_SHELL_PARAMETERS_PROTOCOL and functions for creation, + manipulation, and initialization of EFI_SHELL_PARAMETERS_PROTOCOL. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SHELL_PARAMETERS_PROTOCOL_PROVIDER_HEADER_ +#define _SHELL_PARAMETERS_PROTOCOL_PROVIDER_HEADER_ + +#include "Shell.h" + +typedef enum { + Internal_Command, + Script_File_Name, + Efi_Application, + File_Sys_Change, + Unknown_Invalid +} SHELL_OPERATION_TYPES; + +/** + creates a new EFI_SHELL_PARAMETERS_PROTOCOL instance and populates it and then + installs it on our handle and if there is an existing version of the protocol + that one is cached for removal later. + + @param[in, out] NewShellParameters on a successful return, a pointer to pointer + to the newly installed interface. + @param[in, out] RootShellInstance on a successful return, pointer to boolean. + TRUE if this is the root shell instance. + + @retval EFI_SUCCESS the operation completed successfully. + @return other the operation failed. + @sa ReinstallProtocolInterface + @sa InstallProtocolInterface + @sa ParseCommandLineToArgs +**/ +EFI_STATUS +CreatePopulateInstallShellParametersProtocol ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL **NewShellParameters, + IN OUT BOOLEAN *RootShellInstance + ); + +/** + frees all memory used by creation and installation of shell parameters protocol + and if there was an old version installed it will restore that one. + + @param NewShellParameters the interface of EFI_SHELL_PARAMETERS_PROTOCOL that is + being cleaned up. + + @retval EFI_SUCCESS the cleanup was successful + @return other the cleanup failed + @sa ReinstallProtocolInterface + @sa UninstallProtocolInterface +**/ +EFI_STATUS +CleanUpShellParametersProtocol ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *NewShellParameters + ); + +/** + Function will replace the current Argc and Argv in the ShellParameters protocol + structure by parsing NewCommandLine. The current values are returned to the + user. + + @param[in, out] ShellParameters pointer to parameter structure to modify + @param[in] NewCommandLine the new command line to parse and use + @param[in] Type the type of operation. + @param[out] OldArgv pointer to old list of parameters + @param[out] OldArgc pointer to old number of items in Argv list + + @retval EFI_SUCCESS operation was successful, Argv and Argc are valid + @return EFI_INVALID_PARAMETER some parameters are invalid + @retval EFI_OUT_OF_RESOURCES a memory allocation failed. +**/ +EFI_STATUS +UpdateArgcArgv ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters, + IN CONST CHAR16 *NewCommandLine, + IN SHELL_OPERATION_TYPES Type, + OUT CHAR16 ***OldArgv, + OUT UINTN *OldArgc + ); + +/** + Function will replace the current Argc and Argv in the ShellParameters protocol + structure with Argv and Argc. The current values are de-allocated and the + OldArgv must not be deallocated by the caller. + + @param[in, out] ShellParameters pointer to parameter structure to modify + @param[in] OldArgv pointer to old list of parameters + @param[in] OldArgc pointer to old number of items in Argv list +**/ +VOID +RestoreArgcArgv ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters, + IN CHAR16 ***OldArgv, + IN UINTN *OldArgc + ); + +typedef struct { + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; + EFI_HANDLE ConInHandle; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; + EFI_HANDLE ConOutHandle; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ErrOut; + EFI_HANDLE ErrOutHandle; +} SYSTEM_TABLE_INFO; + +/** + Function will replace the current StdIn and StdOut in the ShellParameters protocol + structure by parsing NewCommandLine. The current values are returned to the + user. + + This will also update the system table. + + @param[in, out] ShellParameters Pointer to parameter structure to modify. + @param[in] NewCommandLine The new command line to parse and use. + @param[out] OldStdIn Pointer to old StdIn. + @param[out] OldStdOut Pointer to old StdOut. + @param[out] OldStdErr Pointer to old StdErr. + @param[out] SystemTableInfo Pointer to old system table information. + + @retval EFI_SUCCESS Operation was successful, Argv and Argc are valid. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +UpdateStdInStdOutStdErr ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters, + IN CHAR16 *NewCommandLine, + OUT SHELL_FILE_HANDLE *OldStdIn, + OUT SHELL_FILE_HANDLE *OldStdOut, + OUT SHELL_FILE_HANDLE *OldStdErr, + OUT SYSTEM_TABLE_INFO *SystemTableInfo + ); + +/** + Function will replace the current StdIn and StdOut in the ShellParameters protocol + structure with StdIn and StdOut. The current values are de-allocated. + + @param[in, out] ShellParameters Pointer to parameter structure to modify. + @param[in] OldStdIn Pointer to old StdIn. + @param[in] OldStdOut Pointer to old StdOut. + @param[in] OldStdErr Pointer to old StdErr. + @param[in] SystemTableInfo Pointer to old system table information. +**/ +EFI_STATUS +RestoreStdInStdOutStdErr ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters, + IN SHELL_FILE_HANDLE *OldStdIn, + IN SHELL_FILE_HANDLE *OldStdOut, + IN SHELL_FILE_HANDLE *OldStdErr, + IN SYSTEM_TABLE_INFO *SystemTableInfo + ); + +/** + function to populate Argc and Argv. + + This function parses the CommandLine and divides it into standard C style Argc/Argv + parameters for inclusion in EFI_SHELL_PARAMETERS_PROTOCOL. this supports space + delimited and quote surrounded parameter definition. + + @param[in] CommandLine String of command line to parse + @param[in] StripQuotation if TRUE then strip the quotation marks surrounding + the parameters. + @param[in, out] Argv pointer to array of strings; one for each parameter + @param[in, out] Argc pointer to number of strings in Argv array + + @return EFI_SUCCESS the operation was successful + @return EFI_INVALID_PARAMETER some parameters are invalid + @return EFI_OUT_OF_RESOURCES a memory allocation failed. +**/ +EFI_STATUS +ParseCommandLineToArgs ( + IN CONST CHAR16 *CommandLine, + IN BOOLEAN StripQuotation, + IN OUT CHAR16 ***Argv, + IN OUT UINTN *Argc + ); + +/** + return the next parameter from a command line string; + + This function moves the next parameter from Walker into TempParameter and moves + Walker up past that parameter for recursive calling. When the final parameter + is moved *Walker will be set to NULL; + + Temp Parameter must be large enough to hold the parameter before calling this + function. + + @param[in, out] Walker pointer to string of command line. Adjusted to + remaining command line on return + @param[in, out] TempParameter pointer to string of command line item extracted. + @param[in] Length Length of (*TempParameter) in bytes + @param[in] StripQuotation if TRUE then strip the quotation marks surrounding + the parameters. + + @return EFI_INVALID_PARAMETER A required parameter was NULL or pointed to a NULL or empty string. + @return EFI_NOT_FOUND A closing " could not be found on the specified string +**/ +EFI_STATUS +GetNextParameter ( + IN OUT CHAR16 **Walker, + IN OUT CHAR16 **TempParameter, + IN CONST UINTN Length, + IN BOOLEAN StripQuotation + ); + +#endif //_SHELL_PARAMETERS_PROTOCOL_PROVIDER_HEADER_ diff --git a/ShellPkg/Application/Shell/ShellProtocol.c b/ShellPkg/Application/Shell/ShellProtocol.c index da8c31cb03..031a876a3e 100644 --- a/ShellPkg/Application/Shell/ShellProtocol.c +++ b/ShellPkg/Application/Shell/ShellProtocol.c @@ -1,4049 +1,4049 @@ -/** @file
- Member functions of EFI_SHELL_PROTOCOL and functions for creation,
- manipulation, and initialization of EFI_SHELL_PROTOCOL.
-
- (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
- (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
- Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "Shell.h"
-
-#define INIT_NAME_BUFFER_SIZE 128
-
-/**
- Close an open file handle.
-
- This function closes a specified file handle. All "dirty" cached file data is
- flushed to the device, and the file is closed. In all cases the handle is
- closed.
-
- @param[in] FileHandle The file handle to close.
-
- @retval EFI_SUCCESS The file handle was closed successfully.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellClose (
- IN SHELL_FILE_HANDLE FileHandle
- )
-{
- ShellFileHandleRemove (FileHandle);
- return (FileHandleClose (ConvertShellHandleToEfiFileProtocol (FileHandle)));
-}
-
-/**
- Internal worker to determine whether there is a BlockIo somewhere
- upon the device path specified.
-
- @param[in] DevicePath The device path to test.
-
- @retval TRUE gEfiBlockIoProtocolGuid was installed on a handle with this device path
- @retval FALSE gEfiBlockIoProtocolGuid was not found.
-**/
-BOOLEAN
-InternalShellProtocolIsBlockIoPresent (
- IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
- )
-{
- EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
- EFI_STATUS Status;
- EFI_HANDLE Handle;
-
- Handle = NULL;
-
- DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL *)DevicePath;
- Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &DevicePathCopy, &Handle);
-
- if ((Handle != NULL) && (!EFI_ERROR (Status))) {
- return (TRUE);
- }
-
- return (FALSE);
-}
-
-/**
- Internal worker to determine whether there is a file system somewhere
- upon the device path specified.
-
- @param[in] DevicePath The device path to test.
-
- @retval TRUE gEfiSimpleFileSystemProtocolGuid was installed on a handle with this device path
- @retval FALSE gEfiSimpleFileSystemProtocolGuid was not found.
-**/
-BOOLEAN
-InternalShellProtocolIsSimpleFileSystemPresent (
- IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
- )
-{
- EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
- EFI_STATUS Status;
- EFI_HANDLE Handle;
-
- Handle = NULL;
-
- DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL *)DevicePath;
- Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);
-
- if ((Handle != NULL) && (!EFI_ERROR (Status))) {
- return (TRUE);
- }
-
- return (FALSE);
-}
-
-/**
- This function creates a mapping for a device path.
-
- If both DevicePath and Mapping are NULL, this will reset the mapping to default values.
-
- @param DevicePath Points to the device path. If this is NULL and Mapping points to a valid mapping,
- then the mapping will be deleted.
- @param Mapping Points to the NULL-terminated mapping for the device path. Must end with a ':'
-
- @retval EFI_SUCCESS Mapping created or deleted successfully.
- @retval EFI_NO_MAPPING There is no handle that corresponds exactly to DevicePath. See the
- boot service function LocateDevicePath().
- @retval EFI_ACCESS_DENIED The mapping is a built-in alias.
- @retval EFI_INVALID_PARAMETER Mapping was NULL
- @retval EFI_INVALID_PARAMETER Mapping did not end with a ':'
- @retval EFI_INVALID_PARAMETER DevicePath was not pointing at a device that had a SIMPLE_FILE_SYSTEM_PROTOCOL installed.
- @retval EFI_NOT_FOUND There was no mapping found to delete
- @retval EFI_OUT_OF_RESOURCES Memory allocation failed
-**/
-EFI_STATUS
-EFIAPI
-EfiShellSetMap (
- IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL,
- IN CONST CHAR16 *Mapping
- )
-{
- EFI_STATUS Status;
- SHELL_MAP_LIST *MapListNode;
-
- if (Mapping == NULL) {
- return (EFI_INVALID_PARAMETER);
- }
-
- if (Mapping[StrLen (Mapping)-1] != ':') {
- return (EFI_INVALID_PARAMETER);
- }
-
- //
- // Delete the mapping
- //
- if (DevicePath == NULL) {
- if (IsListEmpty (&gShellMapList.Link)) {
- return (EFI_NOT_FOUND);
- }
-
- for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link)
- ; !IsNull (&gShellMapList.Link, &MapListNode->Link)
- ; MapListNode = (SHELL_MAP_LIST *)GetNextNode (&gShellMapList.Link, &MapListNode->Link)
- )
- {
- if (StringNoCaseCompare (&MapListNode->MapName, &Mapping) == 0) {
- RemoveEntryList (&MapListNode->Link);
- SHELL_FREE_NON_NULL (MapListNode->DevicePath);
- SHELL_FREE_NON_NULL (MapListNode->MapName);
- SHELL_FREE_NON_NULL (MapListNode->CurrentDirectoryPath);
- FreePool (MapListNode);
- return (EFI_SUCCESS);
- }
- } // for loop
-
- //
- // We didn't find one to delete
- //
- return (EFI_NOT_FOUND);
- }
-
- //
- // make sure this is a valid to add device path
- //
- /// @todo add BlockIo to this test...
- if ( !InternalShellProtocolIsSimpleFileSystemPresent (DevicePath)
- && !InternalShellProtocolIsBlockIoPresent (DevicePath))
- {
- return (EFI_INVALID_PARAMETER);
- }
-
- //
- // First make sure there is no old mapping
- //
- Status = EfiShellSetMap (NULL, Mapping);
- if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_FOUND)) {
- return (Status);
- }
-
- //
- // now add the new one.
- //
- Status = ShellCommandAddMapItemAndUpdatePath (Mapping, DevicePath, 0, FALSE);
-
- return (Status);
-}
-
-/**
- Gets the device path from the mapping.
-
- This function gets the device path associated with a mapping.
-
- @param Mapping A pointer to the mapping
-
- @retval !=NULL Pointer to the device path that corresponds to the
- device mapping. The returned pointer does not need
- to be freed.
- @retval NULL There is no device path associated with the
- specified mapping.
-**/
-CONST EFI_DEVICE_PATH_PROTOCOL *
-EFIAPI
-EfiShellGetDevicePathFromMap (
- IN CONST CHAR16 *Mapping
- )
-{
- SHELL_MAP_LIST *MapListItem;
- CHAR16 *NewName;
- UINTN Size;
-
- NewName = NULL;
- Size = 0;
-
- StrnCatGrow (&NewName, &Size, Mapping, 0);
- if (Mapping[StrLen (Mapping)-1] != L':') {
- StrnCatGrow (&NewName, &Size, L":", 0);
- }
-
- MapListItem = ShellCommandFindMapItem (NewName);
-
- FreePool (NewName);
-
- if (MapListItem != NULL) {
- return (MapListItem->DevicePath);
- }
-
- return (NULL);
-}
-
-/**
- Gets the mapping(s) that most closely matches the device path.
-
- This function gets the mapping which corresponds to the device path *DevicePath. If
- there is no exact match, then the mapping which most closely matches *DevicePath
- is returned, and *DevicePath is updated to point to the remaining portion of the
- device path. If there is an exact match, the mapping is returned and *DevicePath
- points to the end-of-device-path node.
-
- If there are multiple map names they will be semi-colon separated in the
- NULL-terminated string.
-
- @param DevicePath On entry, points to a device path pointer. On
- exit, updates the pointer to point to the
- portion of the device path after the mapping.
-
- @retval NULL No mapping was found.
- @return !=NULL Pointer to NULL-terminated mapping. The buffer
- is callee allocated and should be freed by the caller.
-**/
-CONST CHAR16 *
-EFIAPI
-EfiShellGetMapFromDevicePath (
- IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
- )
-{
- SHELL_MAP_LIST *Node;
- CHAR16 *PathForReturn;
- UINTN PathSize;
-
- // EFI_HANDLE PathHandle;
- // EFI_HANDLE MapHandle;
- // EFI_STATUS Status;
- // EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
- // EFI_DEVICE_PATH_PROTOCOL *MapPathCopy;
-
- if ((DevicePath == NULL) || (*DevicePath == NULL)) {
- return (NULL);
- }
-
- PathForReturn = NULL;
- PathSize = 0;
-
- for ( Node = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link)
- ; !IsNull (&gShellMapList.Link, &Node->Link)
- ; Node = (SHELL_MAP_LIST *)GetNextNode (&gShellMapList.Link, &Node->Link)
- )
- {
- //
- // check for exact match
- //
- if (DevicePathCompare (DevicePath, &Node->DevicePath) == 0) {
- ASSERT ((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
- if (PathSize != 0) {
- PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, L";", 0);
- }
-
- PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, Node->MapName, 0);
- }
- }
-
- if (PathForReturn != NULL) {
- while (!IsDevicePathEndType (*DevicePath)) {
- *DevicePath = NextDevicePathNode (*DevicePath);
- }
-
- //
- // Do not call SetDevicePathEndNode() if the device path node is already the
- // end of an entire device path.
- //
- if (!IsDevicePathEnd (*DevicePath)) {
- SetDevicePathEndNode (*DevicePath);
- }
- }
-
- /*
- ///@todo finish code for inexact matches.
- if (PathForReturn == NULL) {
- PathSize = 0;
-
- DevicePathCopy = DuplicateDevicePath(*DevicePath);
- ASSERT(DevicePathCopy != NULL);
- Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
- ASSERT_EFI_ERROR(Status);
- //
- // check each of the device paths we have to get the root of the path for consist mappings
- //
- for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
- ; !IsNull(&gShellMapList.Link, &Node->Link)
- ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
- ){
- if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) == 0) {
- continue;
- }
- MapPathCopy = DuplicateDevicePath(Node->DevicePath);
- ASSERT(MapPathCopy != NULL);
- Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
- if (MapHandle == PathHandle) {
-
- *DevicePath = DevicePathCopy;
-
- MapPathCopy = NULL;
- DevicePathCopy = NULL;
- PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
- PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
- break;
- }
- }
- //
- // now add on the non-consistent mappings
- //
- for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
- ; !IsNull(&gShellMapList.Link, &Node->Link)
- ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
- ){
- if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) != 0) {
- continue;
- }
- MapPathCopy = Node->DevicePath;
- ASSERT(MapPathCopy != NULL);
- Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
- if (MapHandle == PathHandle) {
- PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
- PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
- break;
- }
- }
- }
- */
-
- return (AddBufferToFreeList (PathForReturn));
-}
-
-/**
- Converts a device path to a file system-style path.
-
- This function converts a device path to a file system path by replacing part, or all, of
- the device path with the file-system mapping. If there are more than one application
- file system mappings, the one that most closely matches Path will be used.
-
- @param Path The pointer to the device path
-
- @retval NULL the device path could not be found.
- @return all The pointer of the NULL-terminated file path. The path
- is callee-allocated and should be freed by the caller.
-**/
-CHAR16 *
-EFIAPI
-EfiShellGetFilePathFromDevicePath (
- IN CONST EFI_DEVICE_PATH_PROTOCOL *Path
- )
-{
- EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
- EFI_DEVICE_PATH_PROTOCOL *MapPathCopy;
- SHELL_MAP_LIST *MapListItem;
- CHAR16 *PathForReturn;
- UINTN PathSize;
- EFI_HANDLE PathHandle;
- EFI_HANDLE MapHandle;
- EFI_STATUS Status;
- FILEPATH_DEVICE_PATH *FilePath;
- FILEPATH_DEVICE_PATH *AlignedNode;
-
- PathForReturn = NULL;
- PathSize = 0;
-
- DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL *)Path;
- ASSERT (DevicePathCopy != NULL);
- if (DevicePathCopy == NULL) {
- return (NULL);
- }
-
- /// @todo BlockIo?
- Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
-
- if (EFI_ERROR (Status)) {
- return (NULL);
- }
-
- //
- // check each of the device paths we have to get the root of the path
- //
- for ( MapListItem = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link)
- ; !IsNull (&gShellMapList.Link, &MapListItem->Link)
- ; MapListItem = (SHELL_MAP_LIST *)GetNextNode (&gShellMapList.Link, &MapListItem->Link)
- )
- {
- MapPathCopy = (EFI_DEVICE_PATH_PROTOCOL *)MapListItem->DevicePath;
- ASSERT (MapPathCopy != NULL);
- /// @todo BlockIo?
- Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
- if (MapHandle == PathHandle) {
- ASSERT ((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
- PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, MapListItem->MapName, 0);
- //
- // go through all the remaining nodes in the device path
- //
- for ( FilePath = (FILEPATH_DEVICE_PATH *)DevicePathCopy
- ; !IsDevicePathEnd (&FilePath->Header)
- ; FilePath = (FILEPATH_DEVICE_PATH *)NextDevicePathNode (&FilePath->Header)
- )
- {
- //
- // If any node is not a file path node, then the conversion can not be completed
- //
- if ((DevicePathType (&FilePath->Header) != MEDIA_DEVICE_PATH) ||
- (DevicePathSubType (&FilePath->Header) != MEDIA_FILEPATH_DP))
- {
- FreePool (PathForReturn);
- return NULL;
- }
-
- //
- // append the path part onto the filepath.
- //
- ASSERT ((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
-
- AlignedNode = AllocateCopyPool (DevicePathNodeLength (FilePath), FilePath);
- if (AlignedNode == NULL) {
- FreePool (PathForReturn);
- return NULL;
- }
-
- // File Path Device Path Nodes 'can optionally add a "\" separator to
- // the beginning and/or the end of the Path Name string.'
- // (UEFI Spec 2.4 section 9.3.6.4).
- // If necessary, add a "\", but otherwise don't
- // (This is specified in the above section, and also implied by the
- // UEFI Shell spec section 3.7)
- if ((PathSize != 0) &&
- (PathForReturn != NULL) &&
- (PathForReturn[PathSize / sizeof (CHAR16) - 1] != L'\\') &&
- (AlignedNode->PathName[0] != L'\\'))
- {
- PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, L"\\", 1);
- }
-
- PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, AlignedNode->PathName, 0);
- FreePool (AlignedNode);
- } // for loop of remaining nodes
- }
-
- if (PathForReturn != NULL) {
- break;
- }
- } // for loop of paths to check
-
- return (PathForReturn);
-}
-
-/**
- Converts a file system style name to a device path.
-
- This function converts a file system style name to a device path, by replacing any
- mapping references to the associated device path.
-
- @param[in] Path The pointer to the path.
-
- @return The pointer of the file path. The file path is callee
- allocated and should be freed by the caller.
- @retval NULL The path could not be found.
- @retval NULL There was not enough available memory.
-**/
-EFI_DEVICE_PATH_PROTOCOL *
-EFIAPI
-EfiShellGetDevicePathFromFilePath (
- IN CONST CHAR16 *Path
- )
-{
- CHAR16 *MapName;
- CHAR16 *NewPath;
- CONST CHAR16 *Cwd;
- UINTN Size;
- CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath;
- EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
- EFI_DEVICE_PATH_PROTOCOL *DevicePathCopyForFree;
- EFI_DEVICE_PATH_PROTOCOL *DevicePathForReturn;
- EFI_HANDLE Handle;
- EFI_STATUS Status;
-
- if (Path == NULL) {
- return (NULL);
- }
-
- MapName = NULL;
- NewPath = NULL;
-
- if (StrStr (Path, L":") == NULL) {
- Cwd = EfiShellGetCurDir (NULL);
- if (Cwd == NULL) {
- return (NULL);
- }
-
- Size = StrSize (Cwd) + StrSize (Path);
- NewPath = AllocateZeroPool (Size);
- if (NewPath == NULL) {
- return (NULL);
- }
-
- StrCpyS (NewPath, Size/sizeof (CHAR16), Cwd);
- StrCatS (NewPath, Size/sizeof (CHAR16), L"\\");
- if (*Path == L'\\') {
- Path++;
- while (PathRemoveLastItem (NewPath)) {
- }
- }
-
- StrCatS (NewPath, Size/sizeof (CHAR16), Path);
- DevicePathForReturn = EfiShellGetDevicePathFromFilePath (NewPath);
- FreePool (NewPath);
- return (DevicePathForReturn);
- }
-
- Size = 0;
- //
- // find the part before (but including) the : for the map name
- //
- ASSERT ((MapName == NULL && Size == 0) || (MapName != NULL));
- MapName = StrnCatGrow (&MapName, &Size, Path, (StrStr (Path, L":")-Path+1));
- if ((MapName == NULL) || (MapName[StrLen (MapName)-1] != L':')) {
- return (NULL);
- }
-
- //
- // look up the device path in the map
- //
- DevicePath = EfiShellGetDevicePathFromMap (MapName);
- if (DevicePath == NULL) {
- //
- // Must have been a bad Mapname
- //
- return (NULL);
- }
-
- //
- // make a copy for LocateDevicePath to modify (also save a pointer to call FreePool with)
- //
- DevicePathCopyForFree = DevicePathCopy = DuplicateDevicePath (DevicePath);
- if (DevicePathCopy == NULL) {
- FreePool (MapName);
- return (NULL);
- }
-
- //
- // get the handle
- //
- /// @todo BlockIo?
- Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);
- if (EFI_ERROR (Status)) {
- if (DevicePathCopyForFree != NULL) {
- FreePool (DevicePathCopyForFree);
- }
-
- FreePool (MapName);
- return (NULL);
- }
-
- //
- // build the full device path
- //
- if ((*(Path+StrLen (MapName)) != CHAR_NULL) &&
- (*(Path+StrLen (MapName)+1) == CHAR_NULL))
- {
- DevicePathForReturn = FileDevicePath (Handle, L"\\");
- } else {
- DevicePathForReturn = FileDevicePath (Handle, Path+StrLen (MapName));
- }
-
- FreePool (MapName);
- if (DevicePathCopyForFree != NULL) {
- FreePool (DevicePathCopyForFree);
- }
-
- return (DevicePathForReturn);
-}
-
-/**
- Gets the name of the device specified by the device handle.
-
- This function gets the user-readable name of the device specified by the device
- handle. If no user-readable name could be generated, then *BestDeviceName will be
- NULL and EFI_NOT_FOUND will be returned.
-
- If EFI_DEVICE_NAME_USE_COMPONENT_NAME is set, then the function will return the
- device's name using the EFI_COMPONENT_NAME2_PROTOCOL, if present on
- DeviceHandle.
-
- If EFI_DEVICE_NAME_USE_DEVICE_PATH is set, then the function will return the
- device's name using the EFI_DEVICE_PATH_PROTOCOL, if present on DeviceHandle.
- If both EFI_DEVICE_NAME_USE_COMPONENT_NAME and
- EFI_DEVICE_NAME_USE_DEVICE_PATH are set, then
- EFI_DEVICE_NAME_USE_COMPONENT_NAME will have higher priority.
-
- @param DeviceHandle The handle of the device.
- @param Flags Determines the possible sources of component names.
- Valid bits are:
- EFI_DEVICE_NAME_USE_COMPONENT_NAME
- EFI_DEVICE_NAME_USE_DEVICE_PATH
- @param Language A pointer to the language specified for the device
- name, in the same format as described in the UEFI
- specification, Appendix M
- @param BestDeviceName On return, points to the callee-allocated NULL-
- terminated name of the device. If no device name
- could be found, points to NULL. The name must be
- freed by the caller...
-
- @retval EFI_SUCCESS Get the name successfully.
- @retval EFI_NOT_FOUND Fail to get the device name.
- @retval EFI_INVALID_PARAMETER Flags did not have a valid bit set.
- @retval EFI_INVALID_PARAMETER BestDeviceName was NULL
- @retval EFI_INVALID_PARAMETER DeviceHandle was NULL
-**/
-EFI_STATUS
-EFIAPI
-EfiShellGetDeviceName (
- IN EFI_HANDLE DeviceHandle,
- IN EFI_SHELL_DEVICE_NAME_FLAGS Flags,
- IN CHAR8 *Language,
- OUT CHAR16 **BestDeviceName
- )
-{
- EFI_STATUS Status;
- EFI_COMPONENT_NAME2_PROTOCOL *CompName2;
- EFI_DEVICE_PATH_PROTOCOL *DevicePath;
- EFI_HANDLE *HandleList;
- UINTN HandleCount;
- UINTN LoopVar;
- CHAR16 *DeviceNameToReturn;
- CHAR8 *Lang;
- UINTN ParentControllerCount;
- EFI_HANDLE *ParentControllerBuffer;
- UINTN ParentDriverCount;
- EFI_HANDLE *ParentDriverBuffer;
-
- if ((BestDeviceName == NULL) ||
- (DeviceHandle == NULL)
- )
- {
- return (EFI_INVALID_PARAMETER);
- }
-
- //
- // make sure one of the 2 supported bits is on
- //
- if (((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) == 0) &&
- ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) == 0))
- {
- return (EFI_INVALID_PARAMETER);
- }
-
- DeviceNameToReturn = NULL;
- *BestDeviceName = NULL;
- HandleList = NULL;
- HandleCount = 0;
- Lang = NULL;
-
- if ((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) != 0) {
- Status = ParseHandleDatabaseByRelationship (
- NULL,
- DeviceHandle,
- HR_DRIVER_BINDING_HANDLE|HR_DEVICE_DRIVER,
- &HandleCount,
- &HandleList
- );
- for (LoopVar = 0; LoopVar < HandleCount; LoopVar++) {
- //
- // Go through those handles until we get one that passes for GetComponentName
- //
- Status = gBS->OpenProtocol (
- HandleList[LoopVar],
- &gEfiComponentName2ProtocolGuid,
- (VOID **)&CompName2,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- if (EFI_ERROR (Status)) {
- Status = gBS->OpenProtocol (
- HandleList[LoopVar],
- &gEfiComponentNameProtocolGuid,
- (VOID **)&CompName2,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- }
-
- if (EFI_ERROR (Status)) {
- continue;
- }
-
- Lang = GetBestLanguageForDriver (CompName2->SupportedLanguages, Language, FALSE);
- Status = CompName2->GetControllerName (CompName2, DeviceHandle, NULL, Lang, &DeviceNameToReturn);
- FreePool (Lang);
- Lang = NULL;
- if (!EFI_ERROR (Status) && (DeviceNameToReturn != NULL)) {
- break;
- }
- }
-
- if (HandleList != NULL) {
- FreePool (HandleList);
- }
-
- //
- // Now check the parent controller using this as the child.
- //
- Status = PARSE_HANDLE_DATABASE_PARENTS (DeviceHandle, &ParentControllerCount, &ParentControllerBuffer);
- if ((DeviceNameToReturn == NULL) && !EFI_ERROR (Status)) {
- for (LoopVar = 0; LoopVar < ParentControllerCount; LoopVar++) {
- Status = PARSE_HANDLE_DATABASE_UEFI_DRIVERS (ParentControllerBuffer[LoopVar], &ParentDriverCount, &ParentDriverBuffer);
- if (!EFI_ERROR (Status)) {
- for (HandleCount = 0; HandleCount < ParentDriverCount; HandleCount++) {
- //
- // try using that driver's component name with controller and our driver as the child.
- //
- Status = gBS->OpenProtocol (
- ParentDriverBuffer[HandleCount],
- &gEfiComponentName2ProtocolGuid,
- (VOID **)&CompName2,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- if (EFI_ERROR (Status)) {
- Status = gBS->OpenProtocol (
- ParentDriverBuffer[HandleCount],
- &gEfiComponentNameProtocolGuid,
- (VOID **)&CompName2,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- }
-
- if (EFI_ERROR (Status)) {
- continue;
- }
-
- Lang = GetBestLanguageForDriver (CompName2->SupportedLanguages, Language, FALSE);
- Status = CompName2->GetControllerName (CompName2, ParentControllerBuffer[LoopVar], DeviceHandle, Lang, &DeviceNameToReturn);
- FreePool (Lang);
- Lang = NULL;
- if (!EFI_ERROR (Status) && (DeviceNameToReturn != NULL)) {
- break;
- }
- }
-
- SHELL_FREE_NON_NULL (ParentDriverBuffer);
- if (!EFI_ERROR (Status) && (DeviceNameToReturn != NULL)) {
- break;
- }
- }
- }
-
- SHELL_FREE_NON_NULL (ParentControllerBuffer);
- }
-
- //
- // dont return on fail since we will try device path if that bit is on
- //
- if (DeviceNameToReturn != NULL) {
- ASSERT (BestDeviceName != NULL);
- StrnCatGrow (BestDeviceName, NULL, DeviceNameToReturn, 0);
- return (EFI_SUCCESS);
- }
- }
-
- if ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) != 0) {
- Status = gBS->OpenProtocol (
- DeviceHandle,
- &gEfiDevicePathProtocolGuid,
- (VOID **)&DevicePath,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- if (!EFI_ERROR (Status)) {
- //
- // use device path to text on the device path
- //
- *BestDeviceName = ConvertDevicePathToText (DevicePath, TRUE, TRUE);
- return (EFI_SUCCESS);
- }
- }
-
- //
- // none of the selected bits worked.
- //
- return (EFI_NOT_FOUND);
-}
-
-/**
- Opens the root directory of a device on a handle
-
- This function opens the root directory of a device and returns a file handle to it.
-
- @param DeviceHandle The handle of the device that contains the volume.
- @param FileHandle On exit, points to the file handle corresponding to the root directory on the
- device.
-
- @retval EFI_SUCCESS Root opened successfully.
- @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
- could not be opened.
- @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted.
- @retval EFI_DEVICE_ERROR The device had an error.
- @retval Others Error status returned from EFI_SIMPLE_FILE_SYSTEM_PROTOCOL->OpenVolume().
-**/
-EFI_STATUS
-EFIAPI
-EfiShellOpenRootByHandle (
- IN EFI_HANDLE DeviceHandle,
- OUT SHELL_FILE_HANDLE *FileHandle
- )
-{
- EFI_STATUS Status;
- EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
- EFI_FILE_PROTOCOL *RealFileHandle;
- EFI_DEVICE_PATH_PROTOCOL *DevPath;
-
- //
- // get the simple file system interface
- //
- Status = gBS->OpenProtocol (
- DeviceHandle,
- &gEfiSimpleFileSystemProtocolGuid,
- (VOID **)&SimpleFileSystem,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- if (EFI_ERROR (Status)) {
- return (EFI_NOT_FOUND);
- }
-
- Status = gBS->OpenProtocol (
- DeviceHandle,
- &gEfiDevicePathProtocolGuid,
- (VOID **)&DevPath,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- if (EFI_ERROR (Status)) {
- return (EFI_NOT_FOUND);
- }
-
- //
- // Open the root volume now...
- //
- Status = SimpleFileSystem->OpenVolume (SimpleFileSystem, &RealFileHandle);
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- *FileHandle = ConvertEfiFileProtocolToShellHandle (RealFileHandle, EfiShellGetMapFromDevicePath (&DevPath));
- return (EFI_SUCCESS);
-}
-
-/**
- Opens the root directory of a device.
-
- This function opens the root directory of a device and returns a file handle to it.
-
- @param DevicePath Points to the device path corresponding to the device where the
- EFI_SIMPLE_FILE_SYSTEM_PROTOCOL is installed.
- @param FileHandle On exit, points to the file handle corresponding to the root directory on the
- device.
-
- @retval EFI_SUCCESS Root opened successfully.
- @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
- could not be opened.
- @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted.
- @retval EFI_DEVICE_ERROR The device had an error
- @retval EFI_INVALID_PARAMETER FileHandle is NULL.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellOpenRoot (
- IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
- OUT SHELL_FILE_HANDLE *FileHandle
- )
-{
- EFI_STATUS Status;
- EFI_HANDLE Handle;
-
- if (FileHandle == NULL) {
- return (EFI_INVALID_PARAMETER);
- }
-
- //
- // find the handle of the device with that device handle and the file system
- //
- /// @todo BlockIo?
- Status = gBS->LocateDevicePath (
- &gEfiSimpleFileSystemProtocolGuid,
- &DevicePath,
- &Handle
- );
- if (EFI_ERROR (Status)) {
- return (EFI_NOT_FOUND);
- }
-
- return (EfiShellOpenRootByHandle (Handle, FileHandle));
-}
-
-/**
- Returns whether any script files are currently being processed.
-
- @retval TRUE There is at least one script file active.
- @retval FALSE No script files are active now.
-
-**/
-BOOLEAN
-EFIAPI
-EfiShellBatchIsActive (
- VOID
- )
-{
- if (ShellCommandGetCurrentScriptFile () == NULL) {
- return (FALSE);
- }
-
- return (TRUE);
-}
-
-/**
- Worker function to open a file based on a device path. this will open the root
- of the volume and then traverse down to the file itself.
-
- @param DevicePath Device Path of the file.
- @param FileHandle Pointer to the file upon a successful return.
- @param OpenMode mode to open file in.
- @param Attributes the File Attributes to use when creating a new file.
-
- @retval EFI_SUCCESS the file is open and FileHandle is valid
- @retval EFI_UNSUPPORTED the device path contained non-path elements
- @retval other an error occurred.
-**/
-EFI_STATUS
-InternalOpenFileDevicePath (
- IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath,
- OUT SHELL_FILE_HANDLE *FileHandle,
- IN UINT64 OpenMode,
- IN UINT64 Attributes OPTIONAL
- )
-{
- EFI_STATUS Status;
- FILEPATH_DEVICE_PATH *FilePathNode;
- EFI_HANDLE Handle;
- SHELL_FILE_HANDLE ShellHandle;
- EFI_FILE_PROTOCOL *Handle1;
- EFI_FILE_PROTOCOL *Handle2;
- FILEPATH_DEVICE_PATH *AlignedNode;
-
- if (FileHandle == NULL) {
- return (EFI_INVALID_PARAMETER);
- }
-
- *FileHandle = NULL;
- Handle1 = NULL;
- Handle2 = NULL;
- Handle = NULL;
- ShellHandle = NULL;
- FilePathNode = NULL;
- AlignedNode = NULL;
-
- Status = EfiShellOpenRoot (DevicePath, &ShellHandle);
-
- if (!EFI_ERROR (Status)) {
- Handle1 = ConvertShellHandleToEfiFileProtocol (ShellHandle);
- if (Handle1 != NULL) {
- //
- // chop off the beginning part before the file system part...
- //
- /// @todo BlockIo?
- Status = gBS->LocateDevicePath (
- &gEfiSimpleFileSystemProtocolGuid,
- &DevicePath,
- &Handle
- );
- if (!EFI_ERROR (Status)) {
- //
- // To access as a file system, the file path should only
- // contain file path components. Follow the file path nodes
- // and find the target file
- //
- for ( FilePathNode = (FILEPATH_DEVICE_PATH *)DevicePath
- ; !IsDevicePathEnd (&FilePathNode->Header)
- ; FilePathNode = (FILEPATH_DEVICE_PATH *)NextDevicePathNode (&FilePathNode->Header)
- )
- {
- SHELL_FREE_NON_NULL (AlignedNode);
- AlignedNode = AllocateCopyPool (DevicePathNodeLength (FilePathNode), FilePathNode);
- //
- // For file system access each node should be a file path component
- //
- if ((DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH) ||
- (DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP)
- )
- {
- Status = EFI_UNSUPPORTED;
- break;
- }
-
- //
- // Open this file path node
- //
- Handle2 = Handle1;
- Handle1 = NULL;
-
- //
- // if this is the last node in the DevicePath always create (if that was requested).
- //
- if (IsDevicePathEnd ((NextDevicePathNode (&FilePathNode->Header)))) {
- Status = Handle2->Open (
- Handle2,
- &Handle1,
- AlignedNode->PathName,
- OpenMode,
- Attributes
- );
- } else {
- //
- // This is not the last node and we dont want to 'create' existing
- // directory entries...
- //
-
- //
- // open without letting it create
- // prevents error on existing files/directories
- //
- Status = Handle2->Open (
- Handle2,
- &Handle1,
- AlignedNode->PathName,
- OpenMode &~EFI_FILE_MODE_CREATE,
- Attributes
- );
- //
- // if above failed now open and create the 'item'
- // if OpenMode EFI_FILE_MODE_CREATE bit was on (but disabled above)
- //
- if ((EFI_ERROR (Status)) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) {
- Status = Handle2->Open (
- Handle2,
- &Handle1,
- AlignedNode->PathName,
- OpenMode,
- Attributes
- );
- }
- }
-
- //
- // Close the last node
- //
- ShellInfoObject.NewEfiShellProtocol->CloseFile (Handle2);
-
- //
- // If there's been an error, stop
- //
- if (EFI_ERROR (Status)) {
- break;
- }
- } // for loop
- }
- }
- }
-
- SHELL_FREE_NON_NULL (AlignedNode);
- if (EFI_ERROR (Status)) {
- if (Handle1 != NULL) {
- ShellInfoObject.NewEfiShellProtocol->CloseFile (Handle1);
- }
- } else {
- *FileHandle = ConvertEfiFileProtocolToShellHandle (Handle1, ShellFileHandleGetPath (ShellHandle));
- }
-
- return (Status);
-}
-
-/**
- Creates a file or directory by name.
-
- This function creates an empty new file or directory with the specified attributes and
- returns the new file's handle. If the file already exists and is read-only, then
- EFI_INVALID_PARAMETER will be returned.
-
- If the file already existed, it is truncated and its attributes updated. If the file is
- created successfully, the FileHandle is the file's handle, else, the FileHandle is NULL.
-
- If the file name begins with >v, then the file handle which is returned refers to the
- shell environment variable with the specified name. If the shell environment variable
- already exists and is non-volatile then EFI_INVALID_PARAMETER is returned.
-
- @param FileName Pointer to NULL-terminated file path
- @param FileAttribs The new file's attributes. the different attributes are
- described in EFI_FILE_PROTOCOL.Open().
- @param FileHandle On return, points to the created file handle or directory's handle
-
- @retval EFI_SUCCESS The file was opened. FileHandle points to the new file's handle.
- @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
- @retval EFI_UNSUPPORTED could not open the file path
- @retval EFI_NOT_FOUND the specified file could not be found on the device, or could not
- file the file system on the device.
- @retval EFI_NO_MEDIA the device has no medium.
- @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
- longer supported.
- @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
- the DirName.
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
- @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
- when the media is write-protected.
- @retval EFI_ACCESS_DENIED The service denied access to the file.
- @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
- @retval EFI_VOLUME_FULL The volume is full.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellCreateFile (
- IN CONST CHAR16 *FileName,
- IN UINT64 FileAttribs,
- OUT SHELL_FILE_HANDLE *FileHandle
- )
-{
- EFI_DEVICE_PATH_PROTOCOL *DevicePath;
- EFI_STATUS Status;
- BOOLEAN Volatile;
-
- //
- // Is this for an environment variable
- // do we start with >v
- //
- if (StrStr (FileName, L">v") == FileName) {
- Status = IsVolatileEnv (FileName + 2, &Volatile);
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- if (!Volatile) {
- return (EFI_INVALID_PARAMETER);
- }
-
- *FileHandle = CreateFileInterfaceEnv (FileName+2);
- return (EFI_SUCCESS);
- }
-
- //
- // We are opening a regular file.
- //
- DevicePath = EfiShellGetDevicePathFromFilePath (FileName);
- if (DevicePath == NULL) {
- return (EFI_NOT_FOUND);
- }
-
- Status = InternalOpenFileDevicePath (DevicePath, FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, FileAttribs);
- FreePool (DevicePath);
-
- return (Status);
-}
-
-/**
- Register a GUID and a localized human readable name for it.
-
- If Guid is not assigned a name, then assign GuidName to Guid. This list of GUID
- names must be used whenever a shell command outputs GUID information.
-
- This function is only available when the major and minor versions in the
- EfiShellProtocol are greater than or equal to 2 and 1, respectively.
-
- @param[in] Guid A pointer to the GUID being registered.
- @param[in] GuidName A pointer to the localized name for the GUID being registered.
-
- @retval EFI_SUCCESS The operation was successful.
- @retval EFI_INVALID_PARAMETER Guid was NULL.
- @retval EFI_INVALID_PARAMETER GuidName was NULL.
- @retval EFI_ACCESS_DENIED Guid already is assigned a name.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellRegisterGuidName (
- IN CONST EFI_GUID *Guid,
- IN CONST CHAR16 *GuidName
- )
-{
- return (AddNewGuidNameMapping (Guid, GuidName, NULL));
-}
-
-/**
- Opens a file or a directory by file name.
-
- This function opens the specified file in the specified OpenMode and returns a file
- handle.
- If the file name begins with >v, then the file handle which is returned refers to the
- shell environment variable with the specified name. If the shell environment variable
- exists, is non-volatile and the OpenMode indicates EFI_FILE_MODE_WRITE, then
- EFI_INVALID_PARAMETER is returned.
-
- If the file name is >i, then the file handle which is returned refers to the standard
- input. If the OpenMode indicates EFI_FILE_MODE_WRITE, then EFI_INVALID_PARAMETER
- is returned.
-
- If the file name is >o, then the file handle which is returned refers to the standard
- output. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
- is returned.
-
- If the file name is >e, then the file handle which is returned refers to the standard
- error. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
- is returned.
-
- If the file name is NUL, then the file handle that is returned refers to the standard NUL
- file. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER is
- returned.
-
- If return EFI_SUCCESS, the FileHandle is the opened file's handle, else, the
- FileHandle is NULL.
-
- @param FileName Points to the NULL-terminated UCS-2 encoded file name.
- @param FileHandle On return, points to the file handle.
- @param OpenMode File open mode. Either EFI_FILE_MODE_READ or
- EFI_FILE_MODE_WRITE from section 12.4 of the UEFI
- Specification.
- @retval EFI_SUCCESS The file was opened. FileHandle has the opened file's handle.
- @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. FileHandle is NULL.
- @retval EFI_UNSUPPORTED Could not open the file path. FileHandle is NULL.
- @retval EFI_NOT_FOUND The specified file could not be found on the device or the file
- system could not be found on the device. FileHandle is NULL.
- @retval EFI_NO_MEDIA The device has no medium. FileHandle is NULL.
- @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
- longer supported. FileHandle is NULL.
- @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
- the FileName. FileHandle is NULL.
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. FileHandle is NULL.
- @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
- when the media is write-protected. FileHandle is NULL.
- @retval EFI_ACCESS_DENIED The service denied access to the file. FileHandle is NULL.
- @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. FileHandle
- is NULL.
- @retval EFI_VOLUME_FULL The volume is full. FileHandle is NULL.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellOpenFileByName (
- IN CONST CHAR16 *FileName,
- OUT SHELL_FILE_HANDLE *FileHandle,
- IN UINT64 OpenMode
- )
-{
- EFI_DEVICE_PATH_PROTOCOL *DevicePath;
- EFI_STATUS Status;
- BOOLEAN Volatile;
-
- *FileHandle = NULL;
-
- //
- // Is this for StdIn
- //
- if (StrCmp (FileName, L">i") == 0) {
- //
- // make sure not writing to StdIn
- //
- if ((OpenMode & EFI_FILE_MODE_WRITE) != 0) {
- return (EFI_INVALID_PARAMETER);
- }
-
- *FileHandle = ShellInfoObject.NewShellParametersProtocol->StdIn;
- ASSERT (*FileHandle != NULL);
- return (EFI_SUCCESS);
- }
-
- //
- // Is this for StdOut
- //
- if (StrCmp (FileName, L">o") == 0) {
- //
- // make sure not writing to StdIn
- //
- if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
- return (EFI_INVALID_PARAMETER);
- }
-
- *FileHandle = &FileInterfaceStdOut;
- return (EFI_SUCCESS);
- }
-
- //
- // Is this for NUL / NULL file
- //
- if ((gUnicodeCollation->StriColl (gUnicodeCollation, (CHAR16 *)FileName, L"NUL") == 0) ||
- (gUnicodeCollation->StriColl (gUnicodeCollation, (CHAR16 *)FileName, L"NULL") == 0))
- {
- *FileHandle = &FileInterfaceNulFile;
- return (EFI_SUCCESS);
- }
-
- //
- // Is this for StdErr
- //
- if (StrCmp (FileName, L">e") == 0) {
- //
- // make sure not writing to StdIn
- //
- if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
- return (EFI_INVALID_PARAMETER);
- }
-
- *FileHandle = &FileInterfaceStdErr;
- return (EFI_SUCCESS);
- }
-
- //
- // Is this for an environment variable
- // do we start with >v
- //
- if (StrStr (FileName, L">v") == FileName) {
- Status = IsVolatileEnv (FileName + 2, &Volatile);
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- if (!Volatile &&
- ((OpenMode & EFI_FILE_MODE_WRITE) != 0))
- {
- return (EFI_INVALID_PARAMETER);
- }
-
- *FileHandle = CreateFileInterfaceEnv (FileName+2);
- return (EFI_SUCCESS);
- }
-
- //
- // We are opening a regular file.
- //
- DevicePath = EfiShellGetDevicePathFromFilePath (FileName);
-
- if (DevicePath == NULL) {
- return (EFI_NOT_FOUND);
- }
-
- //
- // Copy the device path, open the file, then free the memory
- //
- Status = InternalOpenFileDevicePath (DevicePath, FileHandle, OpenMode, 0); // 0 = no specific file attributes
- FreePool (DevicePath);
-
- return (Status);
-}
-
-/**
- Deletes the file specified by the file name.
-
- This function deletes a file.
-
- @param FileName Points to the NULL-terminated file name.
-
- @retval EFI_SUCCESS The file was closed and deleted, and the handle was closed.
- @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted.
- @sa EfiShellCreateFile
-**/
-EFI_STATUS
-EFIAPI
-EfiShellDeleteFileByName (
- IN CONST CHAR16 *FileName
- )
-{
- SHELL_FILE_HANDLE FileHandle;
- EFI_STATUS Status;
-
- FileHandle = NULL;
-
- //
- // get a handle to the file
- //
- Status = EfiShellCreateFile (
- FileName,
- 0,
- &FileHandle
- );
- if (EFI_ERROR (Status)) {
- return (Status);
- }
-
- //
- // now delete the file
- //
- ShellFileHandleRemove (FileHandle);
- return (ShellInfoObject.NewEfiShellProtocol->DeleteFile (FileHandle));
-}
-
-/**
- Disables the page break output mode.
-**/
-VOID
-EFIAPI
-EfiShellDisablePageBreak (
- VOID
- )
-{
- ShellInfoObject.PageBreakEnabled = FALSE;
-}
-
-/**
- Enables the page break output mode.
-**/
-VOID
-EFIAPI
-EfiShellEnablePageBreak (
- VOID
- )
-{
- ShellInfoObject.PageBreakEnabled = TRUE;
-}
-
-/**
- internal worker function to load and run an image via device path.
-
- @param ParentImageHandle A handle of the image that is executing the specified
- command line.
- @param DevicePath device path of the file to execute
- @param CommandLine Points to the NULL-terminated UCS-2 encoded string
- containing the command line. If NULL then the command-
- line will be empty.
- @param Environment Points to a NULL-terminated array of environment
- variables with the format 'x=y', where x is the
- environment variable name and y is the value. If this
- is NULL, then the current shell environment is used.
-
- @param[out] StartImageStatus Returned status from gBS->StartImage.
-
- @retval EFI_SUCCESS The command executed successfully. The status code
- returned by the command is pointed to by StatusCode.
- @retval EFI_INVALID_PARAMETER The parameters are invalid.
- @retval EFI_OUT_OF_RESOURCES Out of resources.
- @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
-**/
-EFI_STATUS
-InternalShellExecuteDevicePath (
- IN CONST EFI_HANDLE *ParentImageHandle,
- IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
- IN CONST CHAR16 *CommandLine OPTIONAL,
- IN CONST CHAR16 **Environment OPTIONAL,
- OUT EFI_STATUS *StartImageStatus OPTIONAL
- )
-{
- EFI_STATUS Status;
- EFI_STATUS StartStatus;
- EFI_STATUS CleanupStatus;
- EFI_HANDLE NewHandle;
- EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
- LIST_ENTRY OrigEnvs;
- EFI_SHELL_PARAMETERS_PROTOCOL ShellParamsProtocol;
- CHAR16 *ImagePath;
- UINTN Index;
- CHAR16 *Walker;
- CHAR16 *NewCmdLine;
-
- if (ParentImageHandle == NULL) {
- return (EFI_INVALID_PARAMETER);
- }
-
- InitializeListHead (&OrigEnvs);
- ZeroMem (&ShellParamsProtocol, sizeof (EFI_SHELL_PARAMETERS_PROTOCOL));
-
- NewHandle = NULL;
-
- NewCmdLine = AllocateCopyPool (StrSize (CommandLine), CommandLine);
- if (NewCmdLine == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL; Walker++) {
- if ((*Walker == L'^') && (*(Walker+1) == L'#')) {
- CopyMem (Walker, Walker+1, StrSize (Walker) - sizeof (Walker[0]));
- }
- }
-
- //
- // Load the image with:
- // FALSE - not from boot manager and NULL, 0 being not already in memory
- //
- Status = gBS->LoadImage (
- FALSE,
- *ParentImageHandle,
- (EFI_DEVICE_PATH_PROTOCOL *)DevicePath,
- NULL,
- 0,
- &NewHandle
- );
-
- if (EFI_ERROR (Status)) {
- if (NewHandle != NULL) {
- gBS->UnloadImage (NewHandle);
- }
-
- FreePool (NewCmdLine);
- return (Status);
- }
-
- Status = gBS->OpenProtocol (
- NewHandle,
- &gEfiLoadedImageProtocolGuid,
- (VOID **)&LoadedImage,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
-
- if (!EFI_ERROR (Status)) {
- //
- // If the image is not an app abort it.
- //
- if (LoadedImage->ImageCodeType != EfiLoaderCode) {
- ShellPrintHiiEx (
- -1,
- -1,
- NULL,
- STRING_TOKEN (STR_SHELL_IMAGE_NOT_APP),
- ShellInfoObject.HiiHandle
- );
- goto UnloadImage;
- }
-
- ASSERT (LoadedImage->LoadOptionsSize == 0);
- if (NewCmdLine != NULL) {
- LoadedImage->LoadOptionsSize = (UINT32)StrSize (NewCmdLine);
- LoadedImage->LoadOptions = (VOID *)NewCmdLine;
- }
-
- //
- // Save our current environment settings for later restoration if necessary
- //
- if (Environment != NULL) {
- Status = GetEnvironmentVariableList (&OrigEnvs);
- if (!EFI_ERROR (Status)) {
- Status = SetEnvironmentVariables (Environment);
- }
- }
-
- //
- // Initialize and install a shell parameters protocol on the image.
- //
- ShellParamsProtocol.StdIn = ShellInfoObject.NewShellParametersProtocol->StdIn;
- ShellParamsProtocol.StdOut = ShellInfoObject.NewShellParametersProtocol->StdOut;
- ShellParamsProtocol.StdErr = ShellInfoObject.NewShellParametersProtocol->StdErr;
- Status = UpdateArgcArgv (&ShellParamsProtocol, NewCmdLine, Efi_Application, NULL, NULL);
- if (EFI_ERROR (Status)) {
- goto UnloadImage;
- }
-
- //
- // Replace Argv[0] with the full path of the binary we're executing:
- // If the command line was "foo", the binary might be called "foo.efi".
- // "The first entry in [Argv] is always the full file path of the
- // executable" - UEFI Shell Spec section 2.3
- //
- ImagePath = EfiShellGetFilePathFromDevicePath (DevicePath);
- // The image we're executing isn't necessarily in a filesystem - it might
- // be memory mapped. In this case EfiShellGetFilePathFromDevicePath will
- // return NULL, and we'll leave Argv[0] as UpdateArgcArgv set it.
- if (ImagePath != NULL) {
- if (ShellParamsProtocol.Argv == NULL) {
- // Command line was empty or null.
- // (UpdateArgcArgv sets Argv to NULL when CommandLine is "" or NULL)
- ShellParamsProtocol.Argv = AllocatePool (sizeof (CHAR16 *));
- if (ShellParamsProtocol.Argv == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto UnloadImage;
- }
-
- ShellParamsProtocol.Argc = 1;
- } else {
- // Free the string UpdateArgcArgv put in Argv[0];
- FreePool (ShellParamsProtocol.Argv[0]);
- }
-
- ShellParamsProtocol.Argv[0] = ImagePath;
- }
-
- Status = gBS->InstallProtocolInterface (&NewHandle, &gEfiShellParametersProtocolGuid, EFI_NATIVE_INTERFACE, &ShellParamsProtocol);
- ASSERT_EFI_ERROR (Status);
-
- /// @todo initialize and install ShellInterface protocol on the new image for compatibility if - PcdGetBool(PcdShellSupportOldProtocols)
-
- //
- // now start the image and if the caller wanted the return code pass it to them...
- //
- if (!EFI_ERROR (Status)) {
- StartStatus = gBS->StartImage (
- NewHandle,
- 0,
- NULL
- );
- if (StartImageStatus != NULL) {
- *StartImageStatus = StartStatus;
- }
-
- CleanupStatus = gBS->UninstallProtocolInterface (
- NewHandle,
- &gEfiShellParametersProtocolGuid,
- &ShellParamsProtocol
- );
- ASSERT_EFI_ERROR (CleanupStatus);
-
- goto FreeAlloc;
- }
-
-UnloadImage:
- // Unload image - We should only get here if we didn't call StartImage
- gBS->UnloadImage (NewHandle);
-
-FreeAlloc:
- // Free Argv (Allocated in UpdateArgcArgv)
- if (ShellParamsProtocol.Argv != NULL) {
- for (Index = 0; Index < ShellParamsProtocol.Argc; Index++) {
- if (ShellParamsProtocol.Argv[Index] != NULL) {
- FreePool (ShellParamsProtocol.Argv[Index]);
- }
- }
-
- FreePool (ShellParamsProtocol.Argv);
- }
- }
-
- // Restore environment variables
- if (!IsListEmpty (&OrigEnvs)) {
- CleanupStatus = SetEnvironmentVariableList (&OrigEnvs);
- ASSERT_EFI_ERROR (CleanupStatus);
- }
-
- FreePool (NewCmdLine);
-
- return (Status);
-}
-
-/**
- internal worker function to load and run an image in the current shell.
-
- @param CommandLine Points to the NULL-terminated UCS-2 encoded string
- containing the command line. If NULL then the command-
- line will be empty.
- @param Environment Points to a NULL-terminated array of environment
- variables with the format 'x=y', where x is the
- environment variable name and y is the value. If this
- is NULL, then the current shell environment is used.
-
- @param[out] StartImageStatus Returned status from the command line.
-
- @retval EFI_SUCCESS The command executed successfully. The status code
- returned by the command is pointed to by StatusCode.
- @retval EFI_INVALID_PARAMETER The parameters are invalid.
- @retval EFI_OUT_OF_RESOURCES Out of resources.
- @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
-**/
-EFI_STATUS
-InternalShellExecute (
- IN CONST CHAR16 *CommandLine OPTIONAL,
- IN CONST CHAR16 **Environment OPTIONAL,
- OUT EFI_STATUS *StartImageStatus OPTIONAL
- )
-{
- EFI_STATUS Status;
- EFI_STATUS CleanupStatus;
- LIST_ENTRY OrigEnvs;
-
- InitializeListHead (&OrigEnvs);
-
- //
- // Save our current environment settings for later restoration if necessary
- //
- if (Environment != NULL) {
- Status = GetEnvironmentVariableList (&OrigEnvs);
- if (!EFI_ERROR (Status)) {
- Status = SetEnvironmentVariables (Environment);
- } else {
- return Status;
- }
- }
-
- Status = RunShellCommand (CommandLine, StartImageStatus);
-
- // Restore environment variables
- if (!IsListEmpty (&OrigEnvs)) {
- CleanupStatus = SetEnvironmentVariableList (&OrigEnvs);
- ASSERT_EFI_ERROR (CleanupStatus);
- }
-
- return (Status);
-}
-
-/**
- Determine if the UEFI Shell is currently running with nesting enabled or disabled.
-
- @retval FALSE nesting is required
- @retval other nesting is enabled
-**/
-STATIC
-BOOLEAN
-NestingEnabled (
- VOID
- )
-{
- EFI_STATUS Status;
- CHAR16 *Temp;
- CHAR16 *Temp2;
- UINTN TempSize;
- BOOLEAN RetVal;
-
- RetVal = TRUE;
- Temp = NULL;
- Temp2 = NULL;
-
- if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) {
- TempSize = 0;
- Temp = NULL;
- Status = SHELL_GET_ENVIRONMENT_VARIABLE (mNoNestingEnvVarName, &TempSize, Temp);
- if (Status == EFI_BUFFER_TOO_SMALL) {
- Temp = AllocateZeroPool (TempSize + sizeof (CHAR16));
- if (Temp != NULL) {
- Status = SHELL_GET_ENVIRONMENT_VARIABLE (mNoNestingEnvVarName, &TempSize, Temp);
- }
- }
-
- Temp2 = StrnCatGrow (&Temp2, NULL, mNoNestingTrue, 0);
- if ((Temp != NULL) && (Temp2 != NULL) && (StringNoCaseCompare (&Temp, &Temp2) == 0)) {
- //
- // Use the no nesting method.
- //
- RetVal = FALSE;
- }
- }
-
- SHELL_FREE_NON_NULL (Temp);
- SHELL_FREE_NON_NULL (Temp2);
- return (RetVal);
-}
-
-/**
- Execute the command line.
-
- This function creates a nested instance of the shell and executes the specified
- command (CommandLine) with the specified environment (Environment). Upon return,
- the status code returned by the specified command is placed in StatusCode.
-
- If Environment is NULL, then the current environment is used and all changes made
- by the commands executed will be reflected in the current environment. If the
- Environment is non-NULL, then the changes made will be discarded.
-
- The CommandLine is executed from the current working directory on the current
- device.
-
- @param ParentImageHandle A handle of the image that is executing the specified
- command line.
- @param CommandLine Points to the NULL-terminated UCS-2 encoded string
- containing the command line. If NULL then the command-
- line will be empty.
- @param Environment Points to a NULL-terminated array of environment
- variables with the format 'x=y', where x is the
- environment variable name and y is the value. If this
- is NULL, then the current shell environment is used.
- @param StatusCode Points to the status code returned by the CommandLine.
-
- @retval EFI_SUCCESS The command executed successfully. The status code
- returned by the command is pointed to by StatusCode.
- @retval EFI_INVALID_PARAMETER The parameters are invalid.
- @retval EFI_OUT_OF_RESOURCES Out of resources.
- @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
- @retval EFI_UNSUPPORTED The support level required for this function is not present.
-
- @sa InternalShellExecuteDevicePath
-**/
-EFI_STATUS
-EFIAPI
-EfiShellExecute (
- IN EFI_HANDLE *ParentImageHandle,
- IN CHAR16 *CommandLine OPTIONAL,
- IN CHAR16 **Environment OPTIONAL,
- OUT EFI_STATUS *StatusCode OPTIONAL
- )
-{
- EFI_STATUS Status;
- CHAR16 *Temp;
- EFI_DEVICE_PATH_PROTOCOL *DevPath;
- UINTN Size;
-
- if ((PcdGet8 (PcdShellSupportLevel) < 1)) {
- return (EFI_UNSUPPORTED);
- }
-
- if (NestingEnabled ()) {
- DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
-
- DEBUG_CODE_BEGIN ();
- Temp = ConvertDevicePathToText (ShellInfoObject.FileDevPath, TRUE, TRUE);
- FreePool (Temp);
- Temp = ConvertDevicePathToText (ShellInfoObject.ImageDevPath, TRUE, TRUE);
- FreePool (Temp);
- Temp = ConvertDevicePathToText (DevPath, TRUE, TRUE);
- FreePool (Temp);
- DEBUG_CODE_END ();
-
- Temp = NULL;
- Size = 0;
- ASSERT ((Temp == NULL && Size == 0) || (Temp != NULL));
- StrnCatGrow (&Temp, &Size, L"Shell.efi -exit ", 0);
- StrnCatGrow (&Temp, &Size, CommandLine, 0);
-
- Status = InternalShellExecuteDevicePath (
- ParentImageHandle,
- DevPath,
- Temp,
- (CONST CHAR16 **)Environment,
- StatusCode
- );
-
- //
- // de-allocate and return
- //
- FreePool (DevPath);
- FreePool (Temp);
- } else {
- Status = InternalShellExecute (
- (CONST CHAR16 *)CommandLine,
- (CONST CHAR16 **)Environment,
- StatusCode
- );
- }
-
- return (Status);
-}
-
-/**
- Utility cleanup function for EFI_SHELL_FILE_INFO objects.
-
- 1) frees all pointers (non-NULL)
- 2) Closes the SHELL_FILE_HANDLE
-
- @param FileListNode pointer to the list node to free
-**/
-VOID
-InternalFreeShellFileInfoNode (
- IN EFI_SHELL_FILE_INFO *FileListNode
- )
-{
- if (FileListNode->Info != NULL) {
- FreePool ((VOID *)FileListNode->Info);
- }
-
- if (FileListNode->FileName != NULL) {
- FreePool ((VOID *)FileListNode->FileName);
- }
-
- if (FileListNode->FullName != NULL) {
- FreePool ((VOID *)FileListNode->FullName);
- }
-
- if (FileListNode->Handle != NULL) {
- ShellInfoObject.NewEfiShellProtocol->CloseFile (FileListNode->Handle);
- }
-
- FreePool (FileListNode);
-}
-
-/**
- Frees the file list.
-
- This function cleans up the file list and any related data structures. It has no
- impact on the files themselves.
-
- @param FileList The file list to free. Type EFI_SHELL_FILE_INFO is
- defined in OpenFileList()
-
- @retval EFI_SUCCESS Free the file list successfully.
- @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
-**/
-EFI_STATUS
-EFIAPI
-EfiShellFreeFileList (
- IN EFI_SHELL_FILE_INFO **FileList
- )
-{
- EFI_SHELL_FILE_INFO *ShellFileListItem;
-
- if ((FileList == NULL) || (*FileList == NULL)) {
- return (EFI_INVALID_PARAMETER);
- }
-
- for ( ShellFileListItem = (EFI_SHELL_FILE_INFO *)GetFirstNode (&(*FileList)->Link)
- ; !IsListEmpty (&(*FileList)->Link)
- ; ShellFileListItem = (EFI_SHELL_FILE_INFO *)GetFirstNode (&(*FileList)->Link)
- )
- {
- RemoveEntryList (&ShellFileListItem->Link);
- InternalFreeShellFileInfoNode (ShellFileListItem);
- }
-
- InternalFreeShellFileInfoNode (*FileList);
- *FileList = NULL;
- return (EFI_SUCCESS);
-}
-
-/**
- Deletes the duplicate file names files in the given file list.
-
- This function deletes the reduplicate files in the given file list.
-
- @param FileList A pointer to the first entry in the file list.
-
- @retval EFI_SUCCESS Always success.
- @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
-**/
-EFI_STATUS
-EFIAPI
-EfiShellRemoveDupInFileList (
- IN EFI_SHELL_FILE_INFO **FileList
- )
-{
- EFI_STATUS Status;
- EFI_SHELL_FILE_INFO *Duplicates;
- EFI_SHELL_FILE_INFO *ShellFileListItem;
- EFI_SHELL_FILE_INFO *ShellFileListItem2;
- EFI_SHELL_FILE_INFO *TempNode;
-
- if ((FileList == NULL) || (*FileList == NULL)) {
- return (EFI_INVALID_PARAMETER);
- }
-
- Status = ShellSortFileList (
- FileList,
- &Duplicates,
- ShellSortFileListByFullName
- );
- if (!EFI_ERROR (Status)) {
- EfiShellFreeFileList (&Duplicates);
- return EFI_SUCCESS;
- }
-
- //
- // Fall back to the slow method that needs no extra memory, and so cannot
- // fail.
- //
- for ( ShellFileListItem = (EFI_SHELL_FILE_INFO *)GetFirstNode (&(*FileList)->Link)
- ; !IsNull (&(*FileList)->Link, &ShellFileListItem->Link)
- ; ShellFileListItem = (EFI_SHELL_FILE_INFO *)GetNextNode (&(*FileList)->Link, &ShellFileListItem->Link)
- )
- {
- for ( ShellFileListItem2 = (EFI_SHELL_FILE_INFO *)GetNextNode (&(*FileList)->Link, &ShellFileListItem->Link)
- ; !IsNull (&(*FileList)->Link, &ShellFileListItem2->Link)
- ; ShellFileListItem2 = (EFI_SHELL_FILE_INFO *)GetNextNode (&(*FileList)->Link, &ShellFileListItem2->Link)
- )
- {
- if (gUnicodeCollation->StriColl (
- gUnicodeCollation,
- (CHAR16 *)ShellFileListItem->FullName,
- (CHAR16 *)ShellFileListItem2->FullName
- ) == 0
- )
- {
- TempNode = (EFI_SHELL_FILE_INFO *)GetPreviousNode (
- &(*FileList)->Link,
- &ShellFileListItem2->Link
- );
- RemoveEntryList (&ShellFileListItem2->Link);
- InternalFreeShellFileInfoNode (ShellFileListItem2);
- // Set ShellFileListItem2 to PreviousNode so we don't access Freed
- // memory in GetNextNode in the loop expression above.
- ShellFileListItem2 = TempNode;
- }
- }
- }
-
- return (EFI_SUCCESS);
-}
-
-//
-// This is the same structure as the external version, but it has no CONST qualifiers.
-//
-typedef struct {
- LIST_ENTRY Link; ///< Linked list members.
- EFI_STATUS Status; ///< Status of opening the file. Valid only if Handle != NULL.
- CHAR16 *FullName; ///< Fully qualified filename.
- CHAR16 *FileName; ///< name of this file.
- SHELL_FILE_HANDLE Handle; ///< Handle for interacting with the opened file or NULL if closed.
- EFI_FILE_INFO *Info; ///< Pointer to the FileInfo struct for this file or NULL.
-} EFI_SHELL_FILE_INFO_NO_CONST;
-
-/**
- Allocates and duplicates a EFI_SHELL_FILE_INFO node.
-
- @param[in] Node The node to copy from.
- @param[in] Save TRUE to set Node->Handle to NULL, FALSE otherwise.
-
- @retval NULL a memory allocation error occurred
- @return != NULL a pointer to the new node
-**/
-EFI_SHELL_FILE_INFO *
-InternalDuplicateShellFileInfo (
- IN EFI_SHELL_FILE_INFO *Node,
- IN BOOLEAN Save
- )
-{
- EFI_SHELL_FILE_INFO_NO_CONST *NewNode;
-
- //
- // try to confirm that the objects are in sync
- //
- ASSERT (sizeof (EFI_SHELL_FILE_INFO_NO_CONST) == sizeof (EFI_SHELL_FILE_INFO));
-
- NewNode = AllocateZeroPool (sizeof (EFI_SHELL_FILE_INFO));
- if (NewNode == NULL) {
- return (NULL);
- }
-
- NewNode->FullName = AllocateCopyPool (StrSize (Node->FullName), Node->FullName);
- NewNode->FileName = AllocateCopyPool (StrSize (Node->FileName), Node->FileName);
- NewNode->Info = AllocateCopyPool ((UINTN)Node->Info->Size, Node->Info);
- if ( (NewNode->FullName == NULL)
- || (NewNode->FileName == NULL)
- || (NewNode->Info == NULL)
- )
- {
- SHELL_FREE_NON_NULL (NewNode->FullName);
- SHELL_FREE_NON_NULL (NewNode->FileName);
- SHELL_FREE_NON_NULL (NewNode->Info);
- SHELL_FREE_NON_NULL (NewNode);
- return (NULL);
- }
-
- NewNode->Status = Node->Status;
- NewNode->Handle = Node->Handle;
- if (!Save) {
- Node->Handle = NULL;
- }
-
- return ((EFI_SHELL_FILE_INFO *)NewNode);
-}
-
-/**
- Allocates and populates a EFI_SHELL_FILE_INFO structure. if any memory operation
- failed it will return NULL.
-
- @param[in] BasePath the Path to prepend onto filename for FullPath
- @param[in] Status Status member initial value.
- @param[in] FileName FileName member initial value.
- @param[in] Handle Handle member initial value.
- @param[in] Info Info struct to copy.
-
- @retval NULL An error occurred.
- @return a pointer to the newly allocated structure.
-**/
-EFI_SHELL_FILE_INFO *
-CreateAndPopulateShellFileInfo (
- IN CONST CHAR16 *BasePath,
- IN CONST EFI_STATUS Status,
- IN CONST CHAR16 *FileName,
- IN CONST SHELL_FILE_HANDLE Handle,
- IN CONST EFI_FILE_INFO *Info
- )
-{
- EFI_SHELL_FILE_INFO *ShellFileListItem;
- CHAR16 *TempString;
- UINTN Size;
-
- TempString = NULL;
- Size = 0;
-
- ShellFileListItem = AllocateZeroPool (sizeof (EFI_SHELL_FILE_INFO));
- if (ShellFileListItem == NULL) {
- return (NULL);
- }
-
- if ((Info != NULL) && (Info->Size != 0)) {
- ShellFileListItem->Info = AllocateZeroPool ((UINTN)Info->Size);
- if (ShellFileListItem->Info == NULL) {
- FreePool (ShellFileListItem);
- return (NULL);
- }
-
- CopyMem (ShellFileListItem->Info, Info, (UINTN)Info->Size);
- } else {
- ShellFileListItem->Info = NULL;
- }
-
- if (FileName != NULL) {
- ASSERT (TempString == NULL);
- ShellFileListItem->FileName = StrnCatGrow (&TempString, 0, FileName, 0);
- if (ShellFileListItem->FileName == NULL) {
- FreePool (ShellFileListItem->Info);
- FreePool (ShellFileListItem);
- return (NULL);
- }
- } else {
- ShellFileListItem->FileName = NULL;
- }
-
- Size = 0;
- TempString = NULL;
- if (BasePath != NULL) {
- ASSERT ((TempString == NULL && Size == 0) || (TempString != NULL));
- TempString = StrnCatGrow (&TempString, &Size, BasePath, 0);
- if (TempString == NULL) {
- FreePool ((VOID *)ShellFileListItem->FileName);
- SHELL_FREE_NON_NULL (ShellFileListItem->Info);
- FreePool (ShellFileListItem);
- return (NULL);
- }
- }
-
- if (ShellFileListItem->FileName != NULL) {
- ASSERT ((TempString == NULL && Size == 0) || (TempString != NULL));
- TempString = StrnCatGrow (&TempString, &Size, ShellFileListItem->FileName, 0);
- if (TempString == NULL) {
- FreePool ((VOID *)ShellFileListItem->FileName);
- FreePool (ShellFileListItem->Info);
- FreePool (ShellFileListItem);
- return (NULL);
- }
- }
-
- TempString = PathCleanUpDirectories (TempString);
-
- ShellFileListItem->FullName = TempString;
- ShellFileListItem->Status = Status;
- ShellFileListItem->Handle = Handle;
-
- return (ShellFileListItem);
-}
-
-/**
- Find all files in a specified directory.
-
- @param FileDirHandle Handle of the directory to search.
- @param FileList On return, points to the list of files in the directory
- or NULL if there are no files in the directory.
-
- @retval EFI_SUCCESS File information was returned successfully.
- @retval EFI_VOLUME_CORRUPTED The file system structures have been corrupted.
- @retval EFI_DEVICE_ERROR The device reported an error.
- @retval EFI_NO_MEDIA The device media is not present.
- @retval EFI_INVALID_PARAMETER The FileDirHandle was not a directory.
- @return An error from FileHandleGetFileName().
-**/
-EFI_STATUS
-EFIAPI
-EfiShellFindFilesInDir (
- IN SHELL_FILE_HANDLE FileDirHandle,
- OUT EFI_SHELL_FILE_INFO **FileList
- )
-{
- EFI_SHELL_FILE_INFO *ShellFileList;
- EFI_SHELL_FILE_INFO *ShellFileListItem;
- EFI_FILE_INFO *FileInfo;
- EFI_STATUS Status;
- BOOLEAN NoFile;
- CHAR16 *TempString;
- CHAR16 *BasePath;
- UINTN Size;
- CHAR16 *TempSpot;
-
- BasePath = NULL;
- Status = FileHandleGetFileName (FileDirHandle, &BasePath);
- if (EFI_ERROR (Status)) {
- return (Status);
- }
-
- if (ShellFileHandleGetPath (FileDirHandle) != NULL) {
- TempString = NULL;
- Size = 0;
- TempString = StrnCatGrow (&TempString, &Size, ShellFileHandleGetPath (FileDirHandle), 0);
- if (TempString == NULL) {
- SHELL_FREE_NON_NULL (BasePath);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- TempSpot = StrStr (TempString, L";");
-
- if (TempSpot != NULL) {
- *TempSpot = CHAR_NULL;
- }
-
- TempString = StrnCatGrow (&TempString, &Size, BasePath, 0);
- if (TempString == NULL) {
- SHELL_FREE_NON_NULL (BasePath);
- return (EFI_OUT_OF_RESOURCES);
- }
-
- SHELL_FREE_NON_NULL (BasePath);
- BasePath = TempString;
- }
-
- NoFile = FALSE;
- ShellFileList = NULL;
- ShellFileListItem = NULL;
- FileInfo = NULL;
- Status = EFI_SUCCESS;
-
- for ( Status = FileHandleFindFirstFile (FileDirHandle, &FileInfo)
- ; !EFI_ERROR (Status) && !NoFile
- ; Status = FileHandleFindNextFile (FileDirHandle, FileInfo, &NoFile)
- )
- {
- if (ShellFileList == NULL) {
- ShellFileList = (EFI_SHELL_FILE_INFO *)AllocateZeroPool (sizeof (EFI_SHELL_FILE_INFO));
- if (ShellFileList == NULL) {
- SHELL_FREE_NON_NULL (BasePath);
- return EFI_OUT_OF_RESOURCES;
- }
-
- InitializeListHead (&ShellFileList->Link);
- }
-
- //
- // allocate a new EFI_SHELL_FILE_INFO and populate it...
- //
- ShellFileListItem = CreateAndPopulateShellFileInfo (
- BasePath,
- EFI_SUCCESS, // success since we didn't fail to open it...
- FileInfo->FileName,
- NULL, // no handle since not open
- FileInfo
- );
- if (ShellFileListItem == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- //
- // Free resources outside the loop.
- //
- break;
- }
-
- InsertTailList (&ShellFileList->Link, &ShellFileListItem->Link);
- }
-
- if (EFI_ERROR (Status)) {
- EfiShellFreeFileList (&ShellFileList);
- *FileList = NULL;
- } else {
- *FileList = ShellFileList;
- }
-
- SHELL_FREE_NON_NULL (BasePath);
- return (Status);
-}
-
-/**
- Get the GUID value from a human readable name.
-
- If GuidName is a known GUID name, then update Guid to have the correct value for
- that GUID.
-
- This function is only available when the major and minor versions in the
- EfiShellProtocol are greater than or equal to 2 and 1, respectively.
-
- @param[in] GuidName A pointer to the localized name for the GUID being queried.
- @param[out] Guid A pointer to the GUID structure to be filled in.
-
- @retval EFI_SUCCESS The operation was successful.
- @retval EFI_INVALID_PARAMETER Guid was NULL.
- @retval EFI_INVALID_PARAMETER GuidName was NULL.
- @retval EFI_NOT_FOUND GuidName is not a known GUID Name.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellGetGuidFromName (
- IN CONST CHAR16 *GuidName,
- OUT EFI_GUID *Guid
- )
-{
- EFI_GUID *NewGuid;
- EFI_STATUS Status;
-
- if ((Guid == NULL) || (GuidName == NULL)) {
- return (EFI_INVALID_PARAMETER);
- }
-
- Status = GetGuidFromStringName (GuidName, NULL, &NewGuid);
-
- if (!EFI_ERROR (Status)) {
- CopyGuid (Guid, NewGuid);
- }
-
- return (Status);
-}
-
-/**
- Get the human readable name for a GUID from the value.
-
- If Guid is assigned a name, then update *GuidName to point to the name. The callee
- should not modify the value.
-
- This function is only available when the major and minor versions in the
- EfiShellProtocol are greater than or equal to 2 and 1, respectively.
-
- @param[in] Guid A pointer to the GUID being queried.
- @param[out] GuidName A pointer to a pointer the localized to name for the GUID being requested
-
- @retval EFI_SUCCESS The operation was successful.
- @retval EFI_INVALID_PARAMETER Guid was NULL.
- @retval EFI_INVALID_PARAMETER GuidName was NULL.
- @retval EFI_NOT_FOUND Guid is not assigned a name.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellGetGuidName (
- IN CONST EFI_GUID *Guid,
- OUT CONST CHAR16 **GuidName
- )
-{
- CHAR16 *Name;
-
- if ((Guid == NULL) || (GuidName == NULL)) {
- return (EFI_INVALID_PARAMETER);
- }
-
- Name = GetStringNameFromGuid (Guid, NULL);
- if ((Name == NULL) || (StrLen (Name) == 0)) {
- SHELL_FREE_NON_NULL (Name);
- return (EFI_NOT_FOUND);
- }
-
- *GuidName = AddBufferToFreeList (Name);
-
- return (EFI_SUCCESS);
-}
-
-/**
- If FileHandle is a directory then the function reads from FileHandle and reads in
- each of the FileInfo structures. If one of them matches the Pattern's first
- "level" then it opens that handle and calls itself on that handle.
-
- If FileHandle is a file and matches all of the remaining Pattern (which would be
- on its last node), then add a EFI_SHELL_FILE_INFO object for this file to fileList.
-
- Upon a EFI_SUCCESS return fromt he function any the caller is responsible to call
- FreeFileList with FileList.
-
- @param[in] FilePattern The FilePattern to check against.
- @param[in] UnicodeCollation The pointer to EFI_UNICODE_COLLATION_PROTOCOL structure
- @param[in] FileHandle The FileHandle to start with
- @param[in, out] FileList pointer to pointer to list of found files.
- @param[in] ParentNode The node for the parent. Same file as identified by HANDLE.
- @param[in] MapName The file system name this file is on.
-
- @retval EFI_SUCCESS all files were found and the FileList contains a list.
- @retval EFI_NOT_FOUND no files were found
- @retval EFI_OUT_OF_RESOURCES a memory allocation failed
-**/
-EFI_STATUS
-ShellSearchHandle (
- IN CONST CHAR16 *FilePattern,
- IN EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation,
- IN SHELL_FILE_HANDLE FileHandle,
- IN OUT EFI_SHELL_FILE_INFO **FileList,
- IN CONST EFI_SHELL_FILE_INFO *ParentNode OPTIONAL,
- IN CONST CHAR16 *MapName
- )
-{
- EFI_STATUS Status;
- CONST CHAR16 *NextFilePatternStart;
- CHAR16 *CurrentFilePattern;
- EFI_SHELL_FILE_INFO *ShellInfo;
- EFI_SHELL_FILE_INFO *ShellInfoNode;
- EFI_SHELL_FILE_INFO *NewShellNode;
- EFI_FILE_INFO *FileInfo;
- BOOLEAN Directory;
- CHAR16 *NewFullName;
- UINTN Size;
-
- if ( (FilePattern == NULL)
- || (UnicodeCollation == NULL)
- || (FileList == NULL)
- )
- {
- return (EFI_INVALID_PARAMETER);
- }
-
- ShellInfo = NULL;
- CurrentFilePattern = NULL;
-
- if (*FilePattern == L'\\') {
- FilePattern++;
- }
-
- for ( NextFilePatternStart = FilePattern
- ; *NextFilePatternStart != CHAR_NULL && *NextFilePatternStart != L'\\'
- ; NextFilePatternStart++)
- {
- }
-
- CurrentFilePattern = AllocateZeroPool ((NextFilePatternStart-FilePattern+1)*sizeof (CHAR16));
- if (CurrentFilePattern == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- StrnCpyS (CurrentFilePattern, NextFilePatternStart-FilePattern+1, FilePattern, NextFilePatternStart-FilePattern);
-
- if ( (CurrentFilePattern[0] == CHAR_NULL)
- && (NextFilePatternStart[0] == CHAR_NULL)
- )
- {
- //
- // we want the parent or root node (if no parent)
- //
- if (ParentNode == NULL) {
- //
- // We want the root node. create the node.
- //
- FileInfo = FileHandleGetInfo (FileHandle);
- NewShellNode = CreateAndPopulateShellFileInfo (
- MapName,
- EFI_SUCCESS,
- L"\\",
- FileHandle,
- FileInfo
- );
- SHELL_FREE_NON_NULL (FileInfo);
- } else {
- //
- // Add the current parameter FileHandle to the list, then end...
- //
- NewShellNode = InternalDuplicateShellFileInfo ((EFI_SHELL_FILE_INFO *)ParentNode, TRUE);
- }
-
- if (NewShellNode == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- } else {
- NewShellNode->Handle = NULL;
- if (*FileList == NULL) {
- *FileList = AllocateZeroPool (sizeof (EFI_SHELL_FILE_INFO));
- InitializeListHead (&((*FileList)->Link));
- }
-
- //
- // Add to the returning to use list
- //
- InsertTailList (&(*FileList)->Link, &NewShellNode->Link);
-
- Status = EFI_SUCCESS;
- }
- } else {
- Status = EfiShellFindFilesInDir (FileHandle, &ShellInfo);
-
- if (!EFI_ERROR (Status)) {
- if (StrStr (NextFilePatternStart, L"\\") != NULL) {
- Directory = TRUE;
- } else {
- Directory = FALSE;
- }
-
- for ( ShellInfoNode = (EFI_SHELL_FILE_INFO *)GetFirstNode (&ShellInfo->Link)
- ; !IsNull (&ShellInfo->Link, &ShellInfoNode->Link)
- ; ShellInfoNode = (EFI_SHELL_FILE_INFO *)GetNextNode (&ShellInfo->Link, &ShellInfoNode->Link)
- )
- {
- if (UnicodeCollation->MetaiMatch (UnicodeCollation, (CHAR16 *)ShellInfoNode->FileName, CurrentFilePattern)) {
- if ((ShellInfoNode->FullName != NULL) && (StrStr (ShellInfoNode->FullName, L":") == NULL)) {
- Size = StrSize (ShellInfoNode->FullName) + StrSize (MapName);
- NewFullName = AllocateZeroPool (Size);
- if (NewFullName == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- } else {
- StrCpyS (NewFullName, Size / sizeof (CHAR16), MapName);
- StrCatS (NewFullName, Size / sizeof (CHAR16), ShellInfoNode->FullName);
- FreePool ((VOID *)ShellInfoNode->FullName);
- ShellInfoNode->FullName = NewFullName;
- }
- }
-
- if (Directory && !EFI_ERROR (Status) && (ShellInfoNode->FullName != NULL) && (ShellInfoNode->FileName != NULL)) {
- //
- // should be a directory
- //
-
- //
- // don't open the . and .. directories
- //
- if ( (StrCmp (ShellInfoNode->FileName, L".") != 0)
- && (StrCmp (ShellInfoNode->FileName, L"..") != 0)
- )
- {
- //
- //
- //
- if (EFI_ERROR (Status)) {
- break;
- }
-
- //
- // Open the directory since we need that handle in the next recursion.
- //
- ShellInfoNode->Status = EfiShellOpenFileByName (ShellInfoNode->FullName, &ShellInfoNode->Handle, EFI_FILE_MODE_READ);
-
- //
- // recurse with the next part of the pattern
- //
- Status = ShellSearchHandle (NextFilePatternStart, UnicodeCollation, ShellInfoNode->Handle, FileList, ShellInfoNode, MapName);
- EfiShellClose (ShellInfoNode->Handle);
- ShellInfoNode->Handle = NULL;
- }
- } else if (!EFI_ERROR (Status)) {
- //
- // should be a file
- //
-
- //
- // copy the information we need into a new Node
- //
- NewShellNode = InternalDuplicateShellFileInfo (ShellInfoNode, FALSE);
- if (NewShellNode == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- }
-
- if (*FileList == NULL) {
- *FileList = AllocateZeroPool (sizeof (EFI_SHELL_FILE_INFO));
- InitializeListHead (&((*FileList)->Link));
- }
-
- //
- // Add to the returning to use list
- //
- InsertTailList (&(*FileList)->Link, &NewShellNode->Link);
- }
- }
-
- if (EFI_ERROR (Status)) {
- break;
- }
- }
-
- if (EFI_ERROR (Status)) {
- EfiShellFreeFileList (&ShellInfo);
- } else {
- Status = EfiShellFreeFileList (&ShellInfo);
- }
- }
- }
-
- if ((*FileList == NULL) || ((*FileList != NULL) && IsListEmpty (&(*FileList)->Link))) {
- Status = EFI_NOT_FOUND;
- }
-
- FreePool (CurrentFilePattern);
- return (Status);
-}
-
-/**
- Find files that match a specified pattern.
-
- This function searches for all files and directories that match the specified
- FilePattern. The FilePattern can contain wild-card characters. The resulting file
- information is placed in the file list FileList.
-
- Wildcards are processed
- according to the rules specified in UEFI Shell 2.0 spec section 3.7.1.
-
- The files in the file list are not opened. The OpenMode field is set to 0 and the FileInfo
- field is set to NULL.
-
- if *FileList is not NULL then it must be a pre-existing and properly initialized list.
-
- @param FilePattern Points to a NULL-terminated shell file path, including wildcards.
- @param FileList On return, points to the start of a file list containing the names
- of all matching files or else points to NULL if no matching files
- were found. only on a EFI_SUCCESS return will; this be non-NULL.
-
- @retval EFI_SUCCESS Files found. FileList is a valid list.
- @retval EFI_NOT_FOUND No files found.
- @retval EFI_NO_MEDIA The device has no media
- @retval EFI_DEVICE_ERROR The device reported an error
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted
-**/
-EFI_STATUS
-EFIAPI
-EfiShellFindFiles (
- IN CONST CHAR16 *FilePattern,
- OUT EFI_SHELL_FILE_INFO **FileList
- )
-{
- EFI_STATUS Status;
- CHAR16 *PatternCopy;
- CHAR16 *PatternCurrentLocation;
- EFI_DEVICE_PATH_PROTOCOL *RootDevicePath;
- SHELL_FILE_HANDLE RootFileHandle;
- CHAR16 *MapName;
- UINTN Count;
-
- if ( (FilePattern == NULL)
- || (FileList == NULL)
- || (StrStr (FilePattern, L":") == NULL)
- )
- {
- return (EFI_INVALID_PARAMETER);
- }
-
- Status = EFI_SUCCESS;
- RootDevicePath = NULL;
- RootFileHandle = NULL;
- MapName = NULL;
- PatternCopy = AllocateCopyPool (StrSize (FilePattern), FilePattern);
- if (PatternCopy == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- PatternCopy = PathCleanUpDirectories (PatternCopy);
-
- Count = StrStr (PatternCopy, L":") - PatternCopy + 1;
- ASSERT (Count <= StrLen (PatternCopy));
-
- ASSERT (MapName == NULL);
- MapName = StrnCatGrow (&MapName, NULL, PatternCopy, Count);
- if (MapName == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- } else {
- RootDevicePath = EfiShellGetDevicePathFromFilePath (PatternCopy);
- if (RootDevicePath == NULL) {
- Status = EFI_INVALID_PARAMETER;
- } else {
- Status = EfiShellOpenRoot (RootDevicePath, &RootFileHandle);
- if (!EFI_ERROR (Status)) {
- for ( PatternCurrentLocation = PatternCopy
- ; *PatternCurrentLocation != ':'
- ; PatternCurrentLocation++)
- {
- }
-
- PatternCurrentLocation++;
- Status = ShellSearchHandle (PatternCurrentLocation, gUnicodeCollation, RootFileHandle, FileList, NULL, MapName);
- EfiShellClose (RootFileHandle);
- }
-
- FreePool (RootDevicePath);
- }
- }
-
- SHELL_FREE_NON_NULL (PatternCopy);
- SHELL_FREE_NON_NULL (MapName);
-
- return (Status);
-}
-
-/**
- Opens the files that match the path specified.
-
- This function opens all of the files specified by Path. Wildcards are processed
- according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. Each
- matching file has an EFI_SHELL_FILE_INFO structure created in a linked list.
-
- @param Path A pointer to the path string.
- @param OpenMode Specifies the mode used to open each file, EFI_FILE_MODE_READ or
- EFI_FILE_MODE_WRITE.
- @param FileList Points to the start of a list of files opened.
-
- @retval EFI_SUCCESS Create the file list successfully.
- @return Others Can't create the file list.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellOpenFileList (
- IN CHAR16 *Path,
- IN UINT64 OpenMode,
- IN OUT EFI_SHELL_FILE_INFO **FileList
- )
-{
- EFI_STATUS Status;
- EFI_SHELL_FILE_INFO *ShellFileListItem;
- CHAR16 *Path2;
- UINTN Path2Size;
- CONST CHAR16 *CurDir;
- BOOLEAN Found;
-
- PathCleanUpDirectories (Path);
-
- Path2Size = 0;
- Path2 = NULL;
-
- if ((FileList == NULL) || (*FileList == NULL)) {
- return (EFI_INVALID_PARAMETER);
- }
-
- if ((*Path == L'.') && (*(Path+1) == L'\\')) {
- Path += 2;
- }
-
- //
- // convert a local path to an absolute path
- //
- if (StrStr (Path, L":") == NULL) {
- CurDir = EfiShellGetCurDir (NULL);
- ASSERT ((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
- StrnCatGrow (&Path2, &Path2Size, CurDir, 0);
- StrnCatGrow (&Path2, &Path2Size, L"\\", 0);
- if (*Path == L'\\') {
- Path++;
- while (PathRemoveLastItem (Path2)) {
- }
- }
-
- ASSERT ((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
- StrnCatGrow (&Path2, &Path2Size, Path, 0);
- } else {
- ASSERT (Path2 == NULL);
- StrnCatGrow (&Path2, NULL, Path, 0);
- }
-
- PathCleanUpDirectories (Path2);
-
- //
- // do the search
- //
- Status = EfiShellFindFiles (Path2, FileList);
-
- FreePool (Path2);
-
- if (EFI_ERROR (Status)) {
- return (Status);
- }
-
- Found = FALSE;
- //
- // We had no errors so open all the files (that are not already opened...)
- //
- for ( ShellFileListItem = (EFI_SHELL_FILE_INFO *)GetFirstNode (&(*FileList)->Link)
- ; !IsNull (&(*FileList)->Link, &ShellFileListItem->Link)
- ; ShellFileListItem = (EFI_SHELL_FILE_INFO *)GetNextNode (&(*FileList)->Link, &ShellFileListItem->Link)
- )
- {
- if ((ShellFileListItem->Status == 0) && (ShellFileListItem->Handle == NULL)) {
- ShellFileListItem->Status = EfiShellOpenFileByName (ShellFileListItem->FullName, &ShellFileListItem->Handle, OpenMode);
- Found = TRUE;
- }
- }
-
- if (!Found) {
- return (EFI_NOT_FOUND);
- }
-
- return (EFI_SUCCESS);
-}
-
-/**
- Gets the environment variable and Attributes, or list of environment variables. Can be
- used instead of GetEnv().
-
- This function returns the current value of the specified environment variable and
- the Attributes. If no variable name was specified, then all of the known
- variables will be returned.
-
- @param[in] Name A pointer to the environment variable name. If Name is NULL,
- then the function will return all of the defined shell
- environment variables. In the case where multiple environment
- variables are being returned, each variable will be terminated
- by a NULL, and the list will be terminated by a double NULL.
- @param[out] Attributes If not NULL, a pointer to the returned attributes bitmask for
- the environment variable. In the case where Name is NULL, and
- multiple environment variables are being returned, Attributes
- is undefined.
-
- @retval NULL The environment variable doesn't exist.
- @return A non-NULL value points to the variable's value. The returned
- pointer does not need to be freed by the caller.
-**/
-CONST CHAR16 *
-EFIAPI
-EfiShellGetEnvEx (
- IN CONST CHAR16 *Name,
- OUT UINT32 *Attributes OPTIONAL
- )
-{
- EFI_STATUS Status;
- VOID *Buffer;
- UINTN Size;
- ENV_VAR_LIST *Node;
- CHAR16 *CurrentWriteLocation;
-
- Size = 0;
- Buffer = NULL;
-
- if (Name == NULL) {
- //
- // Build the semi-colon delimited list. (2 passes)
- //
- for ( Node = (ENV_VAR_LIST *)GetFirstNode (&gShellEnvVarList.Link)
- ; !IsNull (&gShellEnvVarList.Link, &Node->Link)
- ; Node = (ENV_VAR_LIST *)GetNextNode (&gShellEnvVarList.Link, &Node->Link)
- )
- {
- ASSERT (Node->Key != NULL);
- Size += StrSize (Node->Key);
- }
-
- Size += 2*sizeof (CHAR16);
-
- Buffer = AllocateZeroPool (Size);
- if (Buffer == NULL) {
- return (NULL);
- }
-
- CurrentWriteLocation = (CHAR16 *)Buffer;
-
- for ( Node = (ENV_VAR_LIST *)GetFirstNode (&gShellEnvVarList.Link)
- ; !IsNull (&gShellEnvVarList.Link, &Node->Link)
- ; Node = (ENV_VAR_LIST *)GetNextNode (&gShellEnvVarList.Link, &Node->Link)
- )
- {
- ASSERT (Node->Key != NULL);
- StrCpyS (
- CurrentWriteLocation,
- (Size)/sizeof (CHAR16) - (CurrentWriteLocation - ((CHAR16 *)Buffer)),
- Node->Key
- );
- CurrentWriteLocation += StrLen (CurrentWriteLocation) + 1;
- }
- } else {
- //
- // We are doing a specific environment variable
- //
- Status = ShellFindEnvVarInList (Name, (CHAR16 **)&Buffer, &Size, Attributes);
-
- if (EFI_ERROR (Status)) {
- //
- // get the size we need for this EnvVariable
- //
- Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES (Name, Attributes, &Size, Buffer);
- if (Status == EFI_BUFFER_TOO_SMALL) {
- //
- // Allocate the space and recall the get function
- //
- Buffer = AllocateZeroPool (Size);
- Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES (Name, Attributes, &Size, Buffer);
- }
-
- //
- // we didn't get it (might not exist)
- // free the memory if we allocated any and return NULL
- //
- if (EFI_ERROR (Status)) {
- if (Buffer != NULL) {
- FreePool (Buffer);
- }
-
- return (NULL);
- } else {
- //
- // If we did not find the environment variable in the gShellEnvVarList
- // but get it from UEFI variable storage successfully then we need update
- // the gShellEnvVarList.
- //
- ShellFreeEnvVarList ();
- Status = ShellInitEnvVarList ();
- ASSERT (Status == EFI_SUCCESS);
- }
- }
- }
-
- //
- // return the buffer
- //
- return (AddBufferToFreeList (Buffer));
-}
-
-/**
- Gets either a single or list of environment variables.
-
- If name is not NULL then this function returns the current value of the specified
- environment variable.
-
- If Name is NULL, then a list of all environment variable names is returned. Each is a
- NULL terminated string with a double NULL terminating the list.
-
- @param Name A pointer to the environment variable name. If
- Name is NULL, then the function will return all
- of the defined shell environment variables. In
- the case where multiple environment variables are
- being returned, each variable will be terminated by
- a NULL, and the list will be terminated by a double
- NULL.
-
- @retval !=NULL A pointer to the returned string.
- The returned pointer does not need to be freed by the caller.
-
- @retval NULL The environment variable doesn't exist or there are
- no environment variables.
-**/
-CONST CHAR16 *
-EFIAPI
-EfiShellGetEnv (
- IN CONST CHAR16 *Name
- )
-{
- return (EfiShellGetEnvEx (Name, NULL));
-}
-
-/**
- Internal variable setting function. Allows for setting of the read only variables.
-
- @param Name Points to the NULL-terminated environment variable name.
- @param Value Points to the NULL-terminated environment variable value. If the value is an
- empty string then the environment variable is deleted.
- @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
-
- @retval EFI_SUCCESS The environment variable was successfully updated.
-**/
-EFI_STATUS
-InternalEfiShellSetEnv (
- IN CONST CHAR16 *Name,
- IN CONST CHAR16 *Value,
- IN BOOLEAN Volatile
- )
-{
- EFI_STATUS Status;
-
- if ((Value == NULL) || (StrLen (Value) == 0)) {
- Status = SHELL_DELETE_ENVIRONMENT_VARIABLE (Name);
- if (!EFI_ERROR (Status)) {
- ShellRemvoeEnvVarFromList (Name);
- }
- } else {
- SHELL_DELETE_ENVIRONMENT_VARIABLE (Name);
- Status = ShellAddEnvVarToList (
- Name,
- Value,
- StrSize (Value),
- EFI_VARIABLE_BOOTSERVICE_ACCESS | (Volatile ? 0 : EFI_VARIABLE_NON_VOLATILE)
- );
- if (!EFI_ERROR (Status)) {
- Status = Volatile
- ? SHELL_SET_ENVIRONMENT_VARIABLE_V (Name, StrSize (Value) - sizeof (CHAR16), Value)
- : SHELL_SET_ENVIRONMENT_VARIABLE_NV (Name, StrSize (Value) - sizeof (CHAR16), Value);
- if (EFI_ERROR (Status)) {
- ShellRemvoeEnvVarFromList (Name);
- }
- }
- }
-
- return Status;
-}
-
-/**
- Sets the environment variable.
-
- This function changes the current value of the specified environment variable. If the
- environment variable exists and the Value is an empty string, then the environment
- variable is deleted. If the environment variable exists and the Value is not an empty
- string, then the value of the environment variable is changed. If the environment
- variable does not exist and the Value is an empty string, there is no action. If the
- environment variable does not exist and the Value is a non-empty string, then the
- environment variable is created and assigned the specified value.
-
- For a description of volatile and non-volatile environment variables, see UEFI Shell
- 2.0 specification section 3.6.1.
-
- @param Name Points to the NULL-terminated environment variable name.
- @param Value Points to the NULL-terminated environment variable value. If the value is an
- empty string then the environment variable is deleted.
- @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
-
- @retval EFI_SUCCESS The environment variable was successfully updated.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellSetEnv (
- IN CONST CHAR16 *Name,
- IN CONST CHAR16 *Value,
- IN BOOLEAN Volatile
- )
-{
- if ((Name == NULL) || (*Name == CHAR_NULL)) {
- return (EFI_INVALID_PARAMETER);
- }
-
- //
- // Make sure we dont 'set' a predefined read only variable
- //
- if ((StrCmp (Name, L"cwd") == 0) ||
- (StrCmp (Name, L"lasterror") == 0) ||
- (StrCmp (Name, L"profiles") == 0) ||
- (StrCmp (Name, L"uefishellsupport") == 0) ||
- (StrCmp (Name, L"uefishellversion") == 0) ||
- (StrCmp (Name, L"uefiversion") == 0) ||
- (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest &&
- (StrCmp (Name, mNoNestingEnvVarName) == 0))
- )
- {
- return (EFI_INVALID_PARAMETER);
- }
-
- return (InternalEfiShellSetEnv (Name, Value, Volatile));
-}
-
-/**
- Returns the current directory on the specified device.
-
- If FileSystemMapping is NULL, it returns the current working directory. If the
- FileSystemMapping is not NULL, it returns the current directory associated with the
- FileSystemMapping. In both cases, the returned name includes the file system
- mapping (i.e. fs0:\current-dir).
-
- Note that the current directory string should exclude the tailing backslash character.
-
- @param FileSystemMapping A pointer to the file system mapping. If NULL,
- then the current working directory is returned.
-
- @retval !=NULL The current directory.
- @retval NULL Current directory does not exist.
-**/
-CONST CHAR16 *
-EFIAPI
-EfiShellGetCurDir (
- IN CONST CHAR16 *FileSystemMapping OPTIONAL
- )
-{
- CHAR16 *PathToReturn;
- UINTN Size;
- SHELL_MAP_LIST *MapListItem;
-
- if (!IsListEmpty (&gShellMapList.Link)) {
- //
- // if parameter is NULL, use current
- //
- if (FileSystemMapping == NULL) {
- return (EfiShellGetEnv (L"cwd"));
- } else {
- Size = 0;
- PathToReturn = NULL;
- MapListItem = ShellCommandFindMapItem (FileSystemMapping);
- if (MapListItem != NULL) {
- ASSERT ((PathToReturn == NULL && Size == 0) || (PathToReturn != NULL));
- PathToReturn = StrnCatGrow (&PathToReturn, &Size, MapListItem->MapName, 0);
- PathToReturn = StrnCatGrow (&PathToReturn, &Size, MapListItem->CurrentDirectoryPath, 0);
- }
- }
-
- return (AddBufferToFreeList (PathToReturn));
- } else {
- return (NULL);
- }
-}
-
-/**
- Changes the current directory on the specified device.
-
- If the FileSystem is NULL, and the directory Dir does not contain a file system's
- mapped name, this function changes the current working directory.
-
- If the FileSystem is NULL and the directory Dir contains a mapped name, then the
- current file system and the current directory on that file system are changed.
-
- If FileSystem is NULL, and Dir is not NULL, then this changes the current working file
- system.
-
- If FileSystem is not NULL and Dir is not NULL, then this function changes the current
- directory on the specified file system.
-
- If the current working directory or the current working file system is changed then the
- %cwd% environment variable will be updated
-
- Note that the current directory string should exclude the tailing backslash character.
-
- @param FileSystem A pointer to the file system's mapped name. If NULL, then the current working
- directory is changed.
- @param Dir Points to the NULL-terminated directory on the device specified by FileSystem.
-
- @retval EFI_SUCCESS The operation was successful
- @retval EFI_NOT_FOUND The file system could not be found
-**/
-EFI_STATUS
-EFIAPI
-EfiShellSetCurDir (
- IN CONST CHAR16 *FileSystem OPTIONAL,
- IN CONST CHAR16 *Dir
- )
-{
- CHAR16 *MapName;
- SHELL_MAP_LIST *MapListItem;
- UINTN Size;
- EFI_STATUS Status;
- CHAR16 *TempString;
- CHAR16 *DirectoryName;
- UINTN TempLen;
-
- Size = 0;
- MapName = NULL;
- MapListItem = NULL;
- TempString = NULL;
- DirectoryName = NULL;
-
- if (((FileSystem == NULL) && (Dir == NULL)) || (Dir == NULL)) {
- return (EFI_INVALID_PARAMETER);
- }
-
- if (IsListEmpty (&gShellMapList.Link)) {
- return (EFI_NOT_FOUND);
- }
-
- DirectoryName = StrnCatGrow (&DirectoryName, NULL, Dir, 0);
- ASSERT (DirectoryName != NULL);
-
- PathCleanUpDirectories (DirectoryName);
-
- if (FileSystem == NULL) {
- //
- // determine the file system mapping to use
- //
- if (StrStr (DirectoryName, L":") != NULL) {
- ASSERT (MapName == NULL);
- MapName = StrnCatGrow (&MapName, NULL, DirectoryName, (StrStr (DirectoryName, L":")-DirectoryName+1));
- }
-
- //
- // find the file system mapping's entry in the list
- // or use current
- //
- if (MapName != NULL) {
- MapListItem = ShellCommandFindMapItem (MapName);
-
- //
- // make that the current file system mapping
- //
- if (MapListItem != NULL) {
- gShellCurMapping = MapListItem;
- }
- } else {
- MapListItem = gShellCurMapping;
- }
-
- if (MapListItem == NULL) {
- FreePool (DirectoryName);
- SHELL_FREE_NON_NULL (MapName);
- return (EFI_NOT_FOUND);
- }
-
- //
- // now update the MapListItem's current directory
- //
- if ((MapListItem->CurrentDirectoryPath != NULL) && (DirectoryName[StrLen (DirectoryName) - 1] != L':')) {
- FreePool (MapListItem->CurrentDirectoryPath);
- MapListItem->CurrentDirectoryPath = NULL;
- }
-
- if (MapName != NULL) {
- TempLen = StrLen (MapName);
- if (TempLen != StrLen (DirectoryName)) {
- ASSERT ((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
- MapListItem->CurrentDirectoryPath = StrnCatGrow (&MapListItem->CurrentDirectoryPath, &Size, DirectoryName+StrLen (MapName), 0);
- }
-
- FreePool (MapName);
- } else {
- ASSERT ((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
- MapListItem->CurrentDirectoryPath = StrnCatGrow (&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
- }
-
- if (((MapListItem->CurrentDirectoryPath != NULL) && (MapListItem->CurrentDirectoryPath[StrLen (MapListItem->CurrentDirectoryPath)-1] == L'\\')) || (MapListItem->CurrentDirectoryPath == NULL)) {
- ASSERT ((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
- if (MapListItem->CurrentDirectoryPath != NULL) {
- MapListItem->CurrentDirectoryPath[StrLen (MapListItem->CurrentDirectoryPath)-1] = CHAR_NULL;
- }
- }
- } else {
- //
- // cant have a mapping in the directory...
- //
- if (StrStr (DirectoryName, L":") != NULL) {
- FreePool (DirectoryName);
- return (EFI_INVALID_PARAMETER);
- }
-
- //
- // FileSystem != NULL
- //
- MapListItem = ShellCommandFindMapItem (FileSystem);
- if (MapListItem == NULL) {
- FreePool (DirectoryName);
- return (EFI_INVALID_PARAMETER);
- }
-
- // gShellCurMapping = MapListItem;
- if (DirectoryName != NULL) {
- //
- // change current dir on that file system
- //
-
- if (MapListItem->CurrentDirectoryPath != NULL) {
- FreePool (MapListItem->CurrentDirectoryPath);
- DEBUG_CODE (
- MapListItem->CurrentDirectoryPath = NULL;
- );
- }
-
- // ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
- // MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, FileSystem, 0);
- ASSERT ((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
- MapListItem->CurrentDirectoryPath = StrnCatGrow (&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);
- ASSERT ((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
- MapListItem->CurrentDirectoryPath = StrnCatGrow (&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
- if ((MapListItem->CurrentDirectoryPath != NULL) && (MapListItem->CurrentDirectoryPath[StrLen (MapListItem->CurrentDirectoryPath)-1] == L'\\')) {
- ASSERT ((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
- MapListItem->CurrentDirectoryPath[StrLen (MapListItem->CurrentDirectoryPath)-1] = CHAR_NULL;
- }
- }
- }
-
- FreePool (DirectoryName);
- //
- // if updated the current directory then update the environment variable
- //
- if (MapListItem == gShellCurMapping) {
- Size = 0;
- ASSERT ((TempString == NULL && Size == 0) || (TempString != NULL));
- StrnCatGrow (&TempString, &Size, MapListItem->MapName, 0);
- ASSERT ((TempString == NULL && Size == 0) || (TempString != NULL));
- StrnCatGrow (&TempString, &Size, MapListItem->CurrentDirectoryPath, 0);
- Status = InternalEfiShellSetEnv (L"cwd", TempString, TRUE);
- FreePool (TempString);
- return (Status);
- }
-
- return (EFI_SUCCESS);
-}
-
-/**
- Return help information about a specific command.
-
- This function returns the help information for the specified command. The help text
- can be internal to the shell or can be from a UEFI Shell manual page.
-
- If Sections is specified, then each section name listed will be compared in a casesensitive
- manner, to the section names described in Appendix B. If the section exists,
- it will be appended to the returned help text. If the section does not exist, no
- information will be returned. If Sections is NULL, then all help text information
- available will be returned.
-
- @param Command Points to the NULL-terminated UEFI Shell command name.
- @param Sections Points to the NULL-terminated comma-delimited
- section names to return. If NULL, then all
- sections will be returned.
- @param HelpText On return, points to a callee-allocated buffer
- containing all specified help text.
-
- @retval EFI_SUCCESS The help text was returned.
- @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
- returned help text.
- @retval EFI_INVALID_PARAMETER HelpText is NULL
- @retval EFI_NOT_FOUND There is no help text available for Command.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellGetHelpText (
- IN CONST CHAR16 *Command,
- IN CONST CHAR16 *Sections OPTIONAL,
- OUT CHAR16 **HelpText
- )
-{
- CONST CHAR16 *ManFileName;
- CHAR16 *FixCommand;
- EFI_STATUS Status;
-
- ASSERT (HelpText != NULL);
- FixCommand = NULL;
-
- ManFileName = ShellCommandGetManFileNameHandler (Command);
-
- if (ManFileName != NULL) {
- return (ProcessManFile (ManFileName, Command, Sections, NULL, HelpText));
- } else {
- if ( (StrLen (Command) > 4)
- && ((Command[StrLen (Command)-1] == L'i') || (Command[StrLen (Command)-1] == L'I'))
- && ((Command[StrLen (Command)-2] == L'f') || (Command[StrLen (Command)-2] == L'F'))
- && ((Command[StrLen (Command)-3] == L'e') || (Command[StrLen (Command)-3] == L'E'))
- && (Command[StrLen (Command)-4] == L'.')
- )
- {
- FixCommand = AllocateZeroPool (StrSize (Command) - 4 * sizeof (CHAR16));
- if (FixCommand == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- StrnCpyS (
- FixCommand,
- (StrSize (Command) - 4 * sizeof (CHAR16))/sizeof (CHAR16),
- Command,
- StrLen (Command)-4
- );
- Status = ProcessManFile (FixCommand, FixCommand, Sections, NULL, HelpText);
- FreePool (FixCommand);
- return Status;
- } else {
- return (ProcessManFile (Command, Command, Sections, NULL, HelpText));
- }
- }
-}
-
-/**
- Gets the enable status of the page break output mode.
-
- User can use this function to determine current page break mode.
-
- @retval TRUE The page break output mode is enabled.
- @retval FALSE The page break output mode is disabled.
-**/
-BOOLEAN
-EFIAPI
-EfiShellGetPageBreak (
- VOID
- )
-{
- return (ShellInfoObject.PageBreakEnabled);
-}
-
-/**
- Judges whether the active shell is the root shell.
-
- This function makes the user to know that whether the active Shell is the root shell.
-
- @retval TRUE The active Shell is the root Shell.
- @retval FALSE The active Shell is NOT the root Shell.
-**/
-BOOLEAN
-EFIAPI
-EfiShellIsRootShell (
- VOID
- )
-{
- return (ShellInfoObject.RootShellInstance);
-}
-
-/**
- function to return a semi-colon delimited list of all alias' in the current shell
-
- up to caller to free the memory.
-
- @retval NULL No alias' were found
- @retval NULL An error occurred getting alias'
- @return !NULL a list of all alias'
-**/
-CHAR16 *
-InternalEfiShellGetListAlias (
- VOID
- )
-{
- EFI_STATUS Status;
- EFI_GUID Guid;
- CHAR16 *VariableName;
- UINTN NameSize;
- UINTN NameBufferSize;
- CHAR16 *RetVal;
- UINTN RetSize;
-
- NameBufferSize = INIT_NAME_BUFFER_SIZE;
- VariableName = AllocateZeroPool (NameBufferSize);
- RetSize = 0;
- RetVal = NULL;
-
- if (VariableName == NULL) {
- return (NULL);
- }
-
- VariableName[0] = CHAR_NULL;
-
- while (TRUE) {
- NameSize = NameBufferSize;
- Status = gRT->GetNextVariableName (&NameSize, VariableName, &Guid);
- if (Status == EFI_NOT_FOUND) {
- break;
- } else if (Status == EFI_BUFFER_TOO_SMALL) {
- NameBufferSize = NameSize > NameBufferSize * 2 ? NameSize : NameBufferSize * 2;
- SHELL_FREE_NON_NULL (VariableName);
- VariableName = AllocateZeroPool (NameBufferSize);
- if (VariableName == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- SHELL_FREE_NON_NULL (RetVal);
- RetVal = NULL;
- break;
- }
-
- NameSize = NameBufferSize;
- Status = gRT->GetNextVariableName (&NameSize, VariableName, &Guid);
- }
-
- if (EFI_ERROR (Status)) {
- SHELL_FREE_NON_NULL (RetVal);
- RetVal = NULL;
- break;
- }
-
- if (CompareGuid (&Guid, &gShellAliasGuid)) {
- ASSERT ((RetVal == NULL && RetSize == 0) || (RetVal != NULL));
- RetVal = StrnCatGrow (&RetVal, &RetSize, VariableName, 0);
- RetVal = StrnCatGrow (&RetVal, &RetSize, L";", 0);
- } // compare guid
- } // while
-
- SHELL_FREE_NON_NULL (VariableName);
-
- return (RetVal);
-}
-
-/**
- Convert a null-terminated unicode string, in-place, to all lowercase.
- Then return it.
-
- @param Str The null-terminated string to be converted to all lowercase.
-
- @return The null-terminated string converted into all lowercase.
-**/
-CHAR16 *
-ToLower (
- CHAR16 *Str
- )
-{
- UINTN Index;
-
- for (Index = 0; Str[Index] != L'\0'; Index++) {
- if ((Str[Index] >= L'A') && (Str[Index] <= L'Z')) {
- Str[Index] -= (CHAR16)(L'A' - L'a');
- }
- }
-
- return Str;
-}
-
-/**
- This function returns the command associated with a alias or a list of all
- alias'.
-
- @param[in] Alias Points to the NULL-terminated shell alias.
- If this parameter is NULL, then all
- aliases will be returned in ReturnedData.
- @param[out] Volatile upon return of a single command if TRUE indicates
- this is stored in a volatile fashion. FALSE otherwise.
-
- @return If Alias is not NULL, it will return a pointer to
- the NULL-terminated command for that alias.
- If Alias is NULL, ReturnedData points to a ';'
- delimited list of alias (e.g.
- ReturnedData = "dir;del;copy;mfp") that is NULL-terminated.
- @retval NULL an error occurred
- @retval NULL Alias was not a valid Alias
-**/
-CONST CHAR16 *
-EFIAPI
-EfiShellGetAlias (
- IN CONST CHAR16 *Alias,
- OUT BOOLEAN *Volatile OPTIONAL
- )
-{
- CHAR16 *RetVal;
- UINTN RetSize;
- UINT32 Attribs;
- EFI_STATUS Status;
- CHAR16 *AliasLower;
- CHAR16 *AliasVal;
-
- // Convert to lowercase to make aliases case-insensitive
- if (Alias != NULL) {
- AliasLower = AllocateCopyPool (StrSize (Alias), Alias);
- if (AliasLower == NULL) {
- return NULL;
- }
-
- ToLower (AliasLower);
-
- if (Volatile == NULL) {
- GetVariable2 (AliasLower, &gShellAliasGuid, (VOID **)&AliasVal, NULL);
- FreePool (AliasLower);
- return (AddBufferToFreeList (AliasVal));
- }
-
- RetSize = 0;
- RetVal = NULL;
- Status = gRT->GetVariable (AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
- if (Status == EFI_BUFFER_TOO_SMALL) {
- RetVal = AllocateZeroPool (RetSize);
- Status = gRT->GetVariable (AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
- }
-
- if (EFI_ERROR (Status)) {
- if (RetVal != NULL) {
- FreePool (RetVal);
- }
-
- FreePool (AliasLower);
- return (NULL);
- }
-
- if ((EFI_VARIABLE_NON_VOLATILE & Attribs) == EFI_VARIABLE_NON_VOLATILE) {
- *Volatile = FALSE;
- } else {
- *Volatile = TRUE;
- }
-
- FreePool (AliasLower);
- return (AddBufferToFreeList (RetVal));
- }
-
- return (AddBufferToFreeList (InternalEfiShellGetListAlias ()));
-}
-
-/**
- Changes a shell command alias.
-
- This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
-
- this function does not check for built in alias'.
-
- @param[in] Command Points to the NULL-terminated shell command or existing alias.
- @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and
- Command refers to an alias, that alias will be deleted.
- @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the
- Alias being set will be stored in a non-volatile fashion.
-
- @retval EFI_SUCCESS Alias created or deleted successfully.
- @retval EFI_NOT_FOUND the Alias intended to be deleted was not found
-**/
-EFI_STATUS
-InternalSetAlias (
- IN CONST CHAR16 *Command,
- IN CONST CHAR16 *Alias,
- IN BOOLEAN Volatile
- )
-{
- EFI_STATUS Status;
- CHAR16 *AliasLower;
- BOOLEAN DeleteAlias;
-
- DeleteAlias = FALSE;
- if (Alias == NULL) {
- //
- // We must be trying to remove one if Alias is NULL
- // remove an alias (but passed in COMMAND parameter)
- //
- Alias = Command;
- DeleteAlias = TRUE;
- }
-
- ASSERT (Alias != NULL);
-
- //
- // Convert to lowercase to make aliases case-insensitive
- //
- AliasLower = AllocateCopyPool (StrSize (Alias), Alias);
- if (AliasLower == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- ToLower (AliasLower);
-
- if (DeleteAlias) {
- Status = gRT->SetVariable (AliasLower, &gShellAliasGuid, 0, 0, NULL);
- } else {
- Status = gRT->SetVariable (
- AliasLower,
- &gShellAliasGuid,
- EFI_VARIABLE_BOOTSERVICE_ACCESS | (Volatile ? 0 : EFI_VARIABLE_NON_VOLATILE),
- StrSize (Command),
- (VOID *)Command
- );
- }
-
- FreePool (AliasLower);
-
- return Status;
-}
-
-/**
- Changes a shell command alias.
-
- This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
-
-
- @param[in] Command Points to the NULL-terminated shell command or existing alias.
- @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and
- Command refers to an alias, that alias will be deleted.
- @param[in] Replace If TRUE and the alias already exists, then the existing alias will be replaced. If
- FALSE and the alias already exists, then the existing alias is unchanged and
- EFI_ACCESS_DENIED is returned.
- @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the
- Alias being set will be stored in a non-volatile fashion.
-
- @retval EFI_SUCCESS Alias created or deleted successfully.
- @retval EFI_NOT_FOUND the Alias intended to be deleted was not found
- @retval EFI_ACCESS_DENIED The alias is a built-in alias or already existed and Replace was set to
- FALSE.
- @retval EFI_INVALID_PARAMETER Command is null or the empty string.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellSetAlias (
- IN CONST CHAR16 *Command,
- IN CONST CHAR16 *Alias,
- IN BOOLEAN Replace,
- IN BOOLEAN Volatile
- )
-{
- if (ShellCommandIsOnAliasList ((Alias == NULL) ? Command : Alias)) {
- //
- // cant set over a built in alias
- //
- return (EFI_ACCESS_DENIED);
- } else if ((Command == NULL) || (*Command == CHAR_NULL) || (StrLen (Command) == 0)) {
- //
- // Command is null or empty
- //
- return (EFI_INVALID_PARAMETER);
- } else if ((EfiShellGetAlias (Command, NULL) != NULL) && !Replace) {
- //
- // Alias already exists, Replace not set
- //
- return (EFI_ACCESS_DENIED);
- } else {
- return (InternalSetAlias (Command, Alias, Volatile));
- }
-}
-
-// Pure FILE_HANDLE operations are passed to FileHandleLib
-// these functions are indicated by the *
-EFI_SHELL_PROTOCOL mShellProtocol = {
- EfiShellExecute,
- EfiShellGetEnv,
- EfiShellSetEnv,
- EfiShellGetAlias,
- EfiShellSetAlias,
- EfiShellGetHelpText,
- EfiShellGetDevicePathFromMap,
- EfiShellGetMapFromDevicePath,
- EfiShellGetDevicePathFromFilePath,
- EfiShellGetFilePathFromDevicePath,
- EfiShellSetMap,
- EfiShellGetCurDir,
- EfiShellSetCurDir,
- EfiShellOpenFileList,
- EfiShellFreeFileList,
- EfiShellRemoveDupInFileList,
- EfiShellBatchIsActive,
- EfiShellIsRootShell,
- EfiShellEnablePageBreak,
- EfiShellDisablePageBreak,
- EfiShellGetPageBreak,
- EfiShellGetDeviceName,
- (EFI_SHELL_GET_FILE_INFO)FileHandleGetInfo, // *
- (EFI_SHELL_SET_FILE_INFO)FileHandleSetInfo, // *
- EfiShellOpenFileByName,
- EfiShellClose,
- EfiShellCreateFile,
- (EFI_SHELL_READ_FILE)FileHandleRead, // *
- (EFI_SHELL_WRITE_FILE)FileHandleWrite, // *
- (EFI_SHELL_DELETE_FILE)FileHandleDelete, // *
- EfiShellDeleteFileByName,
- (EFI_SHELL_GET_FILE_POSITION)FileHandleGetPosition, // *
- (EFI_SHELL_SET_FILE_POSITION)FileHandleSetPosition, // *
- (EFI_SHELL_FLUSH_FILE)FileHandleFlush, // *
- EfiShellFindFiles,
- EfiShellFindFilesInDir,
- (EFI_SHELL_GET_FILE_SIZE)FileHandleGetSize, // *
- EfiShellOpenRoot,
- EfiShellOpenRootByHandle,
- NULL,
- SHELL_MAJOR_VERSION,
- SHELL_MINOR_VERSION,
-
- // New for UEFI Shell 2.1
- EfiShellRegisterGuidName,
- EfiShellGetGuidName,
- EfiShellGetGuidFromName,
- EfiShellGetEnvEx
-};
-
-/**
- Function to create and install on the current handle.
-
- Will overwrite any existing ShellProtocols in the system to be sure that
- the current shell is in control.
-
- This must be removed via calling CleanUpShellProtocol().
-
- @param[in, out] NewShell The pointer to the pointer to the structure
- to install.
-
- @retval EFI_SUCCESS The operation was successful.
- @return An error from LocateHandle, CreateEvent, or other core function.
-**/
-EFI_STATUS
-CreatePopulateInstallShellProtocol (
- IN OUT EFI_SHELL_PROTOCOL **NewShell
- )
-{
- EFI_STATUS Status;
- UINTN BufferSize;
- EFI_HANDLE *Buffer;
- UINTN HandleCounter;
- SHELL_PROTOCOL_HANDLE_LIST *OldProtocolNode;
- EFI_SHELL_PROTOCOL *OldShell;
-
- if (NewShell == NULL) {
- return (EFI_INVALID_PARAMETER);
- }
-
- BufferSize = 0;
- Buffer = NULL;
- OldProtocolNode = NULL;
- InitializeListHead (&ShellInfoObject.OldShellList.Link);
-
- //
- // Initialize EfiShellProtocol object...
- //
- Status = gBS->CreateEvent (
- 0,
- 0,
- NULL,
- NULL,
- &mShellProtocol.ExecutionBreak
- );
- if (EFI_ERROR (Status)) {
- return (Status);
- }
-
- //
- // Get the size of the buffer we need.
- //
- Status = gBS->LocateHandle (
- ByProtocol,
- &gEfiShellProtocolGuid,
- NULL,
- &BufferSize,
- Buffer
- );
- if (Status == EFI_BUFFER_TOO_SMALL) {
- //
- // Allocate and recall with buffer of correct size
- //
- Buffer = AllocateZeroPool (BufferSize);
- if (Buffer == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- Status = gBS->LocateHandle (
- ByProtocol,
- &gEfiShellProtocolGuid,
- NULL,
- &BufferSize,
- Buffer
- );
- if (EFI_ERROR (Status)) {
- FreePool (Buffer);
- return (Status);
- }
-
- //
- // now overwrite each of them, but save the info to restore when we end.
- //
- for (HandleCounter = 0; HandleCounter < (BufferSize/sizeof (EFI_HANDLE)); HandleCounter++) {
- Status = gBS->OpenProtocol (
- Buffer[HandleCounter],
- &gEfiShellProtocolGuid,
- (VOID **)&OldShell,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- if (!EFI_ERROR (Status)) {
- OldProtocolNode = AllocateZeroPool (sizeof (SHELL_PROTOCOL_HANDLE_LIST));
- if (OldProtocolNode == NULL) {
- if (!IsListEmpty (&ShellInfoObject.OldShellList.Link)) {
- CleanUpShellProtocol (&mShellProtocol);
- }
-
- Status = EFI_OUT_OF_RESOURCES;
- break;
- }
-
- //
- // reinstall over the old one...
- //
- OldProtocolNode->Handle = Buffer[HandleCounter];
- OldProtocolNode->Interface = OldShell;
- Status = gBS->ReinstallProtocolInterface (
- OldProtocolNode->Handle,
- &gEfiShellProtocolGuid,
- OldProtocolNode->Interface,
- (VOID *)(&mShellProtocol)
- );
- if (!EFI_ERROR (Status)) {
- //
- // we reinstalled successfully. log this so we can reverse it later.
- //
-
- //
- // add to the list for subsequent...
- //
- InsertTailList (&ShellInfoObject.OldShellList.Link, &OldProtocolNode->Link);
- }
- }
- }
-
- FreePool (Buffer);
- } else if (Status == EFI_NOT_FOUND) {
- ASSERT (IsListEmpty (&ShellInfoObject.OldShellList.Link));
- //
- // no one else published yet. just publish it ourselves.
- //
- Status = gBS->InstallProtocolInterface (
- &gImageHandle,
- &gEfiShellProtocolGuid,
- EFI_NATIVE_INTERFACE,
- (VOID *)(&mShellProtocol)
- );
- }
-
- if (PcdGetBool (PcdShellSupportOldProtocols)) {
- /// @todo support ShellEnvironment2
- /// @todo do we need to support ShellEnvironment (not ShellEnvironment2) also?
- }
-
- if (!EFI_ERROR (Status)) {
- *NewShell = &mShellProtocol;
- }
-
- return (Status);
-}
-
-/**
- Opposite of CreatePopulateInstallShellProtocol.
-
- Free all memory and restore the system to the state it was in before calling
- CreatePopulateInstallShellProtocol.
-
- @param[in, out] NewShell The pointer to the new shell protocol structure.
-
- @retval EFI_SUCCESS The operation was successful.
-**/
-EFI_STATUS
-CleanUpShellProtocol (
- IN OUT EFI_SHELL_PROTOCOL *NewShell
- )
-{
- SHELL_PROTOCOL_HANDLE_LIST *Node2;
-
- //
- // if we need to restore old protocols...
- //
- if (!IsListEmpty (&ShellInfoObject.OldShellList.Link)) {
- for (Node2 = (SHELL_PROTOCOL_HANDLE_LIST *)GetFirstNode (&ShellInfoObject.OldShellList.Link)
- ; !IsListEmpty (&ShellInfoObject.OldShellList.Link)
- ; Node2 = (SHELL_PROTOCOL_HANDLE_LIST *)GetFirstNode (&ShellInfoObject.OldShellList.Link)
- )
- {
- RemoveEntryList (&Node2->Link);
- gBS->ReinstallProtocolInterface (Node2->Handle, &gEfiShellProtocolGuid, NewShell, Node2->Interface);
- FreePool (Node2);
- }
- } else {
- //
- // no need to restore
- //
- gBS->UninstallProtocolInterface (gImageHandle, &gEfiShellProtocolGuid, NewShell);
- }
-
- return EFI_SUCCESS;
-}
-
-/**
- Cleanup the shell environment.
-
- @param[in, out] NewShell The pointer to the new shell protocol structure.
-
- @retval EFI_SUCCESS The operation was successful.
-**/
-EFI_STATUS
-CleanUpShellEnvironment (
- IN OUT EFI_SHELL_PROTOCOL *NewShell
- )
-{
- EFI_STATUS Status;
- EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
-
- CleanUpShellProtocol (NewShell);
-
- Status = gBS->CloseEvent (NewShell->ExecutionBreak);
- NewShell->ExecutionBreak = NULL;
-
- Status = gBS->OpenProtocol (
- gST->ConsoleInHandle,
- &gEfiSimpleTextInputExProtocolGuid,
- (VOID **)&SimpleEx,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
-
- if (!EFI_ERROR (Status)) {
- Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlCNotifyHandle1);
- Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlCNotifyHandle2);
- Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlCNotifyHandle3);
- Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlCNotifyHandle4);
- Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlSNotifyHandle1);
- Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlSNotifyHandle2);
- Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlSNotifyHandle3);
- Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlSNotifyHandle4);
- }
-
- return (Status);
-}
-
-/**
- Notification function for keystrokes.
-
- @param[in] KeyData The key that was pressed.
-
- @retval EFI_SUCCESS The operation was successful.
-**/
-EFI_STATUS
-EFIAPI
-NotificationFunction (
- IN EFI_KEY_DATA *KeyData
- )
-{
- if (((KeyData->Key.UnicodeChar == L'c') &&
- ((KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED)) || (KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED)))) ||
- (KeyData->Key.UnicodeChar == 3)
- )
- {
- if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) {
- return (EFI_UNSUPPORTED);
- }
-
- return (gBS->SignalEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak));
- } else if ((KeyData->Key.UnicodeChar == L's') &&
- ((KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED)) || (KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED)))
- )
- {
- ShellInfoObject.HaltOutput = TRUE;
- }
-
- return (EFI_SUCCESS);
-}
-
-/**
- Function to start monitoring for CTRL-C using SimpleTextInputEx. This
- feature's enabled state was not known when the shell initially launched.
-
- @retval EFI_SUCCESS The feature is enabled.
- @retval EFI_OUT_OF_RESOURCES There is not enough memory available.
-**/
-EFI_STATUS
-InernalEfiShellStartMonitor (
- VOID
- )
-{
- EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
- EFI_KEY_DATA KeyData;
- EFI_STATUS Status;
-
- Status = gBS->OpenProtocol (
- gST->ConsoleInHandle,
- &gEfiSimpleTextInputExProtocolGuid,
- (VOID **)&SimpleEx,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- if (EFI_ERROR (Status)) {
- ShellPrintHiiEx (
- -1,
- -1,
- NULL,
- STRING_TOKEN (STR_SHELL_NO_IN_EX),
- ShellInfoObject.HiiHandle
- );
- return (EFI_SUCCESS);
- }
-
- if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) {
- return (EFI_UNSUPPORTED);
- }
-
- KeyData.KeyState.KeyToggleState = 0;
- KeyData.Key.ScanCode = 0;
- KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
- KeyData.Key.UnicodeChar = L'c';
-
- Status = SimpleEx->RegisterKeyNotify (
- SimpleEx,
- &KeyData,
- NotificationFunction,
- &ShellInfoObject.CtrlCNotifyHandle1
- );
-
- KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
- if (!EFI_ERROR (Status)) {
- Status = SimpleEx->RegisterKeyNotify (
- SimpleEx,
- &KeyData,
- NotificationFunction,
- &ShellInfoObject.CtrlCNotifyHandle2
- );
- }
-
- KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
- KeyData.Key.UnicodeChar = 3;
- if (!EFI_ERROR (Status)) {
- Status = SimpleEx->RegisterKeyNotify (
- SimpleEx,
- &KeyData,
- NotificationFunction,
- &ShellInfoObject.CtrlCNotifyHandle3
- );
- }
-
- KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
- if (!EFI_ERROR (Status)) {
- Status = SimpleEx->RegisterKeyNotify (
- SimpleEx,
- &KeyData,
- NotificationFunction,
- &ShellInfoObject.CtrlCNotifyHandle4
- );
- }
-
- return (Status);
-}
+/** @file + Member functions of EFI_SHELL_PROTOCOL and functions for creation, + manipulation, and initialization of EFI_SHELL_PROTOCOL. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR> + (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Shell.h" + +#define INIT_NAME_BUFFER_SIZE 128 + +/** + Close an open file handle. + + This function closes a specified file handle. All "dirty" cached file data is + flushed to the device, and the file is closed. In all cases the handle is + closed. + + @param[in] FileHandle The file handle to close. + + @retval EFI_SUCCESS The file handle was closed successfully. +**/ +EFI_STATUS +EFIAPI +EfiShellClose ( + IN SHELL_FILE_HANDLE FileHandle + ) +{ + ShellFileHandleRemove (FileHandle); + return (FileHandleClose (ConvertShellHandleToEfiFileProtocol (FileHandle))); +} + +/** + Internal worker to determine whether there is a BlockIo somewhere + upon the device path specified. + + @param[in] DevicePath The device path to test. + + @retval TRUE gEfiBlockIoProtocolGuid was installed on a handle with this device path + @retval FALSE gEfiBlockIoProtocolGuid was not found. +**/ +BOOLEAN +InternalShellProtocolIsBlockIoPresent ( + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy; + EFI_STATUS Status; + EFI_HANDLE Handle; + + Handle = NULL; + + DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL *)DevicePath; + Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &DevicePathCopy, &Handle); + + if ((Handle != NULL) && (!EFI_ERROR (Status))) { + return (TRUE); + } + + return (FALSE); +} + +/** + Internal worker to determine whether there is a file system somewhere + upon the device path specified. + + @param[in] DevicePath The device path to test. + + @retval TRUE gEfiSimpleFileSystemProtocolGuid was installed on a handle with this device path + @retval FALSE gEfiSimpleFileSystemProtocolGuid was not found. +**/ +BOOLEAN +InternalShellProtocolIsSimpleFileSystemPresent ( + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy; + EFI_STATUS Status; + EFI_HANDLE Handle; + + Handle = NULL; + + DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL *)DevicePath; + Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle); + + if ((Handle != NULL) && (!EFI_ERROR (Status))) { + return (TRUE); + } + + return (FALSE); +} + +/** + This function creates a mapping for a device path. + + If both DevicePath and Mapping are NULL, this will reset the mapping to default values. + + @param DevicePath Points to the device path. If this is NULL and Mapping points to a valid mapping, + then the mapping will be deleted. + @param Mapping Points to the NULL-terminated mapping for the device path. Must end with a ':' + + @retval EFI_SUCCESS Mapping created or deleted successfully. + @retval EFI_NO_MAPPING There is no handle that corresponds exactly to DevicePath. See the + boot service function LocateDevicePath(). + @retval EFI_ACCESS_DENIED The mapping is a built-in alias. + @retval EFI_INVALID_PARAMETER Mapping was NULL + @retval EFI_INVALID_PARAMETER Mapping did not end with a ':' + @retval EFI_INVALID_PARAMETER DevicePath was not pointing at a device that had a SIMPLE_FILE_SYSTEM_PROTOCOL installed. + @retval EFI_NOT_FOUND There was no mapping found to delete + @retval EFI_OUT_OF_RESOURCES Memory allocation failed +**/ +EFI_STATUS +EFIAPI +EfiShellSetMap ( + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL, + IN CONST CHAR16 *Mapping + ) +{ + EFI_STATUS Status; + SHELL_MAP_LIST *MapListNode; + + if (Mapping == NULL) { + return (EFI_INVALID_PARAMETER); + } + + if (Mapping[StrLen (Mapping)-1] != ':') { + return (EFI_INVALID_PARAMETER); + } + + // + // Delete the mapping + // + if (DevicePath == NULL) { + if (IsListEmpty (&gShellMapList.Link)) { + return (EFI_NOT_FOUND); + } + + for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link) + ; !IsNull (&gShellMapList.Link, &MapListNode->Link) + ; MapListNode = (SHELL_MAP_LIST *)GetNextNode (&gShellMapList.Link, &MapListNode->Link) + ) + { + if (StringNoCaseCompare (&MapListNode->MapName, &Mapping) == 0) { + RemoveEntryList (&MapListNode->Link); + SHELL_FREE_NON_NULL (MapListNode->DevicePath); + SHELL_FREE_NON_NULL (MapListNode->MapName); + SHELL_FREE_NON_NULL (MapListNode->CurrentDirectoryPath); + FreePool (MapListNode); + return (EFI_SUCCESS); + } + } // for loop + + // + // We didn't find one to delete + // + return (EFI_NOT_FOUND); + } + + // + // make sure this is a valid to add device path + // + /// @todo add BlockIo to this test... + if ( !InternalShellProtocolIsSimpleFileSystemPresent (DevicePath) + && !InternalShellProtocolIsBlockIoPresent (DevicePath)) + { + return (EFI_INVALID_PARAMETER); + } + + // + // First make sure there is no old mapping + // + Status = EfiShellSetMap (NULL, Mapping); + if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_FOUND)) { + return (Status); + } + + // + // now add the new one. + // + Status = ShellCommandAddMapItemAndUpdatePath (Mapping, DevicePath, 0, FALSE); + + return (Status); +} + +/** + Gets the device path from the mapping. + + This function gets the device path associated with a mapping. + + @param Mapping A pointer to the mapping + + @retval !=NULL Pointer to the device path that corresponds to the + device mapping. The returned pointer does not need + to be freed. + @retval NULL There is no device path associated with the + specified mapping. +**/ +CONST EFI_DEVICE_PATH_PROTOCOL * +EFIAPI +EfiShellGetDevicePathFromMap ( + IN CONST CHAR16 *Mapping + ) +{ + SHELL_MAP_LIST *MapListItem; + CHAR16 *NewName; + UINTN Size; + + NewName = NULL; + Size = 0; + + StrnCatGrow (&NewName, &Size, Mapping, 0); + if (Mapping[StrLen (Mapping)-1] != L':') { + StrnCatGrow (&NewName, &Size, L":", 0); + } + + MapListItem = ShellCommandFindMapItem (NewName); + + FreePool (NewName); + + if (MapListItem != NULL) { + return (MapListItem->DevicePath); + } + + return (NULL); +} + +/** + Gets the mapping(s) that most closely matches the device path. + + This function gets the mapping which corresponds to the device path *DevicePath. If + there is no exact match, then the mapping which most closely matches *DevicePath + is returned, and *DevicePath is updated to point to the remaining portion of the + device path. If there is an exact match, the mapping is returned and *DevicePath + points to the end-of-device-path node. + + If there are multiple map names they will be semi-colon separated in the + NULL-terminated string. + + @param DevicePath On entry, points to a device path pointer. On + exit, updates the pointer to point to the + portion of the device path after the mapping. + + @retval NULL No mapping was found. + @return !=NULL Pointer to NULL-terminated mapping. The buffer + is callee allocated and should be freed by the caller. +**/ +CONST CHAR16 * +EFIAPI +EfiShellGetMapFromDevicePath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + SHELL_MAP_LIST *Node; + CHAR16 *PathForReturn; + UINTN PathSize; + + // EFI_HANDLE PathHandle; + // EFI_HANDLE MapHandle; + // EFI_STATUS Status; + // EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy; + // EFI_DEVICE_PATH_PROTOCOL *MapPathCopy; + + if ((DevicePath == NULL) || (*DevicePath == NULL)) { + return (NULL); + } + + PathForReturn = NULL; + PathSize = 0; + + for ( Node = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link) + ; !IsNull (&gShellMapList.Link, &Node->Link) + ; Node = (SHELL_MAP_LIST *)GetNextNode (&gShellMapList.Link, &Node->Link) + ) + { + // + // check for exact match + // + if (DevicePathCompare (DevicePath, &Node->DevicePath) == 0) { + ASSERT ((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL)); + if (PathSize != 0) { + PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, L";", 0); + } + + PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, Node->MapName, 0); + } + } + + if (PathForReturn != NULL) { + while (!IsDevicePathEndType (*DevicePath)) { + *DevicePath = NextDevicePathNode (*DevicePath); + } + + // + // Do not call SetDevicePathEndNode() if the device path node is already the + // end of an entire device path. + // + if (!IsDevicePathEnd (*DevicePath)) { + SetDevicePathEndNode (*DevicePath); + } + } + + /* + ///@todo finish code for inexact matches. + if (PathForReturn == NULL) { + PathSize = 0; + + DevicePathCopy = DuplicateDevicePath(*DevicePath); + ASSERT(DevicePathCopy != NULL); + Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle); + ASSERT_EFI_ERROR(Status); + // + // check each of the device paths we have to get the root of the path for consist mappings + // + for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) + ; !IsNull(&gShellMapList.Link, &Node->Link) + ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link) + ){ + if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) == 0) { + continue; + } + MapPathCopy = DuplicateDevicePath(Node->DevicePath); + ASSERT(MapPathCopy != NULL); + Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle); + if (MapHandle == PathHandle) { + + *DevicePath = DevicePathCopy; + + MapPathCopy = NULL; + DevicePathCopy = NULL; + PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0); + PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0); + break; + } + } + // + // now add on the non-consistent mappings + // + for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) + ; !IsNull(&gShellMapList.Link, &Node->Link) + ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link) + ){ + if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) != 0) { + continue; + } + MapPathCopy = Node->DevicePath; + ASSERT(MapPathCopy != NULL); + Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle); + if (MapHandle == PathHandle) { + PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0); + PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0); + break; + } + } + } + */ + + return (AddBufferToFreeList (PathForReturn)); +} + +/** + Converts a device path to a file system-style path. + + This function converts a device path to a file system path by replacing part, or all, of + the device path with the file-system mapping. If there are more than one application + file system mappings, the one that most closely matches Path will be used. + + @param Path The pointer to the device path + + @retval NULL the device path could not be found. + @return all The pointer of the NULL-terminated file path. The path + is callee-allocated and should be freed by the caller. +**/ +CHAR16 * +EFIAPI +EfiShellGetFilePathFromDevicePath ( + IN CONST EFI_DEVICE_PATH_PROTOCOL *Path + ) +{ + EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy; + EFI_DEVICE_PATH_PROTOCOL *MapPathCopy; + SHELL_MAP_LIST *MapListItem; + CHAR16 *PathForReturn; + UINTN PathSize; + EFI_HANDLE PathHandle; + EFI_HANDLE MapHandle; + EFI_STATUS Status; + FILEPATH_DEVICE_PATH *FilePath; + FILEPATH_DEVICE_PATH *AlignedNode; + + PathForReturn = NULL; + PathSize = 0; + + DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL *)Path; + ASSERT (DevicePathCopy != NULL); + if (DevicePathCopy == NULL) { + return (NULL); + } + + /// @todo BlockIo? + Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle); + + if (EFI_ERROR (Status)) { + return (NULL); + } + + // + // check each of the device paths we have to get the root of the path + // + for ( MapListItem = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link) + ; !IsNull (&gShellMapList.Link, &MapListItem->Link) + ; MapListItem = (SHELL_MAP_LIST *)GetNextNode (&gShellMapList.Link, &MapListItem->Link) + ) + { + MapPathCopy = (EFI_DEVICE_PATH_PROTOCOL *)MapListItem->DevicePath; + ASSERT (MapPathCopy != NULL); + /// @todo BlockIo? + Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle); + if (MapHandle == PathHandle) { + ASSERT ((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL)); + PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, MapListItem->MapName, 0); + // + // go through all the remaining nodes in the device path + // + for ( FilePath = (FILEPATH_DEVICE_PATH *)DevicePathCopy + ; !IsDevicePathEnd (&FilePath->Header) + ; FilePath = (FILEPATH_DEVICE_PATH *)NextDevicePathNode (&FilePath->Header) + ) + { + // + // If any node is not a file path node, then the conversion can not be completed + // + if ((DevicePathType (&FilePath->Header) != MEDIA_DEVICE_PATH) || + (DevicePathSubType (&FilePath->Header) != MEDIA_FILEPATH_DP)) + { + FreePool (PathForReturn); + return NULL; + } + + // + // append the path part onto the filepath. + // + ASSERT ((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL)); + + AlignedNode = AllocateCopyPool (DevicePathNodeLength (FilePath), FilePath); + if (AlignedNode == NULL) { + FreePool (PathForReturn); + return NULL; + } + + // File Path Device Path Nodes 'can optionally add a "\" separator to + // the beginning and/or the end of the Path Name string.' + // (UEFI Spec 2.4 section 9.3.6.4). + // If necessary, add a "\", but otherwise don't + // (This is specified in the above section, and also implied by the + // UEFI Shell spec section 3.7) + if ((PathSize != 0) && + (PathForReturn != NULL) && + (PathForReturn[PathSize / sizeof (CHAR16) - 1] != L'\\') && + (AlignedNode->PathName[0] != L'\\')) + { + PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, L"\\", 1); + } + + PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, AlignedNode->PathName, 0); + FreePool (AlignedNode); + } // for loop of remaining nodes + } + + if (PathForReturn != NULL) { + break; + } + } // for loop of paths to check + + return (PathForReturn); +} + +/** + Converts a file system style name to a device path. + + This function converts a file system style name to a device path, by replacing any + mapping references to the associated device path. + + @param[in] Path The pointer to the path. + + @return The pointer of the file path. The file path is callee + allocated and should be freed by the caller. + @retval NULL The path could not be found. + @retval NULL There was not enough available memory. +**/ +EFI_DEVICE_PATH_PROTOCOL * +EFIAPI +EfiShellGetDevicePathFromFilePath ( + IN CONST CHAR16 *Path + ) +{ + CHAR16 *MapName; + CHAR16 *NewPath; + CONST CHAR16 *Cwd; + UINTN Size; + CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy; + EFI_DEVICE_PATH_PROTOCOL *DevicePathCopyForFree; + EFI_DEVICE_PATH_PROTOCOL *DevicePathForReturn; + EFI_HANDLE Handle; + EFI_STATUS Status; + + if (Path == NULL) { + return (NULL); + } + + MapName = NULL; + NewPath = NULL; + + if (StrStr (Path, L":") == NULL) { + Cwd = EfiShellGetCurDir (NULL); + if (Cwd == NULL) { + return (NULL); + } + + Size = StrSize (Cwd) + StrSize (Path); + NewPath = AllocateZeroPool (Size); + if (NewPath == NULL) { + return (NULL); + } + + StrCpyS (NewPath, Size/sizeof (CHAR16), Cwd); + StrCatS (NewPath, Size/sizeof (CHAR16), L"\\"); + if (*Path == L'\\') { + Path++; + while (PathRemoveLastItem (NewPath)) { + } + } + + StrCatS (NewPath, Size/sizeof (CHAR16), Path); + DevicePathForReturn = EfiShellGetDevicePathFromFilePath (NewPath); + FreePool (NewPath); + return (DevicePathForReturn); + } + + Size = 0; + // + // find the part before (but including) the : for the map name + // + ASSERT ((MapName == NULL && Size == 0) || (MapName != NULL)); + MapName = StrnCatGrow (&MapName, &Size, Path, (StrStr (Path, L":")-Path+1)); + if ((MapName == NULL) || (MapName[StrLen (MapName)-1] != L':')) { + return (NULL); + } + + // + // look up the device path in the map + // + DevicePath = EfiShellGetDevicePathFromMap (MapName); + if (DevicePath == NULL) { + // + // Must have been a bad Mapname + // + return (NULL); + } + + // + // make a copy for LocateDevicePath to modify (also save a pointer to call FreePool with) + // + DevicePathCopyForFree = DevicePathCopy = DuplicateDevicePath (DevicePath); + if (DevicePathCopy == NULL) { + FreePool (MapName); + return (NULL); + } + + // + // get the handle + // + /// @todo BlockIo? + Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle); + if (EFI_ERROR (Status)) { + if (DevicePathCopyForFree != NULL) { + FreePool (DevicePathCopyForFree); + } + + FreePool (MapName); + return (NULL); + } + + // + // build the full device path + // + if ((*(Path+StrLen (MapName)) != CHAR_NULL) && + (*(Path+StrLen (MapName)+1) == CHAR_NULL)) + { + DevicePathForReturn = FileDevicePath (Handle, L"\\"); + } else { + DevicePathForReturn = FileDevicePath (Handle, Path+StrLen (MapName)); + } + + FreePool (MapName); + if (DevicePathCopyForFree != NULL) { + FreePool (DevicePathCopyForFree); + } + + return (DevicePathForReturn); +} + +/** + Gets the name of the device specified by the device handle. + + This function gets the user-readable name of the device specified by the device + handle. If no user-readable name could be generated, then *BestDeviceName will be + NULL and EFI_NOT_FOUND will be returned. + + If EFI_DEVICE_NAME_USE_COMPONENT_NAME is set, then the function will return the + device's name using the EFI_COMPONENT_NAME2_PROTOCOL, if present on + DeviceHandle. + + If EFI_DEVICE_NAME_USE_DEVICE_PATH is set, then the function will return the + device's name using the EFI_DEVICE_PATH_PROTOCOL, if present on DeviceHandle. + If both EFI_DEVICE_NAME_USE_COMPONENT_NAME and + EFI_DEVICE_NAME_USE_DEVICE_PATH are set, then + EFI_DEVICE_NAME_USE_COMPONENT_NAME will have higher priority. + + @param DeviceHandle The handle of the device. + @param Flags Determines the possible sources of component names. + Valid bits are: + EFI_DEVICE_NAME_USE_COMPONENT_NAME + EFI_DEVICE_NAME_USE_DEVICE_PATH + @param Language A pointer to the language specified for the device + name, in the same format as described in the UEFI + specification, Appendix M + @param BestDeviceName On return, points to the callee-allocated NULL- + terminated name of the device. If no device name + could be found, points to NULL. The name must be + freed by the caller... + + @retval EFI_SUCCESS Get the name successfully. + @retval EFI_NOT_FOUND Fail to get the device name. + @retval EFI_INVALID_PARAMETER Flags did not have a valid bit set. + @retval EFI_INVALID_PARAMETER BestDeviceName was NULL + @retval EFI_INVALID_PARAMETER DeviceHandle was NULL +**/ +EFI_STATUS +EFIAPI +EfiShellGetDeviceName ( + IN EFI_HANDLE DeviceHandle, + IN EFI_SHELL_DEVICE_NAME_FLAGS Flags, + IN CHAR8 *Language, + OUT CHAR16 **BestDeviceName + ) +{ + EFI_STATUS Status; + EFI_COMPONENT_NAME2_PROTOCOL *CompName2; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_HANDLE *HandleList; + UINTN HandleCount; + UINTN LoopVar; + CHAR16 *DeviceNameToReturn; + CHAR8 *Lang; + UINTN ParentControllerCount; + EFI_HANDLE *ParentControllerBuffer; + UINTN ParentDriverCount; + EFI_HANDLE *ParentDriverBuffer; + + if ((BestDeviceName == NULL) || + (DeviceHandle == NULL) + ) + { + return (EFI_INVALID_PARAMETER); + } + + // + // make sure one of the 2 supported bits is on + // + if (((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) == 0) && + ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) == 0)) + { + return (EFI_INVALID_PARAMETER); + } + + DeviceNameToReturn = NULL; + *BestDeviceName = NULL; + HandleList = NULL; + HandleCount = 0; + Lang = NULL; + + if ((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) != 0) { + Status = ParseHandleDatabaseByRelationship ( + NULL, + DeviceHandle, + HR_DRIVER_BINDING_HANDLE|HR_DEVICE_DRIVER, + &HandleCount, + &HandleList + ); + for (LoopVar = 0; LoopVar < HandleCount; LoopVar++) { + // + // Go through those handles until we get one that passes for GetComponentName + // + Status = gBS->OpenProtocol ( + HandleList[LoopVar], + &gEfiComponentName2ProtocolGuid, + (VOID **)&CompName2, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + HandleList[LoopVar], + &gEfiComponentNameProtocolGuid, + (VOID **)&CompName2, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + } + + if (EFI_ERROR (Status)) { + continue; + } + + Lang = GetBestLanguageForDriver (CompName2->SupportedLanguages, Language, FALSE); + Status = CompName2->GetControllerName (CompName2, DeviceHandle, NULL, Lang, &DeviceNameToReturn); + FreePool (Lang); + Lang = NULL; + if (!EFI_ERROR (Status) && (DeviceNameToReturn != NULL)) { + break; + } + } + + if (HandleList != NULL) { + FreePool (HandleList); + } + + // + // Now check the parent controller using this as the child. + // + Status = PARSE_HANDLE_DATABASE_PARENTS (DeviceHandle, &ParentControllerCount, &ParentControllerBuffer); + if ((DeviceNameToReturn == NULL) && !EFI_ERROR (Status)) { + for (LoopVar = 0; LoopVar < ParentControllerCount; LoopVar++) { + Status = PARSE_HANDLE_DATABASE_UEFI_DRIVERS (ParentControllerBuffer[LoopVar], &ParentDriverCount, &ParentDriverBuffer); + if (!EFI_ERROR (Status)) { + for (HandleCount = 0; HandleCount < ParentDriverCount; HandleCount++) { + // + // try using that driver's component name with controller and our driver as the child. + // + Status = gBS->OpenProtocol ( + ParentDriverBuffer[HandleCount], + &gEfiComponentName2ProtocolGuid, + (VOID **)&CompName2, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + ParentDriverBuffer[HandleCount], + &gEfiComponentNameProtocolGuid, + (VOID **)&CompName2, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + } + + if (EFI_ERROR (Status)) { + continue; + } + + Lang = GetBestLanguageForDriver (CompName2->SupportedLanguages, Language, FALSE); + Status = CompName2->GetControllerName (CompName2, ParentControllerBuffer[LoopVar], DeviceHandle, Lang, &DeviceNameToReturn); + FreePool (Lang); + Lang = NULL; + if (!EFI_ERROR (Status) && (DeviceNameToReturn != NULL)) { + break; + } + } + + SHELL_FREE_NON_NULL (ParentDriverBuffer); + if (!EFI_ERROR (Status) && (DeviceNameToReturn != NULL)) { + break; + } + } + } + + SHELL_FREE_NON_NULL (ParentControllerBuffer); + } + + // + // dont return on fail since we will try device path if that bit is on + // + if (DeviceNameToReturn != NULL) { + ASSERT (BestDeviceName != NULL); + StrnCatGrow (BestDeviceName, NULL, DeviceNameToReturn, 0); + return (EFI_SUCCESS); + } + } + + if ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) != 0) { + Status = gBS->OpenProtocol ( + DeviceHandle, + &gEfiDevicePathProtocolGuid, + (VOID **)&DevicePath, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + // + // use device path to text on the device path + // + *BestDeviceName = ConvertDevicePathToText (DevicePath, TRUE, TRUE); + return (EFI_SUCCESS); + } + } + + // + // none of the selected bits worked. + // + return (EFI_NOT_FOUND); +} + +/** + Opens the root directory of a device on a handle + + This function opens the root directory of a device and returns a file handle to it. + + @param DeviceHandle The handle of the device that contains the volume. + @param FileHandle On exit, points to the file handle corresponding to the root directory on the + device. + + @retval EFI_SUCCESS Root opened successfully. + @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory + could not be opened. + @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted. + @retval EFI_DEVICE_ERROR The device had an error. + @retval Others Error status returned from EFI_SIMPLE_FILE_SYSTEM_PROTOCOL->OpenVolume(). +**/ +EFI_STATUS +EFIAPI +EfiShellOpenRootByHandle ( + IN EFI_HANDLE DeviceHandle, + OUT SHELL_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem; + EFI_FILE_PROTOCOL *RealFileHandle; + EFI_DEVICE_PATH_PROTOCOL *DevPath; + + // + // get the simple file system interface + // + Status = gBS->OpenProtocol ( + DeviceHandle, + &gEfiSimpleFileSystemProtocolGuid, + (VOID **)&SimpleFileSystem, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return (EFI_NOT_FOUND); + } + + Status = gBS->OpenProtocol ( + DeviceHandle, + &gEfiDevicePathProtocolGuid, + (VOID **)&DevPath, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return (EFI_NOT_FOUND); + } + + // + // Open the root volume now... + // + Status = SimpleFileSystem->OpenVolume (SimpleFileSystem, &RealFileHandle); + if (EFI_ERROR (Status)) { + return Status; + } + + *FileHandle = ConvertEfiFileProtocolToShellHandle (RealFileHandle, EfiShellGetMapFromDevicePath (&DevPath)); + return (EFI_SUCCESS); +} + +/** + Opens the root directory of a device. + + This function opens the root directory of a device and returns a file handle to it. + + @param DevicePath Points to the device path corresponding to the device where the + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL is installed. + @param FileHandle On exit, points to the file handle corresponding to the root directory on the + device. + + @retval EFI_SUCCESS Root opened successfully. + @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory + could not be opened. + @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted. + @retval EFI_DEVICE_ERROR The device had an error + @retval EFI_INVALID_PARAMETER FileHandle is NULL. +**/ +EFI_STATUS +EFIAPI +EfiShellOpenRoot ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT SHELL_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + + if (FileHandle == NULL) { + return (EFI_INVALID_PARAMETER); + } + + // + // find the handle of the device with that device handle and the file system + // + /// @todo BlockIo? + Status = gBS->LocateDevicePath ( + &gEfiSimpleFileSystemProtocolGuid, + &DevicePath, + &Handle + ); + if (EFI_ERROR (Status)) { + return (EFI_NOT_FOUND); + } + + return (EfiShellOpenRootByHandle (Handle, FileHandle)); +} + +/** + Returns whether any script files are currently being processed. + + @retval TRUE There is at least one script file active. + @retval FALSE No script files are active now. + +**/ +BOOLEAN +EFIAPI +EfiShellBatchIsActive ( + VOID + ) +{ + if (ShellCommandGetCurrentScriptFile () == NULL) { + return (FALSE); + } + + return (TRUE); +} + +/** + Worker function to open a file based on a device path. this will open the root + of the volume and then traverse down to the file itself. + + @param DevicePath Device Path of the file. + @param FileHandle Pointer to the file upon a successful return. + @param OpenMode mode to open file in. + @param Attributes the File Attributes to use when creating a new file. + + @retval EFI_SUCCESS the file is open and FileHandle is valid + @retval EFI_UNSUPPORTED the device path contained non-path elements + @retval other an error occurred. +**/ +EFI_STATUS +InternalOpenFileDevicePath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT SHELL_FILE_HANDLE *FileHandle, + IN UINT64 OpenMode, + IN UINT64 Attributes OPTIONAL + ) +{ + EFI_STATUS Status; + FILEPATH_DEVICE_PATH *FilePathNode; + EFI_HANDLE Handle; + SHELL_FILE_HANDLE ShellHandle; + EFI_FILE_PROTOCOL *Handle1; + EFI_FILE_PROTOCOL *Handle2; + FILEPATH_DEVICE_PATH *AlignedNode; + + if (FileHandle == NULL) { + return (EFI_INVALID_PARAMETER); + } + + *FileHandle = NULL; + Handle1 = NULL; + Handle2 = NULL; + Handle = NULL; + ShellHandle = NULL; + FilePathNode = NULL; + AlignedNode = NULL; + + Status = EfiShellOpenRoot (DevicePath, &ShellHandle); + + if (!EFI_ERROR (Status)) { + Handle1 = ConvertShellHandleToEfiFileProtocol (ShellHandle); + if (Handle1 != NULL) { + // + // chop off the beginning part before the file system part... + // + /// @todo BlockIo? + Status = gBS->LocateDevicePath ( + &gEfiSimpleFileSystemProtocolGuid, + &DevicePath, + &Handle + ); + if (!EFI_ERROR (Status)) { + // + // To access as a file system, the file path should only + // contain file path components. Follow the file path nodes + // and find the target file + // + for ( FilePathNode = (FILEPATH_DEVICE_PATH *)DevicePath + ; !IsDevicePathEnd (&FilePathNode->Header) + ; FilePathNode = (FILEPATH_DEVICE_PATH *)NextDevicePathNode (&FilePathNode->Header) + ) + { + SHELL_FREE_NON_NULL (AlignedNode); + AlignedNode = AllocateCopyPool (DevicePathNodeLength (FilePathNode), FilePathNode); + // + // For file system access each node should be a file path component + // + if ((DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH) || + (DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP) + ) + { + Status = EFI_UNSUPPORTED; + break; + } + + // + // Open this file path node + // + Handle2 = Handle1; + Handle1 = NULL; + + // + // if this is the last node in the DevicePath always create (if that was requested). + // + if (IsDevicePathEnd ((NextDevicePathNode (&FilePathNode->Header)))) { + Status = Handle2->Open ( + Handle2, + &Handle1, + AlignedNode->PathName, + OpenMode, + Attributes + ); + } else { + // + // This is not the last node and we dont want to 'create' existing + // directory entries... + // + + // + // open without letting it create + // prevents error on existing files/directories + // + Status = Handle2->Open ( + Handle2, + &Handle1, + AlignedNode->PathName, + OpenMode &~EFI_FILE_MODE_CREATE, + Attributes + ); + // + // if above failed now open and create the 'item' + // if OpenMode EFI_FILE_MODE_CREATE bit was on (but disabled above) + // + if ((EFI_ERROR (Status)) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) { + Status = Handle2->Open ( + Handle2, + &Handle1, + AlignedNode->PathName, + OpenMode, + Attributes + ); + } + } + + // + // Close the last node + // + ShellInfoObject.NewEfiShellProtocol->CloseFile (Handle2); + + // + // If there's been an error, stop + // + if (EFI_ERROR (Status)) { + break; + } + } // for loop + } + } + } + + SHELL_FREE_NON_NULL (AlignedNode); + if (EFI_ERROR (Status)) { + if (Handle1 != NULL) { + ShellInfoObject.NewEfiShellProtocol->CloseFile (Handle1); + } + } else { + *FileHandle = ConvertEfiFileProtocolToShellHandle (Handle1, ShellFileHandleGetPath (ShellHandle)); + } + + return (Status); +} + +/** + Creates a file or directory by name. + + This function creates an empty new file or directory with the specified attributes and + returns the new file's handle. If the file already exists and is read-only, then + EFI_INVALID_PARAMETER will be returned. + + If the file already existed, it is truncated and its attributes updated. If the file is + created successfully, the FileHandle is the file's handle, else, the FileHandle is NULL. + + If the file name begins with >v, then the file handle which is returned refers to the + shell environment variable with the specified name. If the shell environment variable + already exists and is non-volatile then EFI_INVALID_PARAMETER is returned. + + @param FileName Pointer to NULL-terminated file path + @param FileAttribs The new file's attributes. the different attributes are + described in EFI_FILE_PROTOCOL.Open(). + @param FileHandle On return, points to the created file handle or directory's handle + + @retval EFI_SUCCESS The file was opened. FileHandle points to the new file's handle. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + @retval EFI_UNSUPPORTED could not open the file path + @retval EFI_NOT_FOUND the specified file could not be found on the device, or could not + file the file system on the device. + @retval EFI_NO_MEDIA the device has no medium. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no + longer supported. + @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according + the DirName. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write + when the media is write-protected. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. + @retval EFI_VOLUME_FULL The volume is full. +**/ +EFI_STATUS +EFIAPI +EfiShellCreateFile ( + IN CONST CHAR16 *FileName, + IN UINT64 FileAttribs, + OUT SHELL_FILE_HANDLE *FileHandle + ) +{ + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_STATUS Status; + BOOLEAN Volatile; + + // + // Is this for an environment variable + // do we start with >v + // + if (StrStr (FileName, L">v") == FileName) { + Status = IsVolatileEnv (FileName + 2, &Volatile); + if (EFI_ERROR (Status)) { + return Status; + } + + if (!Volatile) { + return (EFI_INVALID_PARAMETER); + } + + *FileHandle = CreateFileInterfaceEnv (FileName+2); + return (EFI_SUCCESS); + } + + // + // We are opening a regular file. + // + DevicePath = EfiShellGetDevicePathFromFilePath (FileName); + if (DevicePath == NULL) { + return (EFI_NOT_FOUND); + } + + Status = InternalOpenFileDevicePath (DevicePath, FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, FileAttribs); + FreePool (DevicePath); + + return (Status); +} + +/** + Register a GUID and a localized human readable name for it. + + If Guid is not assigned a name, then assign GuidName to Guid. This list of GUID + names must be used whenever a shell command outputs GUID information. + + This function is only available when the major and minor versions in the + EfiShellProtocol are greater than or equal to 2 and 1, respectively. + + @param[in] Guid A pointer to the GUID being registered. + @param[in] GuidName A pointer to the localized name for the GUID being registered. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_INVALID_PARAMETER Guid was NULL. + @retval EFI_INVALID_PARAMETER GuidName was NULL. + @retval EFI_ACCESS_DENIED Guid already is assigned a name. +**/ +EFI_STATUS +EFIAPI +EfiShellRegisterGuidName ( + IN CONST EFI_GUID *Guid, + IN CONST CHAR16 *GuidName + ) +{ + return (AddNewGuidNameMapping (Guid, GuidName, NULL)); +} + +/** + Opens a file or a directory by file name. + + This function opens the specified file in the specified OpenMode and returns a file + handle. + If the file name begins with >v, then the file handle which is returned refers to the + shell environment variable with the specified name. If the shell environment variable + exists, is non-volatile and the OpenMode indicates EFI_FILE_MODE_WRITE, then + EFI_INVALID_PARAMETER is returned. + + If the file name is >i, then the file handle which is returned refers to the standard + input. If the OpenMode indicates EFI_FILE_MODE_WRITE, then EFI_INVALID_PARAMETER + is returned. + + If the file name is >o, then the file handle which is returned refers to the standard + output. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER + is returned. + + If the file name is >e, then the file handle which is returned refers to the standard + error. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER + is returned. + + If the file name is NUL, then the file handle that is returned refers to the standard NUL + file. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER is + returned. + + If return EFI_SUCCESS, the FileHandle is the opened file's handle, else, the + FileHandle is NULL. + + @param FileName Points to the NULL-terminated UCS-2 encoded file name. + @param FileHandle On return, points to the file handle. + @param OpenMode File open mode. Either EFI_FILE_MODE_READ or + EFI_FILE_MODE_WRITE from section 12.4 of the UEFI + Specification. + @retval EFI_SUCCESS The file was opened. FileHandle has the opened file's handle. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. FileHandle is NULL. + @retval EFI_UNSUPPORTED Could not open the file path. FileHandle is NULL. + @retval EFI_NOT_FOUND The specified file could not be found on the device or the file + system could not be found on the device. FileHandle is NULL. + @retval EFI_NO_MEDIA The device has no medium. FileHandle is NULL. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no + longer supported. FileHandle is NULL. + @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according + the FileName. FileHandle is NULL. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. FileHandle is NULL. + @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write + when the media is write-protected. FileHandle is NULL. + @retval EFI_ACCESS_DENIED The service denied access to the file. FileHandle is NULL. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. FileHandle + is NULL. + @retval EFI_VOLUME_FULL The volume is full. FileHandle is NULL. +**/ +EFI_STATUS +EFIAPI +EfiShellOpenFileByName ( + IN CONST CHAR16 *FileName, + OUT SHELL_FILE_HANDLE *FileHandle, + IN UINT64 OpenMode + ) +{ + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_STATUS Status; + BOOLEAN Volatile; + + *FileHandle = NULL; + + // + // Is this for StdIn + // + if (StrCmp (FileName, L">i") == 0) { + // + // make sure not writing to StdIn + // + if ((OpenMode & EFI_FILE_MODE_WRITE) != 0) { + return (EFI_INVALID_PARAMETER); + } + + *FileHandle = ShellInfoObject.NewShellParametersProtocol->StdIn; + ASSERT (*FileHandle != NULL); + return (EFI_SUCCESS); + } + + // + // Is this for StdOut + // + if (StrCmp (FileName, L">o") == 0) { + // + // make sure not writing to StdIn + // + if ((OpenMode & EFI_FILE_MODE_READ) != 0) { + return (EFI_INVALID_PARAMETER); + } + + *FileHandle = &FileInterfaceStdOut; + return (EFI_SUCCESS); + } + + // + // Is this for NUL / NULL file + // + if ((gUnicodeCollation->StriColl (gUnicodeCollation, (CHAR16 *)FileName, L"NUL") == 0) || + (gUnicodeCollation->StriColl (gUnicodeCollation, (CHAR16 *)FileName, L"NULL") == 0)) + { + *FileHandle = &FileInterfaceNulFile; + return (EFI_SUCCESS); + } + + // + // Is this for StdErr + // + if (StrCmp (FileName, L">e") == 0) { + // + // make sure not writing to StdIn + // + if ((OpenMode & EFI_FILE_MODE_READ) != 0) { + return (EFI_INVALID_PARAMETER); + } + + *FileHandle = &FileInterfaceStdErr; + return (EFI_SUCCESS); + } + + // + // Is this for an environment variable + // do we start with >v + // + if (StrStr (FileName, L">v") == FileName) { + Status = IsVolatileEnv (FileName + 2, &Volatile); + if (EFI_ERROR (Status)) { + return Status; + } + + if (!Volatile && + ((OpenMode & EFI_FILE_MODE_WRITE) != 0)) + { + return (EFI_INVALID_PARAMETER); + } + + *FileHandle = CreateFileInterfaceEnv (FileName+2); + return (EFI_SUCCESS); + } + + // + // We are opening a regular file. + // + DevicePath = EfiShellGetDevicePathFromFilePath (FileName); + + if (DevicePath == NULL) { + return (EFI_NOT_FOUND); + } + + // + // Copy the device path, open the file, then free the memory + // + Status = InternalOpenFileDevicePath (DevicePath, FileHandle, OpenMode, 0); // 0 = no specific file attributes + FreePool (DevicePath); + + return (Status); +} + +/** + Deletes the file specified by the file name. + + This function deletes a file. + + @param FileName Points to the NULL-terminated file name. + + @retval EFI_SUCCESS The file was closed and deleted, and the handle was closed. + @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted. + @sa EfiShellCreateFile +**/ +EFI_STATUS +EFIAPI +EfiShellDeleteFileByName ( + IN CONST CHAR16 *FileName + ) +{ + SHELL_FILE_HANDLE FileHandle; + EFI_STATUS Status; + + FileHandle = NULL; + + // + // get a handle to the file + // + Status = EfiShellCreateFile ( + FileName, + 0, + &FileHandle + ); + if (EFI_ERROR (Status)) { + return (Status); + } + + // + // now delete the file + // + ShellFileHandleRemove (FileHandle); + return (ShellInfoObject.NewEfiShellProtocol->DeleteFile (FileHandle)); +} + +/** + Disables the page break output mode. +**/ +VOID +EFIAPI +EfiShellDisablePageBreak ( + VOID + ) +{ + ShellInfoObject.PageBreakEnabled = FALSE; +} + +/** + Enables the page break output mode. +**/ +VOID +EFIAPI +EfiShellEnablePageBreak ( + VOID + ) +{ + ShellInfoObject.PageBreakEnabled = TRUE; +} + +/** + internal worker function to load and run an image via device path. + + @param ParentImageHandle A handle of the image that is executing the specified + command line. + @param DevicePath device path of the file to execute + @param CommandLine Points to the NULL-terminated UCS-2 encoded string + containing the command line. If NULL then the command- + line will be empty. + @param Environment Points to a NULL-terminated array of environment + variables with the format 'x=y', where x is the + environment variable name and y is the value. If this + is NULL, then the current shell environment is used. + + @param[out] StartImageStatus Returned status from gBS->StartImage. + + @retval EFI_SUCCESS The command executed successfully. The status code + returned by the command is pointed to by StatusCode. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Out of resources. + @retval EFI_UNSUPPORTED Nested shell invocations are not allowed. +**/ +EFI_STATUS +InternalShellExecuteDevicePath ( + IN CONST EFI_HANDLE *ParentImageHandle, + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN CONST CHAR16 *CommandLine OPTIONAL, + IN CONST CHAR16 **Environment OPTIONAL, + OUT EFI_STATUS *StartImageStatus OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_STATUS StartStatus; + EFI_STATUS CleanupStatus; + EFI_HANDLE NewHandle; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + LIST_ENTRY OrigEnvs; + EFI_SHELL_PARAMETERS_PROTOCOL ShellParamsProtocol; + CHAR16 *ImagePath; + UINTN Index; + CHAR16 *Walker; + CHAR16 *NewCmdLine; + + if (ParentImageHandle == NULL) { + return (EFI_INVALID_PARAMETER); + } + + InitializeListHead (&OrigEnvs); + ZeroMem (&ShellParamsProtocol, sizeof (EFI_SHELL_PARAMETERS_PROTOCOL)); + + NewHandle = NULL; + + NewCmdLine = AllocateCopyPool (StrSize (CommandLine), CommandLine); + if (NewCmdLine == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL; Walker++) { + if ((*Walker == L'^') && (*(Walker+1) == L'#')) { + CopyMem (Walker, Walker+1, StrSize (Walker) - sizeof (Walker[0])); + } + } + + // + // Load the image with: + // FALSE - not from boot manager and NULL, 0 being not already in memory + // + Status = gBS->LoadImage ( + FALSE, + *ParentImageHandle, + (EFI_DEVICE_PATH_PROTOCOL *)DevicePath, + NULL, + 0, + &NewHandle + ); + + if (EFI_ERROR (Status)) { + if (NewHandle != NULL) { + gBS->UnloadImage (NewHandle); + } + + FreePool (NewCmdLine); + return (Status); + } + + Status = gBS->OpenProtocol ( + NewHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + // + // If the image is not an app abort it. + // + if (LoadedImage->ImageCodeType != EfiLoaderCode) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_SHELL_IMAGE_NOT_APP), + ShellInfoObject.HiiHandle + ); + goto UnloadImage; + } + + ASSERT (LoadedImage->LoadOptionsSize == 0); + if (NewCmdLine != NULL) { + LoadedImage->LoadOptionsSize = (UINT32)StrSize (NewCmdLine); + LoadedImage->LoadOptions = (VOID *)NewCmdLine; + } + + // + // Save our current environment settings for later restoration if necessary + // + if (Environment != NULL) { + Status = GetEnvironmentVariableList (&OrigEnvs); + if (!EFI_ERROR (Status)) { + Status = SetEnvironmentVariables (Environment); + } + } + + // + // Initialize and install a shell parameters protocol on the image. + // + ShellParamsProtocol.StdIn = ShellInfoObject.NewShellParametersProtocol->StdIn; + ShellParamsProtocol.StdOut = ShellInfoObject.NewShellParametersProtocol->StdOut; + ShellParamsProtocol.StdErr = ShellInfoObject.NewShellParametersProtocol->StdErr; + Status = UpdateArgcArgv (&ShellParamsProtocol, NewCmdLine, Efi_Application, NULL, NULL); + if (EFI_ERROR (Status)) { + goto UnloadImage; + } + + // + // Replace Argv[0] with the full path of the binary we're executing: + // If the command line was "foo", the binary might be called "foo.efi". + // "The first entry in [Argv] is always the full file path of the + // executable" - UEFI Shell Spec section 2.3 + // + ImagePath = EfiShellGetFilePathFromDevicePath (DevicePath); + // The image we're executing isn't necessarily in a filesystem - it might + // be memory mapped. In this case EfiShellGetFilePathFromDevicePath will + // return NULL, and we'll leave Argv[0] as UpdateArgcArgv set it. + if (ImagePath != NULL) { + if (ShellParamsProtocol.Argv == NULL) { + // Command line was empty or null. + // (UpdateArgcArgv sets Argv to NULL when CommandLine is "" or NULL) + ShellParamsProtocol.Argv = AllocatePool (sizeof (CHAR16 *)); + if (ShellParamsProtocol.Argv == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto UnloadImage; + } + + ShellParamsProtocol.Argc = 1; + } else { + // Free the string UpdateArgcArgv put in Argv[0]; + FreePool (ShellParamsProtocol.Argv[0]); + } + + ShellParamsProtocol.Argv[0] = ImagePath; + } + + Status = gBS->InstallProtocolInterface (&NewHandle, &gEfiShellParametersProtocolGuid, EFI_NATIVE_INTERFACE, &ShellParamsProtocol); + ASSERT_EFI_ERROR (Status); + + /// @todo initialize and install ShellInterface protocol on the new image for compatibility if - PcdGetBool(PcdShellSupportOldProtocols) + + // + // now start the image and if the caller wanted the return code pass it to them... + // + if (!EFI_ERROR (Status)) { + StartStatus = gBS->StartImage ( + NewHandle, + 0, + NULL + ); + if (StartImageStatus != NULL) { + *StartImageStatus = StartStatus; + } + + CleanupStatus = gBS->UninstallProtocolInterface ( + NewHandle, + &gEfiShellParametersProtocolGuid, + &ShellParamsProtocol + ); + ASSERT_EFI_ERROR (CleanupStatus); + + goto FreeAlloc; + } + +UnloadImage: + // Unload image - We should only get here if we didn't call StartImage + gBS->UnloadImage (NewHandle); + +FreeAlloc: + // Free Argv (Allocated in UpdateArgcArgv) + if (ShellParamsProtocol.Argv != NULL) { + for (Index = 0; Index < ShellParamsProtocol.Argc; Index++) { + if (ShellParamsProtocol.Argv[Index] != NULL) { + FreePool (ShellParamsProtocol.Argv[Index]); + } + } + + FreePool (ShellParamsProtocol.Argv); + } + } + + // Restore environment variables + if (!IsListEmpty (&OrigEnvs)) { + CleanupStatus = SetEnvironmentVariableList (&OrigEnvs); + ASSERT_EFI_ERROR (CleanupStatus); + } + + FreePool (NewCmdLine); + + return (Status); +} + +/** + internal worker function to load and run an image in the current shell. + + @param CommandLine Points to the NULL-terminated UCS-2 encoded string + containing the command line. If NULL then the command- + line will be empty. + @param Environment Points to a NULL-terminated array of environment + variables with the format 'x=y', where x is the + environment variable name and y is the value. If this + is NULL, then the current shell environment is used. + + @param[out] StartImageStatus Returned status from the command line. + + @retval EFI_SUCCESS The command executed successfully. The status code + returned by the command is pointed to by StatusCode. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Out of resources. + @retval EFI_UNSUPPORTED Nested shell invocations are not allowed. +**/ +EFI_STATUS +InternalShellExecute ( + IN CONST CHAR16 *CommandLine OPTIONAL, + IN CONST CHAR16 **Environment OPTIONAL, + OUT EFI_STATUS *StartImageStatus OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_STATUS CleanupStatus; + LIST_ENTRY OrigEnvs; + + InitializeListHead (&OrigEnvs); + + // + // Save our current environment settings for later restoration if necessary + // + if (Environment != NULL) { + Status = GetEnvironmentVariableList (&OrigEnvs); + if (!EFI_ERROR (Status)) { + Status = SetEnvironmentVariables (Environment); + } else { + return Status; + } + } + + Status = RunShellCommand (CommandLine, StartImageStatus); + + // Restore environment variables + if (!IsListEmpty (&OrigEnvs)) { + CleanupStatus = SetEnvironmentVariableList (&OrigEnvs); + ASSERT_EFI_ERROR (CleanupStatus); + } + + return (Status); +} + +/** + Determine if the UEFI Shell is currently running with nesting enabled or disabled. + + @retval FALSE nesting is required + @retval other nesting is enabled +**/ +STATIC +BOOLEAN +NestingEnabled ( + VOID + ) +{ + EFI_STATUS Status; + CHAR16 *Temp; + CHAR16 *Temp2; + UINTN TempSize; + BOOLEAN RetVal; + + RetVal = TRUE; + Temp = NULL; + Temp2 = NULL; + + if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) { + TempSize = 0; + Temp = NULL; + Status = SHELL_GET_ENVIRONMENT_VARIABLE (mNoNestingEnvVarName, &TempSize, Temp); + if (Status == EFI_BUFFER_TOO_SMALL) { + Temp = AllocateZeroPool (TempSize + sizeof (CHAR16)); + if (Temp != NULL) { + Status = SHELL_GET_ENVIRONMENT_VARIABLE (mNoNestingEnvVarName, &TempSize, Temp); + } + } + + Temp2 = StrnCatGrow (&Temp2, NULL, mNoNestingTrue, 0); + if ((Temp != NULL) && (Temp2 != NULL) && (StringNoCaseCompare (&Temp, &Temp2) == 0)) { + // + // Use the no nesting method. + // + RetVal = FALSE; + } + } + + SHELL_FREE_NON_NULL (Temp); + SHELL_FREE_NON_NULL (Temp2); + return (RetVal); +} + +/** + Execute the command line. + + This function creates a nested instance of the shell and executes the specified + command (CommandLine) with the specified environment (Environment). Upon return, + the status code returned by the specified command is placed in StatusCode. + + If Environment is NULL, then the current environment is used and all changes made + by the commands executed will be reflected in the current environment. If the + Environment is non-NULL, then the changes made will be discarded. + + The CommandLine is executed from the current working directory on the current + device. + + @param ParentImageHandle A handle of the image that is executing the specified + command line. + @param CommandLine Points to the NULL-terminated UCS-2 encoded string + containing the command line. If NULL then the command- + line will be empty. + @param Environment Points to a NULL-terminated array of environment + variables with the format 'x=y', where x is the + environment variable name and y is the value. If this + is NULL, then the current shell environment is used. + @param StatusCode Points to the status code returned by the CommandLine. + + @retval EFI_SUCCESS The command executed successfully. The status code + returned by the command is pointed to by StatusCode. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Out of resources. + @retval EFI_UNSUPPORTED Nested shell invocations are not allowed. + @retval EFI_UNSUPPORTED The support level required for this function is not present. + + @sa InternalShellExecuteDevicePath +**/ +EFI_STATUS +EFIAPI +EfiShellExecute ( + IN EFI_HANDLE *ParentImageHandle, + IN CHAR16 *CommandLine OPTIONAL, + IN CHAR16 **Environment OPTIONAL, + OUT EFI_STATUS *StatusCode OPTIONAL + ) +{ + EFI_STATUS Status; + CHAR16 *Temp; + EFI_DEVICE_PATH_PROTOCOL *DevPath; + UINTN Size; + + if ((PcdGet8 (PcdShellSupportLevel) < 1)) { + return (EFI_UNSUPPORTED); + } + + if (NestingEnabled ()) { + DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath); + + DEBUG_CODE_BEGIN (); + Temp = ConvertDevicePathToText (ShellInfoObject.FileDevPath, TRUE, TRUE); + FreePool (Temp); + Temp = ConvertDevicePathToText (ShellInfoObject.ImageDevPath, TRUE, TRUE); + FreePool (Temp); + Temp = ConvertDevicePathToText (DevPath, TRUE, TRUE); + FreePool (Temp); + DEBUG_CODE_END (); + + Temp = NULL; + Size = 0; + ASSERT ((Temp == NULL && Size == 0) || (Temp != NULL)); + StrnCatGrow (&Temp, &Size, L"Shell.efi -exit ", 0); + StrnCatGrow (&Temp, &Size, CommandLine, 0); + + Status = InternalShellExecuteDevicePath ( + ParentImageHandle, + DevPath, + Temp, + (CONST CHAR16 **)Environment, + StatusCode + ); + + // + // de-allocate and return + // + FreePool (DevPath); + FreePool (Temp); + } else { + Status = InternalShellExecute ( + (CONST CHAR16 *)CommandLine, + (CONST CHAR16 **)Environment, + StatusCode + ); + } + + return (Status); +} + +/** + Utility cleanup function for EFI_SHELL_FILE_INFO objects. + + 1) frees all pointers (non-NULL) + 2) Closes the SHELL_FILE_HANDLE + + @param FileListNode pointer to the list node to free +**/ +VOID +InternalFreeShellFileInfoNode ( + IN EFI_SHELL_FILE_INFO *FileListNode + ) +{ + if (FileListNode->Info != NULL) { + FreePool ((VOID *)FileListNode->Info); + } + + if (FileListNode->FileName != NULL) { + FreePool ((VOID *)FileListNode->FileName); + } + + if (FileListNode->FullName != NULL) { + FreePool ((VOID *)FileListNode->FullName); + } + + if (FileListNode->Handle != NULL) { + ShellInfoObject.NewEfiShellProtocol->CloseFile (FileListNode->Handle); + } + + FreePool (FileListNode); +} + +/** + Frees the file list. + + This function cleans up the file list and any related data structures. It has no + impact on the files themselves. + + @param FileList The file list to free. Type EFI_SHELL_FILE_INFO is + defined in OpenFileList() + + @retval EFI_SUCCESS Free the file list successfully. + @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL; +**/ +EFI_STATUS +EFIAPI +EfiShellFreeFileList ( + IN EFI_SHELL_FILE_INFO **FileList + ) +{ + EFI_SHELL_FILE_INFO *ShellFileListItem; + + if ((FileList == NULL) || (*FileList == NULL)) { + return (EFI_INVALID_PARAMETER); + } + + for ( ShellFileListItem = (EFI_SHELL_FILE_INFO *)GetFirstNode (&(*FileList)->Link) + ; !IsListEmpty (&(*FileList)->Link) + ; ShellFileListItem = (EFI_SHELL_FILE_INFO *)GetFirstNode (&(*FileList)->Link) + ) + { + RemoveEntryList (&ShellFileListItem->Link); + InternalFreeShellFileInfoNode (ShellFileListItem); + } + + InternalFreeShellFileInfoNode (*FileList); + *FileList = NULL; + return (EFI_SUCCESS); +} + +/** + Deletes the duplicate file names files in the given file list. + + This function deletes the reduplicate files in the given file list. + + @param FileList A pointer to the first entry in the file list. + + @retval EFI_SUCCESS Always success. + @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL; +**/ +EFI_STATUS +EFIAPI +EfiShellRemoveDupInFileList ( + IN EFI_SHELL_FILE_INFO **FileList + ) +{ + EFI_STATUS Status; + EFI_SHELL_FILE_INFO *Duplicates; + EFI_SHELL_FILE_INFO *ShellFileListItem; + EFI_SHELL_FILE_INFO *ShellFileListItem2; + EFI_SHELL_FILE_INFO *TempNode; + + if ((FileList == NULL) || (*FileList == NULL)) { + return (EFI_INVALID_PARAMETER); + } + + Status = ShellSortFileList ( + FileList, + &Duplicates, + ShellSortFileListByFullName + ); + if (!EFI_ERROR (Status)) { + EfiShellFreeFileList (&Duplicates); + return EFI_SUCCESS; + } + + // + // Fall back to the slow method that needs no extra memory, and so cannot + // fail. + // + for ( ShellFileListItem = (EFI_SHELL_FILE_INFO *)GetFirstNode (&(*FileList)->Link) + ; !IsNull (&(*FileList)->Link, &ShellFileListItem->Link) + ; ShellFileListItem = (EFI_SHELL_FILE_INFO *)GetNextNode (&(*FileList)->Link, &ShellFileListItem->Link) + ) + { + for ( ShellFileListItem2 = (EFI_SHELL_FILE_INFO *)GetNextNode (&(*FileList)->Link, &ShellFileListItem->Link) + ; !IsNull (&(*FileList)->Link, &ShellFileListItem2->Link) + ; ShellFileListItem2 = (EFI_SHELL_FILE_INFO *)GetNextNode (&(*FileList)->Link, &ShellFileListItem2->Link) + ) + { + if (gUnicodeCollation->StriColl ( + gUnicodeCollation, + (CHAR16 *)ShellFileListItem->FullName, + (CHAR16 *)ShellFileListItem2->FullName + ) == 0 + ) + { + TempNode = (EFI_SHELL_FILE_INFO *)GetPreviousNode ( + &(*FileList)->Link, + &ShellFileListItem2->Link + ); + RemoveEntryList (&ShellFileListItem2->Link); + InternalFreeShellFileInfoNode (ShellFileListItem2); + // Set ShellFileListItem2 to PreviousNode so we don't access Freed + // memory in GetNextNode in the loop expression above. + ShellFileListItem2 = TempNode; + } + } + } + + return (EFI_SUCCESS); +} + +// +// This is the same structure as the external version, but it has no CONST qualifiers. +// +typedef struct { + LIST_ENTRY Link; ///< Linked list members. + EFI_STATUS Status; ///< Status of opening the file. Valid only if Handle != NULL. + CHAR16 *FullName; ///< Fully qualified filename. + CHAR16 *FileName; ///< name of this file. + SHELL_FILE_HANDLE Handle; ///< Handle for interacting with the opened file or NULL if closed. + EFI_FILE_INFO *Info; ///< Pointer to the FileInfo struct for this file or NULL. +} EFI_SHELL_FILE_INFO_NO_CONST; + +/** + Allocates and duplicates a EFI_SHELL_FILE_INFO node. + + @param[in] Node The node to copy from. + @param[in] Save TRUE to set Node->Handle to NULL, FALSE otherwise. + + @retval NULL a memory allocation error occurred + @return != NULL a pointer to the new node +**/ +EFI_SHELL_FILE_INFO * +InternalDuplicateShellFileInfo ( + IN EFI_SHELL_FILE_INFO *Node, + IN BOOLEAN Save + ) +{ + EFI_SHELL_FILE_INFO_NO_CONST *NewNode; + + // + // try to confirm that the objects are in sync + // + ASSERT (sizeof (EFI_SHELL_FILE_INFO_NO_CONST) == sizeof (EFI_SHELL_FILE_INFO)); + + NewNode = AllocateZeroPool (sizeof (EFI_SHELL_FILE_INFO)); + if (NewNode == NULL) { + return (NULL); + } + + NewNode->FullName = AllocateCopyPool (StrSize (Node->FullName), Node->FullName); + NewNode->FileName = AllocateCopyPool (StrSize (Node->FileName), Node->FileName); + NewNode->Info = AllocateCopyPool ((UINTN)Node->Info->Size, Node->Info); + if ( (NewNode->FullName == NULL) + || (NewNode->FileName == NULL) + || (NewNode->Info == NULL) + ) + { + SHELL_FREE_NON_NULL (NewNode->FullName); + SHELL_FREE_NON_NULL (NewNode->FileName); + SHELL_FREE_NON_NULL (NewNode->Info); + SHELL_FREE_NON_NULL (NewNode); + return (NULL); + } + + NewNode->Status = Node->Status; + NewNode->Handle = Node->Handle; + if (!Save) { + Node->Handle = NULL; + } + + return ((EFI_SHELL_FILE_INFO *)NewNode); +} + +/** + Allocates and populates a EFI_SHELL_FILE_INFO structure. if any memory operation + failed it will return NULL. + + @param[in] BasePath the Path to prepend onto filename for FullPath + @param[in] Status Status member initial value. + @param[in] FileName FileName member initial value. + @param[in] Handle Handle member initial value. + @param[in] Info Info struct to copy. + + @retval NULL An error occurred. + @return a pointer to the newly allocated structure. +**/ +EFI_SHELL_FILE_INFO * +CreateAndPopulateShellFileInfo ( + IN CONST CHAR16 *BasePath, + IN CONST EFI_STATUS Status, + IN CONST CHAR16 *FileName, + IN CONST SHELL_FILE_HANDLE Handle, + IN CONST EFI_FILE_INFO *Info + ) +{ + EFI_SHELL_FILE_INFO *ShellFileListItem; + CHAR16 *TempString; + UINTN Size; + + TempString = NULL; + Size = 0; + + ShellFileListItem = AllocateZeroPool (sizeof (EFI_SHELL_FILE_INFO)); + if (ShellFileListItem == NULL) { + return (NULL); + } + + if ((Info != NULL) && (Info->Size != 0)) { + ShellFileListItem->Info = AllocateZeroPool ((UINTN)Info->Size); + if (ShellFileListItem->Info == NULL) { + FreePool (ShellFileListItem); + return (NULL); + } + + CopyMem (ShellFileListItem->Info, Info, (UINTN)Info->Size); + } else { + ShellFileListItem->Info = NULL; + } + + if (FileName != NULL) { + ASSERT (TempString == NULL); + ShellFileListItem->FileName = StrnCatGrow (&TempString, 0, FileName, 0); + if (ShellFileListItem->FileName == NULL) { + FreePool (ShellFileListItem->Info); + FreePool (ShellFileListItem); + return (NULL); + } + } else { + ShellFileListItem->FileName = NULL; + } + + Size = 0; + TempString = NULL; + if (BasePath != NULL) { + ASSERT ((TempString == NULL && Size == 0) || (TempString != NULL)); + TempString = StrnCatGrow (&TempString, &Size, BasePath, 0); + if (TempString == NULL) { + FreePool ((VOID *)ShellFileListItem->FileName); + SHELL_FREE_NON_NULL (ShellFileListItem->Info); + FreePool (ShellFileListItem); + return (NULL); + } + } + + if (ShellFileListItem->FileName != NULL) { + ASSERT ((TempString == NULL && Size == 0) || (TempString != NULL)); + TempString = StrnCatGrow (&TempString, &Size, ShellFileListItem->FileName, 0); + if (TempString == NULL) { + FreePool ((VOID *)ShellFileListItem->FileName); + FreePool (ShellFileListItem->Info); + FreePool (ShellFileListItem); + return (NULL); + } + } + + TempString = PathCleanUpDirectories (TempString); + + ShellFileListItem->FullName = TempString; + ShellFileListItem->Status = Status; + ShellFileListItem->Handle = Handle; + + return (ShellFileListItem); +} + +/** + Find all files in a specified directory. + + @param FileDirHandle Handle of the directory to search. + @param FileList On return, points to the list of files in the directory + or NULL if there are no files in the directory. + + @retval EFI_SUCCESS File information was returned successfully. + @retval EFI_VOLUME_CORRUPTED The file system structures have been corrupted. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_NO_MEDIA The device media is not present. + @retval EFI_INVALID_PARAMETER The FileDirHandle was not a directory. + @return An error from FileHandleGetFileName(). +**/ +EFI_STATUS +EFIAPI +EfiShellFindFilesInDir ( + IN SHELL_FILE_HANDLE FileDirHandle, + OUT EFI_SHELL_FILE_INFO **FileList + ) +{ + EFI_SHELL_FILE_INFO *ShellFileList; + EFI_SHELL_FILE_INFO *ShellFileListItem; + EFI_FILE_INFO *FileInfo; + EFI_STATUS Status; + BOOLEAN NoFile; + CHAR16 *TempString; + CHAR16 *BasePath; + UINTN Size; + CHAR16 *TempSpot; + + BasePath = NULL; + Status = FileHandleGetFileName (FileDirHandle, &BasePath); + if (EFI_ERROR (Status)) { + return (Status); + } + + if (ShellFileHandleGetPath (FileDirHandle) != NULL) { + TempString = NULL; + Size = 0; + TempString = StrnCatGrow (&TempString, &Size, ShellFileHandleGetPath (FileDirHandle), 0); + if (TempString == NULL) { + SHELL_FREE_NON_NULL (BasePath); + return (EFI_OUT_OF_RESOURCES); + } + + TempSpot = StrStr (TempString, L";"); + + if (TempSpot != NULL) { + *TempSpot = CHAR_NULL; + } + + TempString = StrnCatGrow (&TempString, &Size, BasePath, 0); + if (TempString == NULL) { + SHELL_FREE_NON_NULL (BasePath); + return (EFI_OUT_OF_RESOURCES); + } + + SHELL_FREE_NON_NULL (BasePath); + BasePath = TempString; + } + + NoFile = FALSE; + ShellFileList = NULL; + ShellFileListItem = NULL; + FileInfo = NULL; + Status = EFI_SUCCESS; + + for ( Status = FileHandleFindFirstFile (FileDirHandle, &FileInfo) + ; !EFI_ERROR (Status) && !NoFile + ; Status = FileHandleFindNextFile (FileDirHandle, FileInfo, &NoFile) + ) + { + if (ShellFileList == NULL) { + ShellFileList = (EFI_SHELL_FILE_INFO *)AllocateZeroPool (sizeof (EFI_SHELL_FILE_INFO)); + if (ShellFileList == NULL) { + SHELL_FREE_NON_NULL (BasePath); + return EFI_OUT_OF_RESOURCES; + } + + InitializeListHead (&ShellFileList->Link); + } + + // + // allocate a new EFI_SHELL_FILE_INFO and populate it... + // + ShellFileListItem = CreateAndPopulateShellFileInfo ( + BasePath, + EFI_SUCCESS, // success since we didn't fail to open it... + FileInfo->FileName, + NULL, // no handle since not open + FileInfo + ); + if (ShellFileListItem == NULL) { + Status = EFI_OUT_OF_RESOURCES; + // + // Free resources outside the loop. + // + break; + } + + InsertTailList (&ShellFileList->Link, &ShellFileListItem->Link); + } + + if (EFI_ERROR (Status)) { + EfiShellFreeFileList (&ShellFileList); + *FileList = NULL; + } else { + *FileList = ShellFileList; + } + + SHELL_FREE_NON_NULL (BasePath); + return (Status); +} + +/** + Get the GUID value from a human readable name. + + If GuidName is a known GUID name, then update Guid to have the correct value for + that GUID. + + This function is only available when the major and minor versions in the + EfiShellProtocol are greater than or equal to 2 and 1, respectively. + + @param[in] GuidName A pointer to the localized name for the GUID being queried. + @param[out] Guid A pointer to the GUID structure to be filled in. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_INVALID_PARAMETER Guid was NULL. + @retval EFI_INVALID_PARAMETER GuidName was NULL. + @retval EFI_NOT_FOUND GuidName is not a known GUID Name. +**/ +EFI_STATUS +EFIAPI +EfiShellGetGuidFromName ( + IN CONST CHAR16 *GuidName, + OUT EFI_GUID *Guid + ) +{ + EFI_GUID *NewGuid; + EFI_STATUS Status; + + if ((Guid == NULL) || (GuidName == NULL)) { + return (EFI_INVALID_PARAMETER); + } + + Status = GetGuidFromStringName (GuidName, NULL, &NewGuid); + + if (!EFI_ERROR (Status)) { + CopyGuid (Guid, NewGuid); + } + + return (Status); +} + +/** + Get the human readable name for a GUID from the value. + + If Guid is assigned a name, then update *GuidName to point to the name. The callee + should not modify the value. + + This function is only available when the major and minor versions in the + EfiShellProtocol are greater than or equal to 2 and 1, respectively. + + @param[in] Guid A pointer to the GUID being queried. + @param[out] GuidName A pointer to a pointer the localized to name for the GUID being requested + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_INVALID_PARAMETER Guid was NULL. + @retval EFI_INVALID_PARAMETER GuidName was NULL. + @retval EFI_NOT_FOUND Guid is not assigned a name. +**/ +EFI_STATUS +EFIAPI +EfiShellGetGuidName ( + IN CONST EFI_GUID *Guid, + OUT CONST CHAR16 **GuidName + ) +{ + CHAR16 *Name; + + if ((Guid == NULL) || (GuidName == NULL)) { + return (EFI_INVALID_PARAMETER); + } + + Name = GetStringNameFromGuid (Guid, NULL); + if ((Name == NULL) || (StrLen (Name) == 0)) { + SHELL_FREE_NON_NULL (Name); + return (EFI_NOT_FOUND); + } + + *GuidName = AddBufferToFreeList (Name); + + return (EFI_SUCCESS); +} + +/** + If FileHandle is a directory then the function reads from FileHandle and reads in + each of the FileInfo structures. If one of them matches the Pattern's first + "level" then it opens that handle and calls itself on that handle. + + If FileHandle is a file and matches all of the remaining Pattern (which would be + on its last node), then add a EFI_SHELL_FILE_INFO object for this file to fileList. + + Upon a EFI_SUCCESS return fromt he function any the caller is responsible to call + FreeFileList with FileList. + + @param[in] FilePattern The FilePattern to check against. + @param[in] UnicodeCollation The pointer to EFI_UNICODE_COLLATION_PROTOCOL structure + @param[in] FileHandle The FileHandle to start with + @param[in, out] FileList pointer to pointer to list of found files. + @param[in] ParentNode The node for the parent. Same file as identified by HANDLE. + @param[in] MapName The file system name this file is on. + + @retval EFI_SUCCESS all files were found and the FileList contains a list. + @retval EFI_NOT_FOUND no files were found + @retval EFI_OUT_OF_RESOURCES a memory allocation failed +**/ +EFI_STATUS +ShellSearchHandle ( + IN CONST CHAR16 *FilePattern, + IN EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation, + IN SHELL_FILE_HANDLE FileHandle, + IN OUT EFI_SHELL_FILE_INFO **FileList, + IN CONST EFI_SHELL_FILE_INFO *ParentNode OPTIONAL, + IN CONST CHAR16 *MapName + ) +{ + EFI_STATUS Status; + CONST CHAR16 *NextFilePatternStart; + CHAR16 *CurrentFilePattern; + EFI_SHELL_FILE_INFO *ShellInfo; + EFI_SHELL_FILE_INFO *ShellInfoNode; + EFI_SHELL_FILE_INFO *NewShellNode; + EFI_FILE_INFO *FileInfo; + BOOLEAN Directory; + CHAR16 *NewFullName; + UINTN Size; + + if ( (FilePattern == NULL) + || (UnicodeCollation == NULL) + || (FileList == NULL) + ) + { + return (EFI_INVALID_PARAMETER); + } + + ShellInfo = NULL; + CurrentFilePattern = NULL; + + if (*FilePattern == L'\\') { + FilePattern++; + } + + for ( NextFilePatternStart = FilePattern + ; *NextFilePatternStart != CHAR_NULL && *NextFilePatternStart != L'\\' + ; NextFilePatternStart++) + { + } + + CurrentFilePattern = AllocateZeroPool ((NextFilePatternStart-FilePattern+1)*sizeof (CHAR16)); + if (CurrentFilePattern == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + StrnCpyS (CurrentFilePattern, NextFilePatternStart-FilePattern+1, FilePattern, NextFilePatternStart-FilePattern); + + if ( (CurrentFilePattern[0] == CHAR_NULL) + && (NextFilePatternStart[0] == CHAR_NULL) + ) + { + // + // we want the parent or root node (if no parent) + // + if (ParentNode == NULL) { + // + // We want the root node. create the node. + // + FileInfo = FileHandleGetInfo (FileHandle); + NewShellNode = CreateAndPopulateShellFileInfo ( + MapName, + EFI_SUCCESS, + L"\\", + FileHandle, + FileInfo + ); + SHELL_FREE_NON_NULL (FileInfo); + } else { + // + // Add the current parameter FileHandle to the list, then end... + // + NewShellNode = InternalDuplicateShellFileInfo ((EFI_SHELL_FILE_INFO *)ParentNode, TRUE); + } + + if (NewShellNode == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } else { + NewShellNode->Handle = NULL; + if (*FileList == NULL) { + *FileList = AllocateZeroPool (sizeof (EFI_SHELL_FILE_INFO)); + InitializeListHead (&((*FileList)->Link)); + } + + // + // Add to the returning to use list + // + InsertTailList (&(*FileList)->Link, &NewShellNode->Link); + + Status = EFI_SUCCESS; + } + } else { + Status = EfiShellFindFilesInDir (FileHandle, &ShellInfo); + + if (!EFI_ERROR (Status)) { + if (StrStr (NextFilePatternStart, L"\\") != NULL) { + Directory = TRUE; + } else { + Directory = FALSE; + } + + for ( ShellInfoNode = (EFI_SHELL_FILE_INFO *)GetFirstNode (&ShellInfo->Link) + ; !IsNull (&ShellInfo->Link, &ShellInfoNode->Link) + ; ShellInfoNode = (EFI_SHELL_FILE_INFO *)GetNextNode (&ShellInfo->Link, &ShellInfoNode->Link) + ) + { + if (UnicodeCollation->MetaiMatch (UnicodeCollation, (CHAR16 *)ShellInfoNode->FileName, CurrentFilePattern)) { + if ((ShellInfoNode->FullName != NULL) && (StrStr (ShellInfoNode->FullName, L":") == NULL)) { + Size = StrSize (ShellInfoNode->FullName) + StrSize (MapName); + NewFullName = AllocateZeroPool (Size); + if (NewFullName == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } else { + StrCpyS (NewFullName, Size / sizeof (CHAR16), MapName); + StrCatS (NewFullName, Size / sizeof (CHAR16), ShellInfoNode->FullName); + FreePool ((VOID *)ShellInfoNode->FullName); + ShellInfoNode->FullName = NewFullName; + } + } + + if (Directory && !EFI_ERROR (Status) && (ShellInfoNode->FullName != NULL) && (ShellInfoNode->FileName != NULL)) { + // + // should be a directory + // + + // + // don't open the . and .. directories + // + if ( (StrCmp (ShellInfoNode->FileName, L".") != 0) + && (StrCmp (ShellInfoNode->FileName, L"..") != 0) + ) + { + // + // + // + if (EFI_ERROR (Status)) { + break; + } + + // + // Open the directory since we need that handle in the next recursion. + // + ShellInfoNode->Status = EfiShellOpenFileByName (ShellInfoNode->FullName, &ShellInfoNode->Handle, EFI_FILE_MODE_READ); + + // + // recurse with the next part of the pattern + // + Status = ShellSearchHandle (NextFilePatternStart, UnicodeCollation, ShellInfoNode->Handle, FileList, ShellInfoNode, MapName); + EfiShellClose (ShellInfoNode->Handle); + ShellInfoNode->Handle = NULL; + } + } else if (!EFI_ERROR (Status)) { + // + // should be a file + // + + // + // copy the information we need into a new Node + // + NewShellNode = InternalDuplicateShellFileInfo (ShellInfoNode, FALSE); + if (NewShellNode == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } + + if (*FileList == NULL) { + *FileList = AllocateZeroPool (sizeof (EFI_SHELL_FILE_INFO)); + InitializeListHead (&((*FileList)->Link)); + } + + // + // Add to the returning to use list + // + InsertTailList (&(*FileList)->Link, &NewShellNode->Link); + } + } + + if (EFI_ERROR (Status)) { + break; + } + } + + if (EFI_ERROR (Status)) { + EfiShellFreeFileList (&ShellInfo); + } else { + Status = EfiShellFreeFileList (&ShellInfo); + } + } + } + + if ((*FileList == NULL) || ((*FileList != NULL) && IsListEmpty (&(*FileList)->Link))) { + Status = EFI_NOT_FOUND; + } + + FreePool (CurrentFilePattern); + return (Status); +} + +/** + Find files that match a specified pattern. + + This function searches for all files and directories that match the specified + FilePattern. The FilePattern can contain wild-card characters. The resulting file + information is placed in the file list FileList. + + Wildcards are processed + according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. + + The files in the file list are not opened. The OpenMode field is set to 0 and the FileInfo + field is set to NULL. + + if *FileList is not NULL then it must be a pre-existing and properly initialized list. + + @param FilePattern Points to a NULL-terminated shell file path, including wildcards. + @param FileList On return, points to the start of a file list containing the names + of all matching files or else points to NULL if no matching files + were found. only on a EFI_SUCCESS return will; this be non-NULL. + + @retval EFI_SUCCESS Files found. FileList is a valid list. + @retval EFI_NOT_FOUND No files found. + @retval EFI_NO_MEDIA The device has no media + @retval EFI_DEVICE_ERROR The device reported an error + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted +**/ +EFI_STATUS +EFIAPI +EfiShellFindFiles ( + IN CONST CHAR16 *FilePattern, + OUT EFI_SHELL_FILE_INFO **FileList + ) +{ + EFI_STATUS Status; + CHAR16 *PatternCopy; + CHAR16 *PatternCurrentLocation; + EFI_DEVICE_PATH_PROTOCOL *RootDevicePath; + SHELL_FILE_HANDLE RootFileHandle; + CHAR16 *MapName; + UINTN Count; + + if ( (FilePattern == NULL) + || (FileList == NULL) + || (StrStr (FilePattern, L":") == NULL) + ) + { + return (EFI_INVALID_PARAMETER); + } + + Status = EFI_SUCCESS; + RootDevicePath = NULL; + RootFileHandle = NULL; + MapName = NULL; + PatternCopy = AllocateCopyPool (StrSize (FilePattern), FilePattern); + if (PatternCopy == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + PatternCopy = PathCleanUpDirectories (PatternCopy); + + Count = StrStr (PatternCopy, L":") - PatternCopy + 1; + ASSERT (Count <= StrLen (PatternCopy)); + + ASSERT (MapName == NULL); + MapName = StrnCatGrow (&MapName, NULL, PatternCopy, Count); + if (MapName == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } else { + RootDevicePath = EfiShellGetDevicePathFromFilePath (PatternCopy); + if (RootDevicePath == NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + Status = EfiShellOpenRoot (RootDevicePath, &RootFileHandle); + if (!EFI_ERROR (Status)) { + for ( PatternCurrentLocation = PatternCopy + ; *PatternCurrentLocation != ':' + ; PatternCurrentLocation++) + { + } + + PatternCurrentLocation++; + Status = ShellSearchHandle (PatternCurrentLocation, gUnicodeCollation, RootFileHandle, FileList, NULL, MapName); + EfiShellClose (RootFileHandle); + } + + FreePool (RootDevicePath); + } + } + + SHELL_FREE_NON_NULL (PatternCopy); + SHELL_FREE_NON_NULL (MapName); + + return (Status); +} + +/** + Opens the files that match the path specified. + + This function opens all of the files specified by Path. Wildcards are processed + according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. Each + matching file has an EFI_SHELL_FILE_INFO structure created in a linked list. + + @param Path A pointer to the path string. + @param OpenMode Specifies the mode used to open each file, EFI_FILE_MODE_READ or + EFI_FILE_MODE_WRITE. + @param FileList Points to the start of a list of files opened. + + @retval EFI_SUCCESS Create the file list successfully. + @return Others Can't create the file list. +**/ +EFI_STATUS +EFIAPI +EfiShellOpenFileList ( + IN CHAR16 *Path, + IN UINT64 OpenMode, + IN OUT EFI_SHELL_FILE_INFO **FileList + ) +{ + EFI_STATUS Status; + EFI_SHELL_FILE_INFO *ShellFileListItem; + CHAR16 *Path2; + UINTN Path2Size; + CONST CHAR16 *CurDir; + BOOLEAN Found; + + PathCleanUpDirectories (Path); + + Path2Size = 0; + Path2 = NULL; + + if ((FileList == NULL) || (*FileList == NULL)) { + return (EFI_INVALID_PARAMETER); + } + + if ((*Path == L'.') && (*(Path+1) == L'\\')) { + Path += 2; + } + + // + // convert a local path to an absolute path + // + if (StrStr (Path, L":") == NULL) { + CurDir = EfiShellGetCurDir (NULL); + ASSERT ((Path2 == NULL && Path2Size == 0) || (Path2 != NULL)); + StrnCatGrow (&Path2, &Path2Size, CurDir, 0); + StrnCatGrow (&Path2, &Path2Size, L"\\", 0); + if (*Path == L'\\') { + Path++; + while (PathRemoveLastItem (Path2)) { + } + } + + ASSERT ((Path2 == NULL && Path2Size == 0) || (Path2 != NULL)); + StrnCatGrow (&Path2, &Path2Size, Path, 0); + } else { + ASSERT (Path2 == NULL); + StrnCatGrow (&Path2, NULL, Path, 0); + } + + PathCleanUpDirectories (Path2); + + // + // do the search + // + Status = EfiShellFindFiles (Path2, FileList); + + FreePool (Path2); + + if (EFI_ERROR (Status)) { + return (Status); + } + + Found = FALSE; + // + // We had no errors so open all the files (that are not already opened...) + // + for ( ShellFileListItem = (EFI_SHELL_FILE_INFO *)GetFirstNode (&(*FileList)->Link) + ; !IsNull (&(*FileList)->Link, &ShellFileListItem->Link) + ; ShellFileListItem = (EFI_SHELL_FILE_INFO *)GetNextNode (&(*FileList)->Link, &ShellFileListItem->Link) + ) + { + if ((ShellFileListItem->Status == 0) && (ShellFileListItem->Handle == NULL)) { + ShellFileListItem->Status = EfiShellOpenFileByName (ShellFileListItem->FullName, &ShellFileListItem->Handle, OpenMode); + Found = TRUE; + } + } + + if (!Found) { + return (EFI_NOT_FOUND); + } + + return (EFI_SUCCESS); +} + +/** + Gets the environment variable and Attributes, or list of environment variables. Can be + used instead of GetEnv(). + + This function returns the current value of the specified environment variable and + the Attributes. If no variable name was specified, then all of the known + variables will be returned. + + @param[in] Name A pointer to the environment variable name. If Name is NULL, + then the function will return all of the defined shell + environment variables. In the case where multiple environment + variables are being returned, each variable will be terminated + by a NULL, and the list will be terminated by a double NULL. + @param[out] Attributes If not NULL, a pointer to the returned attributes bitmask for + the environment variable. In the case where Name is NULL, and + multiple environment variables are being returned, Attributes + is undefined. + + @retval NULL The environment variable doesn't exist. + @return A non-NULL value points to the variable's value. The returned + pointer does not need to be freed by the caller. +**/ +CONST CHAR16 * +EFIAPI +EfiShellGetEnvEx ( + IN CONST CHAR16 *Name, + OUT UINT32 *Attributes OPTIONAL + ) +{ + EFI_STATUS Status; + VOID *Buffer; + UINTN Size; + ENV_VAR_LIST *Node; + CHAR16 *CurrentWriteLocation; + + Size = 0; + Buffer = NULL; + + if (Name == NULL) { + // + // Build the semi-colon delimited list. (2 passes) + // + for ( Node = (ENV_VAR_LIST *)GetFirstNode (&gShellEnvVarList.Link) + ; !IsNull (&gShellEnvVarList.Link, &Node->Link) + ; Node = (ENV_VAR_LIST *)GetNextNode (&gShellEnvVarList.Link, &Node->Link) + ) + { + ASSERT (Node->Key != NULL); + Size += StrSize (Node->Key); + } + + Size += 2*sizeof (CHAR16); + + Buffer = AllocateZeroPool (Size); + if (Buffer == NULL) { + return (NULL); + } + + CurrentWriteLocation = (CHAR16 *)Buffer; + + for ( Node = (ENV_VAR_LIST *)GetFirstNode (&gShellEnvVarList.Link) + ; !IsNull (&gShellEnvVarList.Link, &Node->Link) + ; Node = (ENV_VAR_LIST *)GetNextNode (&gShellEnvVarList.Link, &Node->Link) + ) + { + ASSERT (Node->Key != NULL); + StrCpyS ( + CurrentWriteLocation, + (Size)/sizeof (CHAR16) - (CurrentWriteLocation - ((CHAR16 *)Buffer)), + Node->Key + ); + CurrentWriteLocation += StrLen (CurrentWriteLocation) + 1; + } + } else { + // + // We are doing a specific environment variable + // + Status = ShellFindEnvVarInList (Name, (CHAR16 **)&Buffer, &Size, Attributes); + + if (EFI_ERROR (Status)) { + // + // get the size we need for this EnvVariable + // + Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES (Name, Attributes, &Size, Buffer); + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // Allocate the space and recall the get function + // + Buffer = AllocateZeroPool (Size); + Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES (Name, Attributes, &Size, Buffer); + } + + // + // we didn't get it (might not exist) + // free the memory if we allocated any and return NULL + // + if (EFI_ERROR (Status)) { + if (Buffer != NULL) { + FreePool (Buffer); + } + + return (NULL); + } else { + // + // If we did not find the environment variable in the gShellEnvVarList + // but get it from UEFI variable storage successfully then we need update + // the gShellEnvVarList. + // + ShellFreeEnvVarList (); + Status = ShellInitEnvVarList (); + ASSERT (Status == EFI_SUCCESS); + } + } + } + + // + // return the buffer + // + return (AddBufferToFreeList (Buffer)); +} + +/** + Gets either a single or list of environment variables. + + If name is not NULL then this function returns the current value of the specified + environment variable. + + If Name is NULL, then a list of all environment variable names is returned. Each is a + NULL terminated string with a double NULL terminating the list. + + @param Name A pointer to the environment variable name. If + Name is NULL, then the function will return all + of the defined shell environment variables. In + the case where multiple environment variables are + being returned, each variable will be terminated by + a NULL, and the list will be terminated by a double + NULL. + + @retval !=NULL A pointer to the returned string. + The returned pointer does not need to be freed by the caller. + + @retval NULL The environment variable doesn't exist or there are + no environment variables. +**/ +CONST CHAR16 * +EFIAPI +EfiShellGetEnv ( + IN CONST CHAR16 *Name + ) +{ + return (EfiShellGetEnvEx (Name, NULL)); +} + +/** + Internal variable setting function. Allows for setting of the read only variables. + + @param Name Points to the NULL-terminated environment variable name. + @param Value Points to the NULL-terminated environment variable value. If the value is an + empty string then the environment variable is deleted. + @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE). + + @retval EFI_SUCCESS The environment variable was successfully updated. +**/ +EFI_STATUS +InternalEfiShellSetEnv ( + IN CONST CHAR16 *Name, + IN CONST CHAR16 *Value, + IN BOOLEAN Volatile + ) +{ + EFI_STATUS Status; + + if ((Value == NULL) || (StrLen (Value) == 0)) { + Status = SHELL_DELETE_ENVIRONMENT_VARIABLE (Name); + if (!EFI_ERROR (Status)) { + ShellRemvoeEnvVarFromList (Name); + } + } else { + SHELL_DELETE_ENVIRONMENT_VARIABLE (Name); + Status = ShellAddEnvVarToList ( + Name, + Value, + StrSize (Value), + EFI_VARIABLE_BOOTSERVICE_ACCESS | (Volatile ? 0 : EFI_VARIABLE_NON_VOLATILE) + ); + if (!EFI_ERROR (Status)) { + Status = Volatile + ? SHELL_SET_ENVIRONMENT_VARIABLE_V (Name, StrSize (Value) - sizeof (CHAR16), Value) + : SHELL_SET_ENVIRONMENT_VARIABLE_NV (Name, StrSize (Value) - sizeof (CHAR16), Value); + if (EFI_ERROR (Status)) { + ShellRemvoeEnvVarFromList (Name); + } + } + } + + return Status; +} + +/** + Sets the environment variable. + + This function changes the current value of the specified environment variable. If the + environment variable exists and the Value is an empty string, then the environment + variable is deleted. If the environment variable exists and the Value is not an empty + string, then the value of the environment variable is changed. If the environment + variable does not exist and the Value is an empty string, there is no action. If the + environment variable does not exist and the Value is a non-empty string, then the + environment variable is created and assigned the specified value. + + For a description of volatile and non-volatile environment variables, see UEFI Shell + 2.0 specification section 3.6.1. + + @param Name Points to the NULL-terminated environment variable name. + @param Value Points to the NULL-terminated environment variable value. If the value is an + empty string then the environment variable is deleted. + @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE). + + @retval EFI_SUCCESS The environment variable was successfully updated. +**/ +EFI_STATUS +EFIAPI +EfiShellSetEnv ( + IN CONST CHAR16 *Name, + IN CONST CHAR16 *Value, + IN BOOLEAN Volatile + ) +{ + if ((Name == NULL) || (*Name == CHAR_NULL)) { + return (EFI_INVALID_PARAMETER); + } + + // + // Make sure we dont 'set' a predefined read only variable + // + if ((StrCmp (Name, L"cwd") == 0) || + (StrCmp (Name, L"lasterror") == 0) || + (StrCmp (Name, L"profiles") == 0) || + (StrCmp (Name, L"uefishellsupport") == 0) || + (StrCmp (Name, L"uefishellversion") == 0) || + (StrCmp (Name, L"uefiversion") == 0) || + (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest && + (StrCmp (Name, mNoNestingEnvVarName) == 0)) + ) + { + return (EFI_INVALID_PARAMETER); + } + + return (InternalEfiShellSetEnv (Name, Value, Volatile)); +} + +/** + Returns the current directory on the specified device. + + If FileSystemMapping is NULL, it returns the current working directory. If the + FileSystemMapping is not NULL, it returns the current directory associated with the + FileSystemMapping. In both cases, the returned name includes the file system + mapping (i.e. fs0:\current-dir). + + Note that the current directory string should exclude the tailing backslash character. + + @param FileSystemMapping A pointer to the file system mapping. If NULL, + then the current working directory is returned. + + @retval !=NULL The current directory. + @retval NULL Current directory does not exist. +**/ +CONST CHAR16 * +EFIAPI +EfiShellGetCurDir ( + IN CONST CHAR16 *FileSystemMapping OPTIONAL + ) +{ + CHAR16 *PathToReturn; + UINTN Size; + SHELL_MAP_LIST *MapListItem; + + if (!IsListEmpty (&gShellMapList.Link)) { + // + // if parameter is NULL, use current + // + if (FileSystemMapping == NULL) { + return (EfiShellGetEnv (L"cwd")); + } else { + Size = 0; + PathToReturn = NULL; + MapListItem = ShellCommandFindMapItem (FileSystemMapping); + if (MapListItem != NULL) { + ASSERT ((PathToReturn == NULL && Size == 0) || (PathToReturn != NULL)); + PathToReturn = StrnCatGrow (&PathToReturn, &Size, MapListItem->MapName, 0); + PathToReturn = StrnCatGrow (&PathToReturn, &Size, MapListItem->CurrentDirectoryPath, 0); + } + } + + return (AddBufferToFreeList (PathToReturn)); + } else { + return (NULL); + } +} + +/** + Changes the current directory on the specified device. + + If the FileSystem is NULL, and the directory Dir does not contain a file system's + mapped name, this function changes the current working directory. + + If the FileSystem is NULL and the directory Dir contains a mapped name, then the + current file system and the current directory on that file system are changed. + + If FileSystem is NULL, and Dir is not NULL, then this changes the current working file + system. + + If FileSystem is not NULL and Dir is not NULL, then this function changes the current + directory on the specified file system. + + If the current working directory or the current working file system is changed then the + %cwd% environment variable will be updated + + Note that the current directory string should exclude the tailing backslash character. + + @param FileSystem A pointer to the file system's mapped name. If NULL, then the current working + directory is changed. + @param Dir Points to the NULL-terminated directory on the device specified by FileSystem. + + @retval EFI_SUCCESS The operation was successful + @retval EFI_NOT_FOUND The file system could not be found +**/ +EFI_STATUS +EFIAPI +EfiShellSetCurDir ( + IN CONST CHAR16 *FileSystem OPTIONAL, + IN CONST CHAR16 *Dir + ) +{ + CHAR16 *MapName; + SHELL_MAP_LIST *MapListItem; + UINTN Size; + EFI_STATUS Status; + CHAR16 *TempString; + CHAR16 *DirectoryName; + UINTN TempLen; + + Size = 0; + MapName = NULL; + MapListItem = NULL; + TempString = NULL; + DirectoryName = NULL; + + if (((FileSystem == NULL) && (Dir == NULL)) || (Dir == NULL)) { + return (EFI_INVALID_PARAMETER); + } + + if (IsListEmpty (&gShellMapList.Link)) { + return (EFI_NOT_FOUND); + } + + DirectoryName = StrnCatGrow (&DirectoryName, NULL, Dir, 0); + ASSERT (DirectoryName != NULL); + + PathCleanUpDirectories (DirectoryName); + + if (FileSystem == NULL) { + // + // determine the file system mapping to use + // + if (StrStr (DirectoryName, L":") != NULL) { + ASSERT (MapName == NULL); + MapName = StrnCatGrow (&MapName, NULL, DirectoryName, (StrStr (DirectoryName, L":")-DirectoryName+1)); + } + + // + // find the file system mapping's entry in the list + // or use current + // + if (MapName != NULL) { + MapListItem = ShellCommandFindMapItem (MapName); + + // + // make that the current file system mapping + // + if (MapListItem != NULL) { + gShellCurMapping = MapListItem; + } + } else { + MapListItem = gShellCurMapping; + } + + if (MapListItem == NULL) { + FreePool (DirectoryName); + SHELL_FREE_NON_NULL (MapName); + return (EFI_NOT_FOUND); + } + + // + // now update the MapListItem's current directory + // + if ((MapListItem->CurrentDirectoryPath != NULL) && (DirectoryName[StrLen (DirectoryName) - 1] != L':')) { + FreePool (MapListItem->CurrentDirectoryPath); + MapListItem->CurrentDirectoryPath = NULL; + } + + if (MapName != NULL) { + TempLen = StrLen (MapName); + if (TempLen != StrLen (DirectoryName)) { + ASSERT ((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL)); + MapListItem->CurrentDirectoryPath = StrnCatGrow (&MapListItem->CurrentDirectoryPath, &Size, DirectoryName+StrLen (MapName), 0); + } + + FreePool (MapName); + } else { + ASSERT ((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL)); + MapListItem->CurrentDirectoryPath = StrnCatGrow (&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0); + } + + if (((MapListItem->CurrentDirectoryPath != NULL) && (MapListItem->CurrentDirectoryPath[StrLen (MapListItem->CurrentDirectoryPath)-1] == L'\\')) || (MapListItem->CurrentDirectoryPath == NULL)) { + ASSERT ((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL)); + if (MapListItem->CurrentDirectoryPath != NULL) { + MapListItem->CurrentDirectoryPath[StrLen (MapListItem->CurrentDirectoryPath)-1] = CHAR_NULL; + } + } + } else { + // + // cant have a mapping in the directory... + // + if (StrStr (DirectoryName, L":") != NULL) { + FreePool (DirectoryName); + return (EFI_INVALID_PARAMETER); + } + + // + // FileSystem != NULL + // + MapListItem = ShellCommandFindMapItem (FileSystem); + if (MapListItem == NULL) { + FreePool (DirectoryName); + return (EFI_INVALID_PARAMETER); + } + + // gShellCurMapping = MapListItem; + if (DirectoryName != NULL) { + // + // change current dir on that file system + // + + if (MapListItem->CurrentDirectoryPath != NULL) { + FreePool (MapListItem->CurrentDirectoryPath); + DEBUG_CODE ( + MapListItem->CurrentDirectoryPath = NULL; + ); + } + + // ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL)); + // MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, FileSystem, 0); + ASSERT ((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL)); + MapListItem->CurrentDirectoryPath = StrnCatGrow (&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0); + ASSERT ((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL)); + MapListItem->CurrentDirectoryPath = StrnCatGrow (&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0); + if ((MapListItem->CurrentDirectoryPath != NULL) && (MapListItem->CurrentDirectoryPath[StrLen (MapListItem->CurrentDirectoryPath)-1] == L'\\')) { + ASSERT ((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL)); + MapListItem->CurrentDirectoryPath[StrLen (MapListItem->CurrentDirectoryPath)-1] = CHAR_NULL; + } + } + } + + FreePool (DirectoryName); + // + // if updated the current directory then update the environment variable + // + if (MapListItem == gShellCurMapping) { + Size = 0; + ASSERT ((TempString == NULL && Size == 0) || (TempString != NULL)); + StrnCatGrow (&TempString, &Size, MapListItem->MapName, 0); + ASSERT ((TempString == NULL && Size == 0) || (TempString != NULL)); + StrnCatGrow (&TempString, &Size, MapListItem->CurrentDirectoryPath, 0); + Status = InternalEfiShellSetEnv (L"cwd", TempString, TRUE); + FreePool (TempString); + return (Status); + } + + return (EFI_SUCCESS); +} + +/** + Return help information about a specific command. + + This function returns the help information for the specified command. The help text + can be internal to the shell or can be from a UEFI Shell manual page. + + If Sections is specified, then each section name listed will be compared in a casesensitive + manner, to the section names described in Appendix B. If the section exists, + it will be appended to the returned help text. If the section does not exist, no + information will be returned. If Sections is NULL, then all help text information + available will be returned. + + @param Command Points to the NULL-terminated UEFI Shell command name. + @param Sections Points to the NULL-terminated comma-delimited + section names to return. If NULL, then all + sections will be returned. + @param HelpText On return, points to a callee-allocated buffer + containing all specified help text. + + @retval EFI_SUCCESS The help text was returned. + @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the + returned help text. + @retval EFI_INVALID_PARAMETER HelpText is NULL + @retval EFI_NOT_FOUND There is no help text available for Command. +**/ +EFI_STATUS +EFIAPI +EfiShellGetHelpText ( + IN CONST CHAR16 *Command, + IN CONST CHAR16 *Sections OPTIONAL, + OUT CHAR16 **HelpText + ) +{ + CONST CHAR16 *ManFileName; + CHAR16 *FixCommand; + EFI_STATUS Status; + + ASSERT (HelpText != NULL); + FixCommand = NULL; + + ManFileName = ShellCommandGetManFileNameHandler (Command); + + if (ManFileName != NULL) { + return (ProcessManFile (ManFileName, Command, Sections, NULL, HelpText)); + } else { + if ( (StrLen (Command) > 4) + && ((Command[StrLen (Command)-1] == L'i') || (Command[StrLen (Command)-1] == L'I')) + && ((Command[StrLen (Command)-2] == L'f') || (Command[StrLen (Command)-2] == L'F')) + && ((Command[StrLen (Command)-3] == L'e') || (Command[StrLen (Command)-3] == L'E')) + && (Command[StrLen (Command)-4] == L'.') + ) + { + FixCommand = AllocateZeroPool (StrSize (Command) - 4 * sizeof (CHAR16)); + if (FixCommand == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + StrnCpyS ( + FixCommand, + (StrSize (Command) - 4 * sizeof (CHAR16))/sizeof (CHAR16), + Command, + StrLen (Command)-4 + ); + Status = ProcessManFile (FixCommand, FixCommand, Sections, NULL, HelpText); + FreePool (FixCommand); + return Status; + } else { + return (ProcessManFile (Command, Command, Sections, NULL, HelpText)); + } + } +} + +/** + Gets the enable status of the page break output mode. + + User can use this function to determine current page break mode. + + @retval TRUE The page break output mode is enabled. + @retval FALSE The page break output mode is disabled. +**/ +BOOLEAN +EFIAPI +EfiShellGetPageBreak ( + VOID + ) +{ + return (ShellInfoObject.PageBreakEnabled); +} + +/** + Judges whether the active shell is the root shell. + + This function makes the user to know that whether the active Shell is the root shell. + + @retval TRUE The active Shell is the root Shell. + @retval FALSE The active Shell is NOT the root Shell. +**/ +BOOLEAN +EFIAPI +EfiShellIsRootShell ( + VOID + ) +{ + return (ShellInfoObject.RootShellInstance); +} + +/** + function to return a semi-colon delimited list of all alias' in the current shell + + up to caller to free the memory. + + @retval NULL No alias' were found + @retval NULL An error occurred getting alias' + @return !NULL a list of all alias' +**/ +CHAR16 * +InternalEfiShellGetListAlias ( + VOID + ) +{ + EFI_STATUS Status; + EFI_GUID Guid; + CHAR16 *VariableName; + UINTN NameSize; + UINTN NameBufferSize; + CHAR16 *RetVal; + UINTN RetSize; + + NameBufferSize = INIT_NAME_BUFFER_SIZE; + VariableName = AllocateZeroPool (NameBufferSize); + RetSize = 0; + RetVal = NULL; + + if (VariableName == NULL) { + return (NULL); + } + + VariableName[0] = CHAR_NULL; + + while (TRUE) { + NameSize = NameBufferSize; + Status = gRT->GetNextVariableName (&NameSize, VariableName, &Guid); + if (Status == EFI_NOT_FOUND) { + break; + } else if (Status == EFI_BUFFER_TOO_SMALL) { + NameBufferSize = NameSize > NameBufferSize * 2 ? NameSize : NameBufferSize * 2; + SHELL_FREE_NON_NULL (VariableName); + VariableName = AllocateZeroPool (NameBufferSize); + if (VariableName == NULL) { + Status = EFI_OUT_OF_RESOURCES; + SHELL_FREE_NON_NULL (RetVal); + RetVal = NULL; + break; + } + + NameSize = NameBufferSize; + Status = gRT->GetNextVariableName (&NameSize, VariableName, &Guid); + } + + if (EFI_ERROR (Status)) { + SHELL_FREE_NON_NULL (RetVal); + RetVal = NULL; + break; + } + + if (CompareGuid (&Guid, &gShellAliasGuid)) { + ASSERT ((RetVal == NULL && RetSize == 0) || (RetVal != NULL)); + RetVal = StrnCatGrow (&RetVal, &RetSize, VariableName, 0); + RetVal = StrnCatGrow (&RetVal, &RetSize, L";", 0); + } // compare guid + } // while + + SHELL_FREE_NON_NULL (VariableName); + + return (RetVal); +} + +/** + Convert a null-terminated unicode string, in-place, to all lowercase. + Then return it. + + @param Str The null-terminated string to be converted to all lowercase. + + @return The null-terminated string converted into all lowercase. +**/ +CHAR16 * +ToLower ( + CHAR16 *Str + ) +{ + UINTN Index; + + for (Index = 0; Str[Index] != L'\0'; Index++) { + if ((Str[Index] >= L'A') && (Str[Index] <= L'Z')) { + Str[Index] -= (CHAR16)(L'A' - L'a'); + } + } + + return Str; +} + +/** + This function returns the command associated with a alias or a list of all + alias'. + + @param[in] Alias Points to the NULL-terminated shell alias. + If this parameter is NULL, then all + aliases will be returned in ReturnedData. + @param[out] Volatile upon return of a single command if TRUE indicates + this is stored in a volatile fashion. FALSE otherwise. + + @return If Alias is not NULL, it will return a pointer to + the NULL-terminated command for that alias. + If Alias is NULL, ReturnedData points to a ';' + delimited list of alias (e.g. + ReturnedData = "dir;del;copy;mfp") that is NULL-terminated. + @retval NULL an error occurred + @retval NULL Alias was not a valid Alias +**/ +CONST CHAR16 * +EFIAPI +EfiShellGetAlias ( + IN CONST CHAR16 *Alias, + OUT BOOLEAN *Volatile OPTIONAL + ) +{ + CHAR16 *RetVal; + UINTN RetSize; + UINT32 Attribs; + EFI_STATUS Status; + CHAR16 *AliasLower; + CHAR16 *AliasVal; + + // Convert to lowercase to make aliases case-insensitive + if (Alias != NULL) { + AliasLower = AllocateCopyPool (StrSize (Alias), Alias); + if (AliasLower == NULL) { + return NULL; + } + + ToLower (AliasLower); + + if (Volatile == NULL) { + GetVariable2 (AliasLower, &gShellAliasGuid, (VOID **)&AliasVal, NULL); + FreePool (AliasLower); + return (AddBufferToFreeList (AliasVal)); + } + + RetSize = 0; + RetVal = NULL; + Status = gRT->GetVariable (AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal); + if (Status == EFI_BUFFER_TOO_SMALL) { + RetVal = AllocateZeroPool (RetSize); + Status = gRT->GetVariable (AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal); + } + + if (EFI_ERROR (Status)) { + if (RetVal != NULL) { + FreePool (RetVal); + } + + FreePool (AliasLower); + return (NULL); + } + + if ((EFI_VARIABLE_NON_VOLATILE & Attribs) == EFI_VARIABLE_NON_VOLATILE) { + *Volatile = FALSE; + } else { + *Volatile = TRUE; + } + + FreePool (AliasLower); + return (AddBufferToFreeList (RetVal)); + } + + return (AddBufferToFreeList (InternalEfiShellGetListAlias ())); +} + +/** + Changes a shell command alias. + + This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias. + + this function does not check for built in alias'. + + @param[in] Command Points to the NULL-terminated shell command or existing alias. + @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and + Command refers to an alias, that alias will be deleted. + @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the + Alias being set will be stored in a non-volatile fashion. + + @retval EFI_SUCCESS Alias created or deleted successfully. + @retval EFI_NOT_FOUND the Alias intended to be deleted was not found +**/ +EFI_STATUS +InternalSetAlias ( + IN CONST CHAR16 *Command, + IN CONST CHAR16 *Alias, + IN BOOLEAN Volatile + ) +{ + EFI_STATUS Status; + CHAR16 *AliasLower; + BOOLEAN DeleteAlias; + + DeleteAlias = FALSE; + if (Alias == NULL) { + // + // We must be trying to remove one if Alias is NULL + // remove an alias (but passed in COMMAND parameter) + // + Alias = Command; + DeleteAlias = TRUE; + } + + ASSERT (Alias != NULL); + + // + // Convert to lowercase to make aliases case-insensitive + // + AliasLower = AllocateCopyPool (StrSize (Alias), Alias); + if (AliasLower == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ToLower (AliasLower); + + if (DeleteAlias) { + Status = gRT->SetVariable (AliasLower, &gShellAliasGuid, 0, 0, NULL); + } else { + Status = gRT->SetVariable ( + AliasLower, + &gShellAliasGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | (Volatile ? 0 : EFI_VARIABLE_NON_VOLATILE), + StrSize (Command), + (VOID *)Command + ); + } + + FreePool (AliasLower); + + return Status; +} + +/** + Changes a shell command alias. + + This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias. + + + @param[in] Command Points to the NULL-terminated shell command or existing alias. + @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and + Command refers to an alias, that alias will be deleted. + @param[in] Replace If TRUE and the alias already exists, then the existing alias will be replaced. If + FALSE and the alias already exists, then the existing alias is unchanged and + EFI_ACCESS_DENIED is returned. + @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the + Alias being set will be stored in a non-volatile fashion. + + @retval EFI_SUCCESS Alias created or deleted successfully. + @retval EFI_NOT_FOUND the Alias intended to be deleted was not found + @retval EFI_ACCESS_DENIED The alias is a built-in alias or already existed and Replace was set to + FALSE. + @retval EFI_INVALID_PARAMETER Command is null or the empty string. +**/ +EFI_STATUS +EFIAPI +EfiShellSetAlias ( + IN CONST CHAR16 *Command, + IN CONST CHAR16 *Alias, + IN BOOLEAN Replace, + IN BOOLEAN Volatile + ) +{ + if (ShellCommandIsOnAliasList ((Alias == NULL) ? Command : Alias)) { + // + // cant set over a built in alias + // + return (EFI_ACCESS_DENIED); + } else if ((Command == NULL) || (*Command == CHAR_NULL) || (StrLen (Command) == 0)) { + // + // Command is null or empty + // + return (EFI_INVALID_PARAMETER); + } else if ((EfiShellGetAlias (Command, NULL) != NULL) && !Replace) { + // + // Alias already exists, Replace not set + // + return (EFI_ACCESS_DENIED); + } else { + return (InternalSetAlias (Command, Alias, Volatile)); + } +} + +// Pure FILE_HANDLE operations are passed to FileHandleLib +// these functions are indicated by the * +EFI_SHELL_PROTOCOL mShellProtocol = { + EfiShellExecute, + EfiShellGetEnv, + EfiShellSetEnv, + EfiShellGetAlias, + EfiShellSetAlias, + EfiShellGetHelpText, + EfiShellGetDevicePathFromMap, + EfiShellGetMapFromDevicePath, + EfiShellGetDevicePathFromFilePath, + EfiShellGetFilePathFromDevicePath, + EfiShellSetMap, + EfiShellGetCurDir, + EfiShellSetCurDir, + EfiShellOpenFileList, + EfiShellFreeFileList, + EfiShellRemoveDupInFileList, + EfiShellBatchIsActive, + EfiShellIsRootShell, + EfiShellEnablePageBreak, + EfiShellDisablePageBreak, + EfiShellGetPageBreak, + EfiShellGetDeviceName, + (EFI_SHELL_GET_FILE_INFO)FileHandleGetInfo, // * + (EFI_SHELL_SET_FILE_INFO)FileHandleSetInfo, // * + EfiShellOpenFileByName, + EfiShellClose, + EfiShellCreateFile, + (EFI_SHELL_READ_FILE)FileHandleRead, // * + (EFI_SHELL_WRITE_FILE)FileHandleWrite, // * + (EFI_SHELL_DELETE_FILE)FileHandleDelete, // * + EfiShellDeleteFileByName, + (EFI_SHELL_GET_FILE_POSITION)FileHandleGetPosition, // * + (EFI_SHELL_SET_FILE_POSITION)FileHandleSetPosition, // * + (EFI_SHELL_FLUSH_FILE)FileHandleFlush, // * + EfiShellFindFiles, + EfiShellFindFilesInDir, + (EFI_SHELL_GET_FILE_SIZE)FileHandleGetSize, // * + EfiShellOpenRoot, + EfiShellOpenRootByHandle, + NULL, + SHELL_MAJOR_VERSION, + SHELL_MINOR_VERSION, + + // New for UEFI Shell 2.1 + EfiShellRegisterGuidName, + EfiShellGetGuidName, + EfiShellGetGuidFromName, + EfiShellGetEnvEx +}; + +/** + Function to create and install on the current handle. + + Will overwrite any existing ShellProtocols in the system to be sure that + the current shell is in control. + + This must be removed via calling CleanUpShellProtocol(). + + @param[in, out] NewShell The pointer to the pointer to the structure + to install. + + @retval EFI_SUCCESS The operation was successful. + @return An error from LocateHandle, CreateEvent, or other core function. +**/ +EFI_STATUS +CreatePopulateInstallShellProtocol ( + IN OUT EFI_SHELL_PROTOCOL **NewShell + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + EFI_HANDLE *Buffer; + UINTN HandleCounter; + SHELL_PROTOCOL_HANDLE_LIST *OldProtocolNode; + EFI_SHELL_PROTOCOL *OldShell; + + if (NewShell == NULL) { + return (EFI_INVALID_PARAMETER); + } + + BufferSize = 0; + Buffer = NULL; + OldProtocolNode = NULL; + InitializeListHead (&ShellInfoObject.OldShellList.Link); + + // + // Initialize EfiShellProtocol object... + // + Status = gBS->CreateEvent ( + 0, + 0, + NULL, + NULL, + &mShellProtocol.ExecutionBreak + ); + if (EFI_ERROR (Status)) { + return (Status); + } + + // + // Get the size of the buffer we need. + // + Status = gBS->LocateHandle ( + ByProtocol, + &gEfiShellProtocolGuid, + NULL, + &BufferSize, + Buffer + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // Allocate and recall with buffer of correct size + // + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + Status = gBS->LocateHandle ( + ByProtocol, + &gEfiShellProtocolGuid, + NULL, + &BufferSize, + Buffer + ); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return (Status); + } + + // + // now overwrite each of them, but save the info to restore when we end. + // + for (HandleCounter = 0; HandleCounter < (BufferSize/sizeof (EFI_HANDLE)); HandleCounter++) { + Status = gBS->OpenProtocol ( + Buffer[HandleCounter], + &gEfiShellProtocolGuid, + (VOID **)&OldShell, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + OldProtocolNode = AllocateZeroPool (sizeof (SHELL_PROTOCOL_HANDLE_LIST)); + if (OldProtocolNode == NULL) { + if (!IsListEmpty (&ShellInfoObject.OldShellList.Link)) { + CleanUpShellProtocol (&mShellProtocol); + } + + Status = EFI_OUT_OF_RESOURCES; + break; + } + + // + // reinstall over the old one... + // + OldProtocolNode->Handle = Buffer[HandleCounter]; + OldProtocolNode->Interface = OldShell; + Status = gBS->ReinstallProtocolInterface ( + OldProtocolNode->Handle, + &gEfiShellProtocolGuid, + OldProtocolNode->Interface, + (VOID *)(&mShellProtocol) + ); + if (!EFI_ERROR (Status)) { + // + // we reinstalled successfully. log this so we can reverse it later. + // + + // + // add to the list for subsequent... + // + InsertTailList (&ShellInfoObject.OldShellList.Link, &OldProtocolNode->Link); + } + } + } + + FreePool (Buffer); + } else if (Status == EFI_NOT_FOUND) { + ASSERT (IsListEmpty (&ShellInfoObject.OldShellList.Link)); + // + // no one else published yet. just publish it ourselves. + // + Status = gBS->InstallProtocolInterface ( + &gImageHandle, + &gEfiShellProtocolGuid, + EFI_NATIVE_INTERFACE, + (VOID *)(&mShellProtocol) + ); + } + + if (PcdGetBool (PcdShellSupportOldProtocols)) { + /// @todo support ShellEnvironment2 + /// @todo do we need to support ShellEnvironment (not ShellEnvironment2) also? + } + + if (!EFI_ERROR (Status)) { + *NewShell = &mShellProtocol; + } + + return (Status); +} + +/** + Opposite of CreatePopulateInstallShellProtocol. + + Free all memory and restore the system to the state it was in before calling + CreatePopulateInstallShellProtocol. + + @param[in, out] NewShell The pointer to the new shell protocol structure. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +CleanUpShellProtocol ( + IN OUT EFI_SHELL_PROTOCOL *NewShell + ) +{ + SHELL_PROTOCOL_HANDLE_LIST *Node2; + + // + // if we need to restore old protocols... + // + if (!IsListEmpty (&ShellInfoObject.OldShellList.Link)) { + for (Node2 = (SHELL_PROTOCOL_HANDLE_LIST *)GetFirstNode (&ShellInfoObject.OldShellList.Link) + ; !IsListEmpty (&ShellInfoObject.OldShellList.Link) + ; Node2 = (SHELL_PROTOCOL_HANDLE_LIST *)GetFirstNode (&ShellInfoObject.OldShellList.Link) + ) + { + RemoveEntryList (&Node2->Link); + gBS->ReinstallProtocolInterface (Node2->Handle, &gEfiShellProtocolGuid, NewShell, Node2->Interface); + FreePool (Node2); + } + } else { + // + // no need to restore + // + gBS->UninstallProtocolInterface (gImageHandle, &gEfiShellProtocolGuid, NewShell); + } + + return EFI_SUCCESS; +} + +/** + Cleanup the shell environment. + + @param[in, out] NewShell The pointer to the new shell protocol structure. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +CleanUpShellEnvironment ( + IN OUT EFI_SHELL_PROTOCOL *NewShell + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx; + + CleanUpShellProtocol (NewShell); + + Status = gBS->CloseEvent (NewShell->ExecutionBreak); + NewShell->ExecutionBreak = NULL; + + Status = gBS->OpenProtocol ( + gST->ConsoleInHandle, + &gEfiSimpleTextInputExProtocolGuid, + (VOID **)&SimpleEx, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlCNotifyHandle1); + Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlCNotifyHandle2); + Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlCNotifyHandle3); + Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlCNotifyHandle4); + Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlSNotifyHandle1); + Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlSNotifyHandle2); + Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlSNotifyHandle3); + Status = SimpleEx->UnregisterKeyNotify (SimpleEx, ShellInfoObject.CtrlSNotifyHandle4); + } + + return (Status); +} + +/** + Notification function for keystrokes. + + @param[in] KeyData The key that was pressed. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +EFIAPI +NotificationFunction ( + IN EFI_KEY_DATA *KeyData + ) +{ + if (((KeyData->Key.UnicodeChar == L'c') && + ((KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED)) || (KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED)))) || + (KeyData->Key.UnicodeChar == 3) + ) + { + if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) { + return (EFI_UNSUPPORTED); + } + + return (gBS->SignalEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak)); + } else if ((KeyData->Key.UnicodeChar == L's') && + ((KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED)) || (KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED))) + ) + { + ShellInfoObject.HaltOutput = TRUE; + } + + return (EFI_SUCCESS); +} + +/** + Function to start monitoring for CTRL-C using SimpleTextInputEx. This + feature's enabled state was not known when the shell initially launched. + + @retval EFI_SUCCESS The feature is enabled. + @retval EFI_OUT_OF_RESOURCES There is not enough memory available. +**/ +EFI_STATUS +InernalEfiShellStartMonitor ( + VOID + ) +{ + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx; + EFI_KEY_DATA KeyData; + EFI_STATUS Status; + + Status = gBS->OpenProtocol ( + gST->ConsoleInHandle, + &gEfiSimpleTextInputExProtocolGuid, + (VOID **)&SimpleEx, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_SHELL_NO_IN_EX), + ShellInfoObject.HiiHandle + ); + return (EFI_SUCCESS); + } + + if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) { + return (EFI_UNSUPPORTED); + } + + KeyData.KeyState.KeyToggleState = 0; + KeyData.Key.ScanCode = 0; + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED; + KeyData.Key.UnicodeChar = L'c'; + + Status = SimpleEx->RegisterKeyNotify ( + SimpleEx, + &KeyData, + NotificationFunction, + &ShellInfoObject.CtrlCNotifyHandle1 + ); + + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED; + if (!EFI_ERROR (Status)) { + Status = SimpleEx->RegisterKeyNotify ( + SimpleEx, + &KeyData, + NotificationFunction, + &ShellInfoObject.CtrlCNotifyHandle2 + ); + } + + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED; + KeyData.Key.UnicodeChar = 3; + if (!EFI_ERROR (Status)) { + Status = SimpleEx->RegisterKeyNotify ( + SimpleEx, + &KeyData, + NotificationFunction, + &ShellInfoObject.CtrlCNotifyHandle3 + ); + } + + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED; + if (!EFI_ERROR (Status)) { + Status = SimpleEx->RegisterKeyNotify ( + SimpleEx, + &KeyData, + NotificationFunction, + &ShellInfoObject.CtrlCNotifyHandle4 + ); + } + + return (Status); +} diff --git a/ShellPkg/Application/Shell/ShellProtocol.h b/ShellPkg/Application/Shell/ShellProtocol.h index 3d872ffb68..c6dbad0f86 100644 --- a/ShellPkg/Application/Shell/ShellProtocol.h +++ b/ShellPkg/Application/Shell/ShellProtocol.h @@ -1,938 +1,938 @@ -/** @file
- Member functions of EFI_SHELL_PROTOCOL and functions for creation,
- manipulation, and initialization of EFI_SHELL_PROTOCOL.
-
- (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
- Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#ifndef _SHELL_PROTOCOL_HEADER_
-#define _SHELL_PROTOCOL_HEADER_
-
-#include "Shell.h"
-
-typedef struct {
- LIST_ENTRY Link;
- EFI_SHELL_PROTOCOL *Interface;
- EFI_HANDLE Handle;
-} SHELL_PROTOCOL_HANDLE_LIST;
-
-// flags values...
-#define SHELL_MAP_FLAGS_CONSIST BIT1
-
-/**
- Function to create and install on the current handle.
-
- Will overwrite any existing ShellProtocols in the system to be sure that
- the current shell is in control.
-
- This must be removed via calling CleanUpShellProtocol().
-
- @param[in, out] NewShell The pointer to the pointer to the structure
- to install.
-
- @retval EFI_SUCCESS The operation was successful.
- @return An error from LocateHandle, CreateEvent, or other core function.
-**/
-EFI_STATUS
-CreatePopulateInstallShellProtocol (
- IN OUT EFI_SHELL_PROTOCOL **NewShell
- );
-
-/**
- Opposite of CreatePopulateInstallShellProtocol.
-
- Free all memory and restore the system to the state it was in before calling
- CreatePopulateInstallShellProtocol.
-
- @param[in, out] NewShell The pointer to the new shell protocol structure.
-
- @retval EFI_SUCCESS The operation was successful.
-**/
-EFI_STATUS
-CleanUpShellProtocol (
- IN OUT EFI_SHELL_PROTOCOL *NewShell
- );
-
-/**
- Cleanup the shell environment.
-
- @param[in, out] NewShell The pointer to the new shell protocol structure.
-
- @retval EFI_SUCCESS The operation was successful.
-**/
-EFI_STATUS
-CleanUpShellEnvironment (
- IN OUT EFI_SHELL_PROTOCOL *NewShell
- );
-
-/**
- This function creates a mapping for a device path.
-
- @param DevicePath Points to the device path. If this is NULL and Mapping points to a valid mapping,
- then the mapping will be deleted.
- @param Mapping Points to the NULL-terminated mapping for the device path. Must end with a ':'
-
- @retval EFI_SUCCESS Mapping created or deleted successfully.
- @retval EFI_NO_MAPPING There is no handle that corresponds exactly to DevicePath. See the
- boot service function LocateDevicePath().
- @retval EFI_ACCESS_DENIED The mapping is a built-in alias.
- @retval EFI_INVALID_PARAMETER Mapping was NULL
- @retval EFI_INVALID_PARAMETER Mapping did not end with a ':'
- @retval EFI_INVALID_PARAMETER DevicePath was not pointing at a device that had a SIMPLE_FILE_SYSTEM_PROTOCOL installed.
- @retval EFI_NOT_FOUND There was no mapping found to delete
- @retval EFI_OUT_OF_RESOURCES Memory allocation failed
-**/
-EFI_STATUS
-EFIAPI
-EfiShellSetMap (
- IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL,
- IN CONST CHAR16 *Mapping
- );
-
-/**
- Gets the device path from the mapping.
-
- This function gets the device path associated with a mapping.
-
- @param Mapping A pointer to the mapping
-
- @retval !=NULL Pointer to the device path that corresponds to the
- device mapping. The returned pointer does not need
- to be freed.
- @retval NULL There is no device path associated with the
- specified mapping.
-**/
-CONST EFI_DEVICE_PATH_PROTOCOL *
-EFIAPI
-EfiShellGetDevicePathFromMap (
- IN CONST CHAR16 *Mapping
- );
-
-/**
- Gets the mapping that most closely matches the device path.
-
- This function gets the mapping which corresponds to the device path *DevicePath. If
- there is no exact match, then the mapping which most closely matches *DevicePath
- is returned, and *DevicePath is updated to point to the remaining portion of the
- device path. If there is an exact match, the mapping is returned and *DevicePath
- points to the end-of-device-path node.
-
- @param DevicePath On entry, points to a device path pointer. On
- exit, updates the pointer to point to the
- portion of the device path after the mapping.
-
- @retval NULL No mapping was found.
- @return !=NULL Pointer to NULL-terminated mapping. The buffer
- is callee allocated and should be freed by the caller.
-**/
-CONST CHAR16 *
-EFIAPI
-EfiShellGetMapFromDevicePath (
- IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
- );
-
-/**
- Converts a device path to a file system-style path.
-
- This function converts a device path to a file system path by replacing part, or all, of
- the device path with the file-system mapping. If there are more than one application
- file system mappings, the one that most closely matches Path will be used.
-
- @param Path The pointer to the device path
-
- @retval NULL the device path could not be found.
- @return all The pointer of the NULL-terminated file path. The path
- is callee-allocated and should be freed by the caller.
-**/
-CHAR16 *
-EFIAPI
-EfiShellGetFilePathFromDevicePath (
- IN CONST EFI_DEVICE_PATH_PROTOCOL *Path
- );
-
-/**
- Converts a file system style name to a device path.
-
- This function converts a file system style name to a device path, by replacing any
- mapping references to the associated device path.
-
- @param Path the pointer to the path
-
- @return all The pointer of the file path. The file path is callee
- allocated and should be freed by the caller.
-**/
-EFI_DEVICE_PATH_PROTOCOL *
-EFIAPI
-EfiShellGetDevicePathFromFilePath (
- IN CONST CHAR16 *Path
- );
-
-/**
- Gets the name of the device specified by the device handle.
-
- This function gets the user-readable name of the device specified by the device
- handle. If no user-readable name could be generated, then *BestDeviceName will be
- NULL and EFI_NOT_FOUND will be returned.
-
- If EFI_DEVICE_NAME_USE_COMPONENT_NAME is set, then the function will return the
- device's name using the EFI_COMPONENT_NAME2_PROTOCOL, if present on
- DeviceHandle.
-
- If EFI_DEVICE_NAME_USE_DEVICE_PATH is set, then the function will return the
- device's name using the EFI_DEVICE_PATH_PROTOCOL, if present on DeviceHandle.
- If both EFI_DEVICE_NAME_USE_COMPONENT_NAME and
- EFI_DEVICE_NAME_USE_DEVICE_PATH are set, then
- EFI_DEVICE_NAME_USE_COMPONENT_NAME will have higher priority.
-
- @param DeviceHandle The handle of the device.
- @param Flags Determines the possible sources of component names.
- Valid bits are:
- EFI_DEVICE_NAME_USE_COMPONENT_NAME
- EFI_DEVICE_NAME_USE_DEVICE_PATH
- @param Language A pointer to the language specified for the device
- name, in the same format as described in the UEFI
- specification, Appendix M
- @param BestDeviceName On return, points to the callee-allocated NULL-
- terminated name of the device. If no device name
- could be found, points to NULL. The name must be
- freed by the caller...
-
- @retval EFI_SUCCESS Get the name successfully.
- @retval EFI_NOT_FOUND Fail to get the device name.
- @retval EFI_INVALID_PARAMETER Flags did not have a valid bit set.
- @retval EFI_INVALID_PARAMETER BestDeviceName was NULL
- @retval EFI_INVALID_PARAMETER DeviceHandle was NULL
-**/
-EFI_STATUS
-EFIAPI
-EfiShellGetDeviceName (
- IN EFI_HANDLE DeviceHandle,
- IN EFI_SHELL_DEVICE_NAME_FLAGS Flags,
- IN CHAR8 *Language,
- OUT CHAR16 **BestDeviceName
- );
-
-/**
- Opens the root directory of a device on a handle
-
- This function opens the root directory of a device and returns a file handle to it.
-
- @param DeviceHandle The handle of the device that contains the volume.
- @param FileHandle On exit, points to the file handle corresponding to the root directory on the
- device.
-
- @retval EFI_SUCCESS Root opened successfully.
- @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
- could not be opened.
- @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted.
- @retval EFI_DEVICE_ERROR The device had an error
-**/
-EFI_STATUS
-EFIAPI
-EfiShellOpenRootByHandle (
- IN EFI_HANDLE DeviceHandle,
- OUT SHELL_FILE_HANDLE *FileHandle
- );
-
-/**
- Opens the root directory of a device.
-
- This function opens the root directory of a device and returns a file handle to it.
-
- @param DevicePath Points to the device path corresponding to the device where the
- EFI_SIMPLE_FILE_SYSTEM_PROTOCOL is installed.
- @param FileHandle On exit, points to the file handle corresponding to the root directory on the
- device.
-
- @retval EFI_SUCCESS Root opened successfully.
- @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
- could not be opened.
- @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted.
- @retval EFI_DEVICE_ERROR The device had an error
-**/
-EFI_STATUS
-EFIAPI
-EfiShellOpenRoot (
- IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
- OUT SHELL_FILE_HANDLE *FileHandle
- );
-
-/**
- Returns whether any script files are currently being processed.
-
- @retval TRUE There is at least one script file active.
- @retval FALSE No script files are active now.
-
-**/
-BOOLEAN
-EFIAPI
-EfiShellBatchIsActive (
- VOID
- );
-
-/**
- Worker function to open a file based on a device path. this will open the root
- of the volume and then traverse down to the file itself.
-
- @param DevicePath2 Device Path of the file
- @param FileHandle Pointer to the file upon a successful return
- @param OpenMode mode to open file in.
- @param Attributes the File Attributes to use when creating a new file
-
- @retval EFI_SUCCESS the file is open and FileHandle is valid
- @retval EFI_UNSUPPORTED the device path contained non-path elements
- @retval other an error occurred.
-**/
-EFI_STATUS
-InternalOpenFileDevicePath (
- IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath2,
- OUT SHELL_FILE_HANDLE *FileHandle,
- IN UINT64 OpenMode,
- IN UINT64 Attributes OPTIONAL
- );
-
-/**
- Creates a file or directory by name.
-
- This function creates an empty new file or directory with the specified attributes and
- returns the new file's handle. If the file already exists and is read-only, then
- EFI_INVALID_PARAMETER will be returned.
-
- If the file already existed, it is truncated and its attributes updated. If the file is
- created successfully, the FileHandle is the file's handle, else, the FileHandle is NULL.
-
- If the file name begins with >v, then the file handle which is returned refers to the
- shell environment variable with the specified name. If the shell environment variable
- already exists and is non-volatile then EFI_INVALID_PARAMETER is returned.
-
- @param FileName Pointer to NULL-terminated file path
- @param FileAttribs The new file's attributes. the different attributes are
- described in EFI_FILE_PROTOCOL.Open().
- @param FileHandle On return, points to the created file handle or directory's handle
-
- @retval EFI_SUCCESS The file was opened. FileHandle points to the new file's handle.
- @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
- @retval EFI_UNSUPPORTED could not open the file path
- @retval EFI_NOT_FOUND the specified file could not be found on the devide, or could not
- file the file system on the device.
- @retval EFI_NO_MEDIA the device has no medium.
- @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
- longer supported.
- @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
- the DirName.
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
- @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
- when the media is write-protected.
- @retval EFI_ACCESS_DENIED The service denied access to the file.
- @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
- @retval EFI_VOLUME_FULL The volume is full.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellCreateFile (
- IN CONST CHAR16 *FileName,
- IN UINT64 FileAttribs,
- OUT SHELL_FILE_HANDLE *FileHandle
- );
-
-/**
- Opens a file or a directory by file name.
-
- This function opens the specified file in the specified OpenMode and returns a file
- handle.
- If the file name begins with >v, then the file handle which is returned refers to the
- shell environment variable with the specified name. If the shell environment variable
- exists, is non-volatile and the OpenMode indicates EFI_FILE_MODE_WRITE, then
- EFI_INVALID_PARAMETER is returned.
-
- If the file name is >i, then the file handle which is returned refers to the standard
- input. If the OpenMode indicates EFI_FILE_MODE_WRITE, then EFI_INVALID_PARAMETER
- is returned.
-
- If the file name is >o, then the file handle which is returned refers to the standard
- output. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
- is returned.
-
- If the file name is >e, then the file handle which is returned refers to the standard
- error. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
- is returned.
-
- If the file name is NUL, then the file handle that is returned refers to the standard NUL
- file. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER is
- returned.
-
- If return EFI_SUCCESS, the FileHandle is the opened file's handle, else, the
- FileHandle is NULL.
-
- @param FileName Points to the NULL-terminated UCS-2 encoded file name.
- @param FileHandle On return, points to the file handle.
- @param OpenMode File open mode. Either EFI_FILE_MODE_READ or
- EFI_FILE_MODE_WRITE from section 12.4 of the UEFI
- Specification.
- @retval EFI_SUCCESS The file was opened. FileHandle has the opened file's handle.
- @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. FileHandle is NULL.
- @retval EFI_UNSUPPORTED Could not open the file path. FileHandle is NULL.
- @retval EFI_NOT_FOUND The specified file could not be found on the device or the file
- system could not be found on the device. FileHandle is NULL.
- @retval EFI_NO_MEDIA The device has no medium. FileHandle is NULL.
- @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
- longer supported. FileHandle is NULL.
- @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
- the FileName. FileHandle is NULL.
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. FileHandle is NULL.
- @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
- when the media is write-protected. FileHandle is NULL.
- @retval EFI_ACCESS_DENIED The service denied access to the file. FileHandle is NULL.
- @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. FileHandle
- is NULL.
- @retval EFI_VOLUME_FULL The volume is full. FileHandle is NULL.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellOpenFileByName (
- IN CONST CHAR16 *FileName,
- OUT SHELL_FILE_HANDLE *FileHandle,
- IN UINT64 OpenMode
- );
-
-/**
- Deletes the file specified by the file name.
-
- This function deletes a file.
-
- @param FileName Points to the NULL-terminated file name.
-
- @retval EFI_SUCCESS The file was closed and deleted, and the handle was closed.
- @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted.
- @sa EfiShellCreateFile
- @sa FileHandleDelete
-**/
-EFI_STATUS
-EFIAPI
-EfiShellDeleteFileByName (
- IN CONST CHAR16 *FileName
- );
-
-/**
- Disables the page break output mode.
-**/
-VOID
-EFIAPI
-EfiShellDisablePageBreak (
- VOID
- );
-
-/**
- Enables the page break output mode.
-**/
-VOID
-EFIAPI
-EfiShellEnablePageBreak (
- VOID
- );
-
-/**
- internal worker function to run a command via Device Path
-
- @param ParentImageHandle A handle of the image that is executing the specified
- command line.
- @param DevicePath device path of the file to execute
- @param CommandLine Points to the NULL-terminated UCS-2 encoded string
- containing the command line. If NULL then the command-
- line will be empty.
- @param Environment Points to a NULL-terminated array of environment
- variables with the format 'x=y', where x is the
- environment variable name and y is the value. If this
- is NULL, then the current shell environment is used.
- @param[out] StartImageStatus Returned status from gBS->StartImage.
-
- @retval EFI_SUCCESS The command executed successfully. The status code
- returned by the command is pointed to by StatusCode.
- @retval EFI_INVALID_PARAMETER The parameters are invalid.
- @retval EFI_OUT_OF_RESOURCES Out of resources.
- @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
-**/
-EFI_STATUS
-InternalShellExecuteDevicePath (
- IN CONST EFI_HANDLE *ParentImageHandle,
- IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
- IN CONST CHAR16 *CommandLine OPTIONAL,
- IN CONST CHAR16 **Environment OPTIONAL,
- OUT EFI_STATUS *StartImageStatus OPTIONAL
- );
-
-/**
- Execute the command line.
-
- This function creates a nested instance of the shell and executes the specified
- command (CommandLine) with the specified environment (Environment). Upon return,
- the status code returned by the specified command is placed in StatusCode.
-
- If Environment is NULL, then the current environment is used and all changes made
- by the commands executed will be reflected in the current environment. If the
- Environment is non-NULL, then the changes made will be discarded.
-
- The CommandLine is executed from the current working directory on the current
- device.
-
- @param ParentImageHandle A handle of the image that is executing the specified
- command line.
- @param CommandLine Points to the NULL-terminated UCS-2 encoded string
- containing the command line. If NULL then the command-
- line will be empty.
- @param Environment Points to a NULL-terminated array of environment
- variables with the format 'x=y', where x is the
- environment variable name and y is the value. If this
- is NULL, then the current shell environment is used.
- @param StatusCode Points to the status code returned by the command.
-
- @retval EFI_SUCCESS The command executed successfully. The status code
- returned by the command is pointed to by StatusCode.
- @retval EFI_INVALID_PARAMETER The parameters are invalid.
- @retval EFI_OUT_OF_RESOURCES Out of resources.
- @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellExecute (
- IN EFI_HANDLE *ParentImageHandle,
- IN CHAR16 *CommandLine OPTIONAL,
- IN CHAR16 **Environment OPTIONAL,
- OUT EFI_STATUS *StatusCode OPTIONAL
- );
-
-/**
- Utility cleanup function for EFI_SHELL_FILE_INFO objects.
-
- 1) frees all pointers (non-NULL)
- 2) Closes the SHELL_FILE_HANDLE
-
- @param FileListNode pointer to the list node to free
-**/
-VOID
-FreeShellFileInfoNode (
- IN EFI_SHELL_FILE_INFO *FileListNode
- );
-
-/**
- Frees the file list.
-
- This function cleans up the file list and any related data structures. It has no
- impact on the files themselves.
-
- @param FileList The file list to free. Type EFI_SHELL_FILE_INFO is
- defined in OpenFileList()
-
- @retval EFI_SUCCESS Free the file list successfully.
- @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
-**/
-EFI_STATUS
-EFIAPI
-EfiShellFreeFileList (
- IN EFI_SHELL_FILE_INFO **FileList
- );
-
-/**
- Deletes the duplicate file names files in the given file list.
-
- This function deletes the reduplicate files in the given file list.
-
- @param FileList A pointer to the first entry in the file list.
-
- @retval EFI_SUCCESS Always success.
- @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
-**/
-EFI_STATUS
-EFIAPI
-EfiShellRemoveDupInFileList (
- IN EFI_SHELL_FILE_INFO **FileList
- );
-
-/**
- Allocates and populates a EFI_SHELL_FILE_INFO structure. if any memory operation
- failed it will return NULL.
-
- @param[in] BasePath the Path to prepend onto filename for FullPath
- @param[in] Status Status member initial value.
- @param[in] FileName FileName member initial value.
- @param[in] Handle Handle member initial value.
- @param[in] Info Info struct to copy.
-
-**/
-EFI_SHELL_FILE_INFO *
-CreateAndPopulateShellFileInfo (
- IN CONST CHAR16 *BasePath,
- IN CONST EFI_STATUS Status,
- IN CONST CHAR16 *FileName,
- IN CONST SHELL_FILE_HANDLE Handle,
- IN CONST EFI_FILE_INFO *Info
- );
-
-/**
- Find all files in a specified directory.
-
- @param FileDirHandle Handle of the directory to search.
- @param FileList On return, points to the list of files in the directory
- or NULL if there are no files in the directory.
-
- @retval EFI_SUCCESS File information was returned successfully.
- @retval EFI_VOLUME_CORRUPTED The file system structures have been corrupted.
- @retval EFI_DEVICE_ERROR The device reported an error.
- @retval EFI_NO_MEDIA The device media is not present.
- @retval EFI_INVALID_PARAMETER The FileDirHandle was not a directory.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellFindFilesInDir (
- IN SHELL_FILE_HANDLE FileDirHandle,
- OUT EFI_SHELL_FILE_INFO **FileList
- );
-
-/**
- Find files that match a specified pattern.
-
- This function searches for all files and directories that match the specified
- FilePattern. The FilePattern can contain wild-card characters. The resulting file
- information is placed in the file list FileList.
-
- Wildcards are processed
- according to the rules specified in UEFI Shell 2.0 spec section 3.7.1.
-
- The files in the file list are not opened. The OpenMode field is set to 0 and the FileInfo
- field is set to NULL.
-
- if *FileList is not NULL then it must be a pre-existing and properly initialized list.
-
- @param FilePattern Points to a NULL-terminated shell file path, including wildcards.
- @param FileList On return, points to the start of a file list containing the names
- of all matching files or else points to NULL if no matching files
- were found. only on a EFI_SUCCESS return will; this be non-NULL.
-
- @retval EFI_SUCCESS Files found. FileList is a valid list.
- @retval EFI_NOT_FOUND No files found.
- @retval EFI_NO_MEDIA The device has no media
- @retval EFI_DEVICE_ERROR The device reported an error
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted
-**/
-EFI_STATUS
-EFIAPI
-EfiShellFindFiles (
- IN CONST CHAR16 *FilePattern,
- OUT EFI_SHELL_FILE_INFO **FileList
- );
-
-/**
- Opens the files that match the path specified.
-
- This function opens all of the files specified by Path. Wildcards are processed
- according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. Each
- matching file has an EFI_SHELL_FILE_INFO structure created in a linked list.
-
- @param Path A pointer to the path string.
- @param OpenMode Specifies the mode used to open each file, EFI_FILE_MODE_READ or
- EFI_FILE_MODE_WRITE.
- @param FileList Points to the start of a list of files opened.
-
- @retval EFI_SUCCESS Create the file list successfully.
- @return Others Can't create the file list.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellOpenFileList (
- IN CHAR16 *Path,
- IN UINT64 OpenMode,
- IN OUT EFI_SHELL_FILE_INFO **FileList
- );
-
-/**
- Gets the environment variable.
-
- This function returns the current value of the specified environment variable.
-
- @param Name A pointer to the environment variable name
-
- @retval !=NULL The environment variable's value. The returned
- pointer does not need to be freed by the caller.
- @retval NULL The environment variable doesn't exist.
-**/
-CONST CHAR16 *
-EFIAPI
-EfiShellGetEnv (
- IN CONST CHAR16 *Name
- );
-
-/**
- Sets the environment variable.
-
- This function changes the current value of the specified environment variable. If the
- environment variable exists and the Value is an empty string, then the environment
- variable is deleted. If the environment variable exists and the Value is not an empty
- string, then the value of the environment variable is changed. If the environment
- variable does not exist and the Value is an empty string, there is no action. If the
- environment variable does not exist and the Value is a non-empty string, then the
- environment variable is created and assigned the specified value.
-
- For a description of volatile and non-volatile environment variables, see UEFI Shell
- 2.0 specification section 3.6.1.
-
- @param Name Points to the NULL-terminated environment variable name.
- @param Value Points to the NULL-terminated environment variable value. If the value is an
- empty string then the environment variable is deleted.
- @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
-
- @retval EFI_SUCCESS The environment variable was successfully updated.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellSetEnv (
- IN CONST CHAR16 *Name,
- IN CONST CHAR16 *Value,
- IN BOOLEAN Volatile
- );
-
-/**
- Returns the current directory on the specified device.
-
- If FileSystemMapping is NULL, it returns the current working directory. If the
- FileSystemMapping is not NULL, it returns the current directory associated with the
- FileSystemMapping. In both cases, the returned name includes the file system
- mapping (i.e. fs0:\current-dir).
-
- @param FileSystemMapping A pointer to the file system mapping. If NULL,
- then the current working directory is returned.
-
- @retval !=NULL The current directory.
- @retval NULL Current directory does not exist.
-**/
-CONST CHAR16 *
-EFIAPI
-EfiShellGetCurDir (
- IN CONST CHAR16 *FileSystemMapping OPTIONAL
- );
-
-/**
- Changes the current directory on the specified device.
-
- If the FileSystem is NULL, and the directory Dir does not contain a file system's
- mapped name, this function changes the current working directory. If FileSystem is
- NULL and the directory Dir contains a mapped name, then the current file system and
- the current directory on that file system are changed.
-
- If FileSystem is not NULL, and Dir is NULL, then this changes the current working file
- system.
-
- If FileSystem is not NULL and Dir is not NULL, then this function changes the current
- directory on the specified file system.
-
- If the current working directory or the current working file system is changed then the
- %cwd% environment variable will be updated
-
- @param FileSystem A pointer to the file system's mapped name. If NULL, then the current working
- directory is changed.
- @param Dir Points to the NULL-terminated directory on the device specified by FileSystem.
-
- @retval EFI_SUCCESS The operation was successful
-**/
-EFI_STATUS
-EFIAPI
-EfiShellSetCurDir (
- IN CONST CHAR16 *FileSystem OPTIONAL,
- IN CONST CHAR16 *Dir
- );
-
-/**
- Return help information about a specific command.
-
- This function returns the help information for the specified command. The help text
- can be internal to the shell or can be from a UEFI Shell manual page.
-
- If Sections is specified, then each section name listed will be compared in a casesensitive
- manner, to the section names described in Appendix B. If the section exists,
- it will be appended to the returned help text. If the section does not exist, no
- information will be returned. If Sections is NULL, then all help text information
- available will be returned.
-
- @param Command Points to the NULL-terminated UEFI Shell command name.
- @param Sections Points to the NULL-terminated comma-delimited
- section names to return. If NULL, then all
- sections will be returned.
- @param HelpText On return, points to a callee-allocated buffer
- containing all specified help text.
-
- @retval EFI_SUCCESS The help text was returned.
- @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
- returned help text.
- @retval EFI_INVALID_PARAMETER HelpText is NULL
- @retval EFI_NOT_FOUND There is no help text available for Command.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellGetHelpText (
- IN CONST CHAR16 *Command,
- IN CONST CHAR16 *Sections OPTIONAL,
- OUT CHAR16 **HelpText
- );
-
-/**
- Gets the enable status of the page break output mode.
-
- User can use this function to determine current page break mode.
-
- @retval TRUE The page break output mode is enabled
- @retval FALSE The page break output mode is disabled
-**/
-BOOLEAN
-EFIAPI
-EfiShellGetPageBreak (
- VOID
- );
-
-/**
- Judges whether the active shell is the root shell.
-
- This function makes the user to know that whether the active Shell is the root shell.
-
- @retval TRUE The active Shell is the root Shell.
- @retval FALSE The active Shell is NOT the root Shell.
-**/
-BOOLEAN
-EFIAPI
-EfiShellIsRootShell (
- VOID
- );
-
-/**
- This function returns the command associated with a alias or a list of all
- alias'.
-
- @param[in] Command Points to the NULL-terminated shell alias.
- If this parameter is NULL, then all
- aliases will be returned in ReturnedData.
- @param[out] Volatile upon return of a single command if TRUE indicates
- this is stored in a volatile fashion. FALSE otherwise.
- @return If Alias is not NULL, it will return a pointer to
- the NULL-terminated command for that alias.
- If Alias is NULL, ReturnedData points to a ';'
- delimited list of alias (e.g.
- ReturnedData = "dir;del;copy;mfp") that is NULL-terminated.
- @retval NULL an error occurred
- @retval NULL Alias was not a valid Alias
-**/
-CONST CHAR16 *
-EFIAPI
-EfiShellGetAlias (
- IN CONST CHAR16 *Command,
- OUT BOOLEAN *Volatile OPTIONAL
- );
-
-/**
- Changes a shell command alias.
-
- This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
-
- this function does not check for built in alias'.
-
- @param[in] Command Points to the NULL-terminated shell command or existing alias.
- @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and
- Command refers to an alias, that alias will be deleted.
- @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the
- Alias being set will be stored in a non-volatile fashion.
-
- @retval EFI_SUCCESS Alias created or deleted successfully.
- @retval EFI_NOT_FOUND the Alias intended to be deleted was not found
-**/
-EFI_STATUS
-InternalSetAlias (
- IN CONST CHAR16 *Command,
- IN CONST CHAR16 *Alias OPTIONAL,
- IN BOOLEAN Volatile
- );
-
-/**
- Changes a shell command alias.
-
- This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
-
-
- @param[in] Command Points to the NULL-terminated shell command or existing alias.
- @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and
- Command refers to an alias, that alias will be deleted.
- @param[in] Replace If TRUE and the alias already exists, then the existing alias will be replaced. If
- FALSE and the alias already exists, then the existing alias is unchanged and
- EFI_ACCESS_DENIED is returned.
- @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the
- Alias being set will be stored in a non-volatile fashion.
-
- @retval EFI_SUCCESS Alias created or deleted successfully.
- @retval EFI_NOT_FOUND the Alias intended to be deleted was not found
- @retval EFI_ACCESS_DENIED The alias is a built-in alias or already existed and Replace was set to
- FALSE.
-**/
-EFI_STATUS
-EFIAPI
-EfiShellSetAlias (
- IN CONST CHAR16 *Command,
- IN CONST CHAR16 *Alias OPTIONAL,
- IN BOOLEAN Replace,
- IN BOOLEAN Volatile
- );
-
-/**
- Utility cleanup function for EFI_SHELL_FILE_INFO objects.
-
- 1) frees all pointers (non-NULL)
- 2) Closes the SHELL_FILE_HANDLE
-
- @param FileListNode pointer to the list node to free
-**/
-VOID
-InternalFreeShellFileInfoNode (
- IN EFI_SHELL_FILE_INFO *FileListNode
- );
-
-/**
- Internal variable setting function. Allows for setting of the read only variables.
-
- @param Name Points to the NULL-terminated environment variable name.
- @param Value Points to the NULL-terminated environment variable value. If the value is an
- empty string then the environment variable is deleted.
- @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
-
- @retval EFI_SUCCESS The environment variable was successfully updated.
-**/
-EFI_STATUS
-InternalEfiShellSetEnv (
- IN CONST CHAR16 *Name,
- IN CONST CHAR16 *Value,
- IN BOOLEAN Volatile
- );
-
-/**
- Function to start monitoring for CTRL-C using SimpleTextInputEx. This
- feature's enabled state was not known when the shell initially launched.
-
- @retval EFI_SUCCESS The feature is enabled.
- @retval EFI_OUT_OF_RESOURCES There is not enough memory available.
-**/
-EFI_STATUS
-InernalEfiShellStartMonitor (
- VOID
- );
-
-/**
- Notification function for keystrokes.
-
- @param[in] KeyData The key that was pressed.
-
- @retval EFI_SUCCESS The operation was successful.
-**/
-EFI_STATUS
-EFIAPI
-NotificationFunction (
- IN EFI_KEY_DATA *KeyData
- );
-
-#endif //_SHELL_PROTOCOL_HEADER_
+/** @file + Member functions of EFI_SHELL_PROTOCOL and functions for creation, + manipulation, and initialization of EFI_SHELL_PROTOCOL. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR> + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SHELL_PROTOCOL_HEADER_ +#define _SHELL_PROTOCOL_HEADER_ + +#include "Shell.h" + +typedef struct { + LIST_ENTRY Link; + EFI_SHELL_PROTOCOL *Interface; + EFI_HANDLE Handle; +} SHELL_PROTOCOL_HANDLE_LIST; + +// flags values... +#define SHELL_MAP_FLAGS_CONSIST BIT1 + +/** + Function to create and install on the current handle. + + Will overwrite any existing ShellProtocols in the system to be sure that + the current shell is in control. + + This must be removed via calling CleanUpShellProtocol(). + + @param[in, out] NewShell The pointer to the pointer to the structure + to install. + + @retval EFI_SUCCESS The operation was successful. + @return An error from LocateHandle, CreateEvent, or other core function. +**/ +EFI_STATUS +CreatePopulateInstallShellProtocol ( + IN OUT EFI_SHELL_PROTOCOL **NewShell + ); + +/** + Opposite of CreatePopulateInstallShellProtocol. + + Free all memory and restore the system to the state it was in before calling + CreatePopulateInstallShellProtocol. + + @param[in, out] NewShell The pointer to the new shell protocol structure. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +CleanUpShellProtocol ( + IN OUT EFI_SHELL_PROTOCOL *NewShell + ); + +/** + Cleanup the shell environment. + + @param[in, out] NewShell The pointer to the new shell protocol structure. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +CleanUpShellEnvironment ( + IN OUT EFI_SHELL_PROTOCOL *NewShell + ); + +/** + This function creates a mapping for a device path. + + @param DevicePath Points to the device path. If this is NULL and Mapping points to a valid mapping, + then the mapping will be deleted. + @param Mapping Points to the NULL-terminated mapping for the device path. Must end with a ':' + + @retval EFI_SUCCESS Mapping created or deleted successfully. + @retval EFI_NO_MAPPING There is no handle that corresponds exactly to DevicePath. See the + boot service function LocateDevicePath(). + @retval EFI_ACCESS_DENIED The mapping is a built-in alias. + @retval EFI_INVALID_PARAMETER Mapping was NULL + @retval EFI_INVALID_PARAMETER Mapping did not end with a ':' + @retval EFI_INVALID_PARAMETER DevicePath was not pointing at a device that had a SIMPLE_FILE_SYSTEM_PROTOCOL installed. + @retval EFI_NOT_FOUND There was no mapping found to delete + @retval EFI_OUT_OF_RESOURCES Memory allocation failed +**/ +EFI_STATUS +EFIAPI +EfiShellSetMap ( + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL, + IN CONST CHAR16 *Mapping + ); + +/** + Gets the device path from the mapping. + + This function gets the device path associated with a mapping. + + @param Mapping A pointer to the mapping + + @retval !=NULL Pointer to the device path that corresponds to the + device mapping. The returned pointer does not need + to be freed. + @retval NULL There is no device path associated with the + specified mapping. +**/ +CONST EFI_DEVICE_PATH_PROTOCOL * +EFIAPI +EfiShellGetDevicePathFromMap ( + IN CONST CHAR16 *Mapping + ); + +/** + Gets the mapping that most closely matches the device path. + + This function gets the mapping which corresponds to the device path *DevicePath. If + there is no exact match, then the mapping which most closely matches *DevicePath + is returned, and *DevicePath is updated to point to the remaining portion of the + device path. If there is an exact match, the mapping is returned and *DevicePath + points to the end-of-device-path node. + + @param DevicePath On entry, points to a device path pointer. On + exit, updates the pointer to point to the + portion of the device path after the mapping. + + @retval NULL No mapping was found. + @return !=NULL Pointer to NULL-terminated mapping. The buffer + is callee allocated and should be freed by the caller. +**/ +CONST CHAR16 * +EFIAPI +EfiShellGetMapFromDevicePath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ); + +/** + Converts a device path to a file system-style path. + + This function converts a device path to a file system path by replacing part, or all, of + the device path with the file-system mapping. If there are more than one application + file system mappings, the one that most closely matches Path will be used. + + @param Path The pointer to the device path + + @retval NULL the device path could not be found. + @return all The pointer of the NULL-terminated file path. The path + is callee-allocated and should be freed by the caller. +**/ +CHAR16 * +EFIAPI +EfiShellGetFilePathFromDevicePath ( + IN CONST EFI_DEVICE_PATH_PROTOCOL *Path + ); + +/** + Converts a file system style name to a device path. + + This function converts a file system style name to a device path, by replacing any + mapping references to the associated device path. + + @param Path the pointer to the path + + @return all The pointer of the file path. The file path is callee + allocated and should be freed by the caller. +**/ +EFI_DEVICE_PATH_PROTOCOL * +EFIAPI +EfiShellGetDevicePathFromFilePath ( + IN CONST CHAR16 *Path + ); + +/** + Gets the name of the device specified by the device handle. + + This function gets the user-readable name of the device specified by the device + handle. If no user-readable name could be generated, then *BestDeviceName will be + NULL and EFI_NOT_FOUND will be returned. + + If EFI_DEVICE_NAME_USE_COMPONENT_NAME is set, then the function will return the + device's name using the EFI_COMPONENT_NAME2_PROTOCOL, if present on + DeviceHandle. + + If EFI_DEVICE_NAME_USE_DEVICE_PATH is set, then the function will return the + device's name using the EFI_DEVICE_PATH_PROTOCOL, if present on DeviceHandle. + If both EFI_DEVICE_NAME_USE_COMPONENT_NAME and + EFI_DEVICE_NAME_USE_DEVICE_PATH are set, then + EFI_DEVICE_NAME_USE_COMPONENT_NAME will have higher priority. + + @param DeviceHandle The handle of the device. + @param Flags Determines the possible sources of component names. + Valid bits are: + EFI_DEVICE_NAME_USE_COMPONENT_NAME + EFI_DEVICE_NAME_USE_DEVICE_PATH + @param Language A pointer to the language specified for the device + name, in the same format as described in the UEFI + specification, Appendix M + @param BestDeviceName On return, points to the callee-allocated NULL- + terminated name of the device. If no device name + could be found, points to NULL. The name must be + freed by the caller... + + @retval EFI_SUCCESS Get the name successfully. + @retval EFI_NOT_FOUND Fail to get the device name. + @retval EFI_INVALID_PARAMETER Flags did not have a valid bit set. + @retval EFI_INVALID_PARAMETER BestDeviceName was NULL + @retval EFI_INVALID_PARAMETER DeviceHandle was NULL +**/ +EFI_STATUS +EFIAPI +EfiShellGetDeviceName ( + IN EFI_HANDLE DeviceHandle, + IN EFI_SHELL_DEVICE_NAME_FLAGS Flags, + IN CHAR8 *Language, + OUT CHAR16 **BestDeviceName + ); + +/** + Opens the root directory of a device on a handle + + This function opens the root directory of a device and returns a file handle to it. + + @param DeviceHandle The handle of the device that contains the volume. + @param FileHandle On exit, points to the file handle corresponding to the root directory on the + device. + + @retval EFI_SUCCESS Root opened successfully. + @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory + could not be opened. + @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted. + @retval EFI_DEVICE_ERROR The device had an error +**/ +EFI_STATUS +EFIAPI +EfiShellOpenRootByHandle ( + IN EFI_HANDLE DeviceHandle, + OUT SHELL_FILE_HANDLE *FileHandle + ); + +/** + Opens the root directory of a device. + + This function opens the root directory of a device and returns a file handle to it. + + @param DevicePath Points to the device path corresponding to the device where the + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL is installed. + @param FileHandle On exit, points to the file handle corresponding to the root directory on the + device. + + @retval EFI_SUCCESS Root opened successfully. + @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory + could not be opened. + @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted. + @retval EFI_DEVICE_ERROR The device had an error +**/ +EFI_STATUS +EFIAPI +EfiShellOpenRoot ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT SHELL_FILE_HANDLE *FileHandle + ); + +/** + Returns whether any script files are currently being processed. + + @retval TRUE There is at least one script file active. + @retval FALSE No script files are active now. + +**/ +BOOLEAN +EFIAPI +EfiShellBatchIsActive ( + VOID + ); + +/** + Worker function to open a file based on a device path. this will open the root + of the volume and then traverse down to the file itself. + + @param DevicePath2 Device Path of the file + @param FileHandle Pointer to the file upon a successful return + @param OpenMode mode to open file in. + @param Attributes the File Attributes to use when creating a new file + + @retval EFI_SUCCESS the file is open and FileHandle is valid + @retval EFI_UNSUPPORTED the device path contained non-path elements + @retval other an error occurred. +**/ +EFI_STATUS +InternalOpenFileDevicePath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath2, + OUT SHELL_FILE_HANDLE *FileHandle, + IN UINT64 OpenMode, + IN UINT64 Attributes OPTIONAL + ); + +/** + Creates a file or directory by name. + + This function creates an empty new file or directory with the specified attributes and + returns the new file's handle. If the file already exists and is read-only, then + EFI_INVALID_PARAMETER will be returned. + + If the file already existed, it is truncated and its attributes updated. If the file is + created successfully, the FileHandle is the file's handle, else, the FileHandle is NULL. + + If the file name begins with >v, then the file handle which is returned refers to the + shell environment variable with the specified name. If the shell environment variable + already exists and is non-volatile then EFI_INVALID_PARAMETER is returned. + + @param FileName Pointer to NULL-terminated file path + @param FileAttribs The new file's attributes. the different attributes are + described in EFI_FILE_PROTOCOL.Open(). + @param FileHandle On return, points to the created file handle or directory's handle + + @retval EFI_SUCCESS The file was opened. FileHandle points to the new file's handle. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + @retval EFI_UNSUPPORTED could not open the file path + @retval EFI_NOT_FOUND the specified file could not be found on the devide, or could not + file the file system on the device. + @retval EFI_NO_MEDIA the device has no medium. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no + longer supported. + @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according + the DirName. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write + when the media is write-protected. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. + @retval EFI_VOLUME_FULL The volume is full. +**/ +EFI_STATUS +EFIAPI +EfiShellCreateFile ( + IN CONST CHAR16 *FileName, + IN UINT64 FileAttribs, + OUT SHELL_FILE_HANDLE *FileHandle + ); + +/** + Opens a file or a directory by file name. + + This function opens the specified file in the specified OpenMode and returns a file + handle. + If the file name begins with >v, then the file handle which is returned refers to the + shell environment variable with the specified name. If the shell environment variable + exists, is non-volatile and the OpenMode indicates EFI_FILE_MODE_WRITE, then + EFI_INVALID_PARAMETER is returned. + + If the file name is >i, then the file handle which is returned refers to the standard + input. If the OpenMode indicates EFI_FILE_MODE_WRITE, then EFI_INVALID_PARAMETER + is returned. + + If the file name is >o, then the file handle which is returned refers to the standard + output. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER + is returned. + + If the file name is >e, then the file handle which is returned refers to the standard + error. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER + is returned. + + If the file name is NUL, then the file handle that is returned refers to the standard NUL + file. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER is + returned. + + If return EFI_SUCCESS, the FileHandle is the opened file's handle, else, the + FileHandle is NULL. + + @param FileName Points to the NULL-terminated UCS-2 encoded file name. + @param FileHandle On return, points to the file handle. + @param OpenMode File open mode. Either EFI_FILE_MODE_READ or + EFI_FILE_MODE_WRITE from section 12.4 of the UEFI + Specification. + @retval EFI_SUCCESS The file was opened. FileHandle has the opened file's handle. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. FileHandle is NULL. + @retval EFI_UNSUPPORTED Could not open the file path. FileHandle is NULL. + @retval EFI_NOT_FOUND The specified file could not be found on the device or the file + system could not be found on the device. FileHandle is NULL. + @retval EFI_NO_MEDIA The device has no medium. FileHandle is NULL. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no + longer supported. FileHandle is NULL. + @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according + the FileName. FileHandle is NULL. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. FileHandle is NULL. + @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write + when the media is write-protected. FileHandle is NULL. + @retval EFI_ACCESS_DENIED The service denied access to the file. FileHandle is NULL. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. FileHandle + is NULL. + @retval EFI_VOLUME_FULL The volume is full. FileHandle is NULL. +**/ +EFI_STATUS +EFIAPI +EfiShellOpenFileByName ( + IN CONST CHAR16 *FileName, + OUT SHELL_FILE_HANDLE *FileHandle, + IN UINT64 OpenMode + ); + +/** + Deletes the file specified by the file name. + + This function deletes a file. + + @param FileName Points to the NULL-terminated file name. + + @retval EFI_SUCCESS The file was closed and deleted, and the handle was closed. + @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted. + @sa EfiShellCreateFile + @sa FileHandleDelete +**/ +EFI_STATUS +EFIAPI +EfiShellDeleteFileByName ( + IN CONST CHAR16 *FileName + ); + +/** + Disables the page break output mode. +**/ +VOID +EFIAPI +EfiShellDisablePageBreak ( + VOID + ); + +/** + Enables the page break output mode. +**/ +VOID +EFIAPI +EfiShellEnablePageBreak ( + VOID + ); + +/** + internal worker function to run a command via Device Path + + @param ParentImageHandle A handle of the image that is executing the specified + command line. + @param DevicePath device path of the file to execute + @param CommandLine Points to the NULL-terminated UCS-2 encoded string + containing the command line. If NULL then the command- + line will be empty. + @param Environment Points to a NULL-terminated array of environment + variables with the format 'x=y', where x is the + environment variable name and y is the value. If this + is NULL, then the current shell environment is used. + @param[out] StartImageStatus Returned status from gBS->StartImage. + + @retval EFI_SUCCESS The command executed successfully. The status code + returned by the command is pointed to by StatusCode. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Out of resources. + @retval EFI_UNSUPPORTED Nested shell invocations are not allowed. +**/ +EFI_STATUS +InternalShellExecuteDevicePath ( + IN CONST EFI_HANDLE *ParentImageHandle, + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN CONST CHAR16 *CommandLine OPTIONAL, + IN CONST CHAR16 **Environment OPTIONAL, + OUT EFI_STATUS *StartImageStatus OPTIONAL + ); + +/** + Execute the command line. + + This function creates a nested instance of the shell and executes the specified + command (CommandLine) with the specified environment (Environment). Upon return, + the status code returned by the specified command is placed in StatusCode. + + If Environment is NULL, then the current environment is used and all changes made + by the commands executed will be reflected in the current environment. If the + Environment is non-NULL, then the changes made will be discarded. + + The CommandLine is executed from the current working directory on the current + device. + + @param ParentImageHandle A handle of the image that is executing the specified + command line. + @param CommandLine Points to the NULL-terminated UCS-2 encoded string + containing the command line. If NULL then the command- + line will be empty. + @param Environment Points to a NULL-terminated array of environment + variables with the format 'x=y', where x is the + environment variable name and y is the value. If this + is NULL, then the current shell environment is used. + @param StatusCode Points to the status code returned by the command. + + @retval EFI_SUCCESS The command executed successfully. The status code + returned by the command is pointed to by StatusCode. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Out of resources. + @retval EFI_UNSUPPORTED Nested shell invocations are not allowed. +**/ +EFI_STATUS +EFIAPI +EfiShellExecute ( + IN EFI_HANDLE *ParentImageHandle, + IN CHAR16 *CommandLine OPTIONAL, + IN CHAR16 **Environment OPTIONAL, + OUT EFI_STATUS *StatusCode OPTIONAL + ); + +/** + Utility cleanup function for EFI_SHELL_FILE_INFO objects. + + 1) frees all pointers (non-NULL) + 2) Closes the SHELL_FILE_HANDLE + + @param FileListNode pointer to the list node to free +**/ +VOID +FreeShellFileInfoNode ( + IN EFI_SHELL_FILE_INFO *FileListNode + ); + +/** + Frees the file list. + + This function cleans up the file list and any related data structures. It has no + impact on the files themselves. + + @param FileList The file list to free. Type EFI_SHELL_FILE_INFO is + defined in OpenFileList() + + @retval EFI_SUCCESS Free the file list successfully. + @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL; +**/ +EFI_STATUS +EFIAPI +EfiShellFreeFileList ( + IN EFI_SHELL_FILE_INFO **FileList + ); + +/** + Deletes the duplicate file names files in the given file list. + + This function deletes the reduplicate files in the given file list. + + @param FileList A pointer to the first entry in the file list. + + @retval EFI_SUCCESS Always success. + @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL; +**/ +EFI_STATUS +EFIAPI +EfiShellRemoveDupInFileList ( + IN EFI_SHELL_FILE_INFO **FileList + ); + +/** + Allocates and populates a EFI_SHELL_FILE_INFO structure. if any memory operation + failed it will return NULL. + + @param[in] BasePath the Path to prepend onto filename for FullPath + @param[in] Status Status member initial value. + @param[in] FileName FileName member initial value. + @param[in] Handle Handle member initial value. + @param[in] Info Info struct to copy. + +**/ +EFI_SHELL_FILE_INFO * +CreateAndPopulateShellFileInfo ( + IN CONST CHAR16 *BasePath, + IN CONST EFI_STATUS Status, + IN CONST CHAR16 *FileName, + IN CONST SHELL_FILE_HANDLE Handle, + IN CONST EFI_FILE_INFO *Info + ); + +/** + Find all files in a specified directory. + + @param FileDirHandle Handle of the directory to search. + @param FileList On return, points to the list of files in the directory + or NULL if there are no files in the directory. + + @retval EFI_SUCCESS File information was returned successfully. + @retval EFI_VOLUME_CORRUPTED The file system structures have been corrupted. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_NO_MEDIA The device media is not present. + @retval EFI_INVALID_PARAMETER The FileDirHandle was not a directory. +**/ +EFI_STATUS +EFIAPI +EfiShellFindFilesInDir ( + IN SHELL_FILE_HANDLE FileDirHandle, + OUT EFI_SHELL_FILE_INFO **FileList + ); + +/** + Find files that match a specified pattern. + + This function searches for all files and directories that match the specified + FilePattern. The FilePattern can contain wild-card characters. The resulting file + information is placed in the file list FileList. + + Wildcards are processed + according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. + + The files in the file list are not opened. The OpenMode field is set to 0 and the FileInfo + field is set to NULL. + + if *FileList is not NULL then it must be a pre-existing and properly initialized list. + + @param FilePattern Points to a NULL-terminated shell file path, including wildcards. + @param FileList On return, points to the start of a file list containing the names + of all matching files or else points to NULL if no matching files + were found. only on a EFI_SUCCESS return will; this be non-NULL. + + @retval EFI_SUCCESS Files found. FileList is a valid list. + @retval EFI_NOT_FOUND No files found. + @retval EFI_NO_MEDIA The device has no media + @retval EFI_DEVICE_ERROR The device reported an error + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted +**/ +EFI_STATUS +EFIAPI +EfiShellFindFiles ( + IN CONST CHAR16 *FilePattern, + OUT EFI_SHELL_FILE_INFO **FileList + ); + +/** + Opens the files that match the path specified. + + This function opens all of the files specified by Path. Wildcards are processed + according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. Each + matching file has an EFI_SHELL_FILE_INFO structure created in a linked list. + + @param Path A pointer to the path string. + @param OpenMode Specifies the mode used to open each file, EFI_FILE_MODE_READ or + EFI_FILE_MODE_WRITE. + @param FileList Points to the start of a list of files opened. + + @retval EFI_SUCCESS Create the file list successfully. + @return Others Can't create the file list. +**/ +EFI_STATUS +EFIAPI +EfiShellOpenFileList ( + IN CHAR16 *Path, + IN UINT64 OpenMode, + IN OUT EFI_SHELL_FILE_INFO **FileList + ); + +/** + Gets the environment variable. + + This function returns the current value of the specified environment variable. + + @param Name A pointer to the environment variable name + + @retval !=NULL The environment variable's value. The returned + pointer does not need to be freed by the caller. + @retval NULL The environment variable doesn't exist. +**/ +CONST CHAR16 * +EFIAPI +EfiShellGetEnv ( + IN CONST CHAR16 *Name + ); + +/** + Sets the environment variable. + + This function changes the current value of the specified environment variable. If the + environment variable exists and the Value is an empty string, then the environment + variable is deleted. If the environment variable exists and the Value is not an empty + string, then the value of the environment variable is changed. If the environment + variable does not exist and the Value is an empty string, there is no action. If the + environment variable does not exist and the Value is a non-empty string, then the + environment variable is created and assigned the specified value. + + For a description of volatile and non-volatile environment variables, see UEFI Shell + 2.0 specification section 3.6.1. + + @param Name Points to the NULL-terminated environment variable name. + @param Value Points to the NULL-terminated environment variable value. If the value is an + empty string then the environment variable is deleted. + @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE). + + @retval EFI_SUCCESS The environment variable was successfully updated. +**/ +EFI_STATUS +EFIAPI +EfiShellSetEnv ( + IN CONST CHAR16 *Name, + IN CONST CHAR16 *Value, + IN BOOLEAN Volatile + ); + +/** + Returns the current directory on the specified device. + + If FileSystemMapping is NULL, it returns the current working directory. If the + FileSystemMapping is not NULL, it returns the current directory associated with the + FileSystemMapping. In both cases, the returned name includes the file system + mapping (i.e. fs0:\current-dir). + + @param FileSystemMapping A pointer to the file system mapping. If NULL, + then the current working directory is returned. + + @retval !=NULL The current directory. + @retval NULL Current directory does not exist. +**/ +CONST CHAR16 * +EFIAPI +EfiShellGetCurDir ( + IN CONST CHAR16 *FileSystemMapping OPTIONAL + ); + +/** + Changes the current directory on the specified device. + + If the FileSystem is NULL, and the directory Dir does not contain a file system's + mapped name, this function changes the current working directory. If FileSystem is + NULL and the directory Dir contains a mapped name, then the current file system and + the current directory on that file system are changed. + + If FileSystem is not NULL, and Dir is NULL, then this changes the current working file + system. + + If FileSystem is not NULL and Dir is not NULL, then this function changes the current + directory on the specified file system. + + If the current working directory or the current working file system is changed then the + %cwd% environment variable will be updated + + @param FileSystem A pointer to the file system's mapped name. If NULL, then the current working + directory is changed. + @param Dir Points to the NULL-terminated directory on the device specified by FileSystem. + + @retval EFI_SUCCESS The operation was successful +**/ +EFI_STATUS +EFIAPI +EfiShellSetCurDir ( + IN CONST CHAR16 *FileSystem OPTIONAL, + IN CONST CHAR16 *Dir + ); + +/** + Return help information about a specific command. + + This function returns the help information for the specified command. The help text + can be internal to the shell or can be from a UEFI Shell manual page. + + If Sections is specified, then each section name listed will be compared in a casesensitive + manner, to the section names described in Appendix B. If the section exists, + it will be appended to the returned help text. If the section does not exist, no + information will be returned. If Sections is NULL, then all help text information + available will be returned. + + @param Command Points to the NULL-terminated UEFI Shell command name. + @param Sections Points to the NULL-terminated comma-delimited + section names to return. If NULL, then all + sections will be returned. + @param HelpText On return, points to a callee-allocated buffer + containing all specified help text. + + @retval EFI_SUCCESS The help text was returned. + @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the + returned help text. + @retval EFI_INVALID_PARAMETER HelpText is NULL + @retval EFI_NOT_FOUND There is no help text available for Command. +**/ +EFI_STATUS +EFIAPI +EfiShellGetHelpText ( + IN CONST CHAR16 *Command, + IN CONST CHAR16 *Sections OPTIONAL, + OUT CHAR16 **HelpText + ); + +/** + Gets the enable status of the page break output mode. + + User can use this function to determine current page break mode. + + @retval TRUE The page break output mode is enabled + @retval FALSE The page break output mode is disabled +**/ +BOOLEAN +EFIAPI +EfiShellGetPageBreak ( + VOID + ); + +/** + Judges whether the active shell is the root shell. + + This function makes the user to know that whether the active Shell is the root shell. + + @retval TRUE The active Shell is the root Shell. + @retval FALSE The active Shell is NOT the root Shell. +**/ +BOOLEAN +EFIAPI +EfiShellIsRootShell ( + VOID + ); + +/** + This function returns the command associated with a alias or a list of all + alias'. + + @param[in] Command Points to the NULL-terminated shell alias. + If this parameter is NULL, then all + aliases will be returned in ReturnedData. + @param[out] Volatile upon return of a single command if TRUE indicates + this is stored in a volatile fashion. FALSE otherwise. + @return If Alias is not NULL, it will return a pointer to + the NULL-terminated command for that alias. + If Alias is NULL, ReturnedData points to a ';' + delimited list of alias (e.g. + ReturnedData = "dir;del;copy;mfp") that is NULL-terminated. + @retval NULL an error occurred + @retval NULL Alias was not a valid Alias +**/ +CONST CHAR16 * +EFIAPI +EfiShellGetAlias ( + IN CONST CHAR16 *Command, + OUT BOOLEAN *Volatile OPTIONAL + ); + +/** + Changes a shell command alias. + + This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias. + + this function does not check for built in alias'. + + @param[in] Command Points to the NULL-terminated shell command or existing alias. + @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and + Command refers to an alias, that alias will be deleted. + @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the + Alias being set will be stored in a non-volatile fashion. + + @retval EFI_SUCCESS Alias created or deleted successfully. + @retval EFI_NOT_FOUND the Alias intended to be deleted was not found +**/ +EFI_STATUS +InternalSetAlias ( + IN CONST CHAR16 *Command, + IN CONST CHAR16 *Alias OPTIONAL, + IN BOOLEAN Volatile + ); + +/** + Changes a shell command alias. + + This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias. + + + @param[in] Command Points to the NULL-terminated shell command or existing alias. + @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and + Command refers to an alias, that alias will be deleted. + @param[in] Replace If TRUE and the alias already exists, then the existing alias will be replaced. If + FALSE and the alias already exists, then the existing alias is unchanged and + EFI_ACCESS_DENIED is returned. + @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the + Alias being set will be stored in a non-volatile fashion. + + @retval EFI_SUCCESS Alias created or deleted successfully. + @retval EFI_NOT_FOUND the Alias intended to be deleted was not found + @retval EFI_ACCESS_DENIED The alias is a built-in alias or already existed and Replace was set to + FALSE. +**/ +EFI_STATUS +EFIAPI +EfiShellSetAlias ( + IN CONST CHAR16 *Command, + IN CONST CHAR16 *Alias OPTIONAL, + IN BOOLEAN Replace, + IN BOOLEAN Volatile + ); + +/** + Utility cleanup function for EFI_SHELL_FILE_INFO objects. + + 1) frees all pointers (non-NULL) + 2) Closes the SHELL_FILE_HANDLE + + @param FileListNode pointer to the list node to free +**/ +VOID +InternalFreeShellFileInfoNode ( + IN EFI_SHELL_FILE_INFO *FileListNode + ); + +/** + Internal variable setting function. Allows for setting of the read only variables. + + @param Name Points to the NULL-terminated environment variable name. + @param Value Points to the NULL-terminated environment variable value. If the value is an + empty string then the environment variable is deleted. + @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE). + + @retval EFI_SUCCESS The environment variable was successfully updated. +**/ +EFI_STATUS +InternalEfiShellSetEnv ( + IN CONST CHAR16 *Name, + IN CONST CHAR16 *Value, + IN BOOLEAN Volatile + ); + +/** + Function to start monitoring for CTRL-C using SimpleTextInputEx. This + feature's enabled state was not known when the shell initially launched. + + @retval EFI_SUCCESS The feature is enabled. + @retval EFI_OUT_OF_RESOURCES There is not enough memory available. +**/ +EFI_STATUS +InernalEfiShellStartMonitor ( + VOID + ); + +/** + Notification function for keystrokes. + + @param[in] KeyData The key that was pressed. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +EFIAPI +NotificationFunction ( + IN EFI_KEY_DATA *KeyData + ); + +#endif //_SHELL_PROTOCOL_HEADER_ diff --git a/ShellPkg/Application/ShellCTestApp/README.txt b/ShellPkg/Application/ShellCTestApp/README.txt index d788d3ef62..6a5f463a3d 100644 --- a/ShellPkg/Application/ShellCTestApp/README.txt +++ b/ShellPkg/Application/ShellCTestApp/README.txt @@ -1,5 +1,5 @@ -TestArgv.nsh is a very simple shell script to test how the interpreter parses
-the parameters. It uses ShellCTestApp.efi to dump the parameters passed from the
-interpreter.
-
-TestArgv.log is the desired output created using "TestArgv.nsh > TestArgv.log".
+TestArgv.nsh is a very simple shell script to test how the interpreter parses +the parameters. It uses ShellCTestApp.efi to dump the parameters passed from the +interpreter. + +TestArgv.log is the desired output created using "TestArgv.nsh > TestArgv.log". diff --git a/ShellPkg/Application/ShellCTestApp/ShellCTestApp.c b/ShellPkg/Application/ShellCTestApp/ShellCTestApp.c index e5f7b89c62..f8616188ef 100644 --- a/ShellPkg/Application/ShellCTestApp/ShellCTestApp.c +++ b/ShellPkg/Application/ShellCTestApp/ShellCTestApp.c @@ -1,47 +1,47 @@ -/** @file
- This is a test application that demonstrates how to use the C-style entry point
- for a shell application.
-
- Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include <Uefi.h>
-#include <Library/UefiLib.h>
-#include <Library/DebugLib.h>
-#include <Library/ShellCEntryLib.h>
-
-/**
- UEFI application entry point which has an interface similar to a
- standard C main function.
-
- The ShellCEntryLib library instance wrappers the actual UEFI application
- entry point and calls this ShellAppMain function.
-
- @param[in] Argc The number of items in Argv.
- @param[in] Argv Array of pointers to strings.
-
- @retval 0 The application exited normally.
- @retval Other An error occurred.
-
-**/
-INTN
-EFIAPI
-ShellAppMain (
- IN UINTN Argc,
- IN CHAR16 **Argv
- )
-{
- UINTN Index;
-
- if (Argc == 1) {
- Print (L"Argv[1] = NULL\n");
- }
-
- for (Index = 1; Index < Argc; Index++) {
- Print (L"Argv[%d]: \"%s\"\n", Index, Argv[Index]);
- }
-
- return 0;
-}
+/** @file + This is a test application that demonstrates how to use the C-style entry point + for a shell application. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi.h> +#include <Library/UefiLib.h> +#include <Library/DebugLib.h> +#include <Library/ShellCEntryLib.h> + +/** + UEFI application entry point which has an interface similar to a + standard C main function. + + The ShellCEntryLib library instance wrappers the actual UEFI application + entry point and calls this ShellAppMain function. + + @param[in] Argc The number of items in Argv. + @param[in] Argv Array of pointers to strings. + + @retval 0 The application exited normally. + @retval Other An error occurred. + +**/ +INTN +EFIAPI +ShellAppMain ( + IN UINTN Argc, + IN CHAR16 **Argv + ) +{ + UINTN Index; + + if (Argc == 1) { + Print (L"Argv[1] = NULL\n"); + } + + for (Index = 1; Index < Argc; Index++) { + Print (L"Argv[%d]: \"%s\"\n", Index, Argv[Index]); + } + + return 0; +} diff --git a/ShellPkg/Application/ShellCTestApp/ShellCTestApp.inf b/ShellPkg/Application/ShellCTestApp/ShellCTestApp.inf index a0211caf3d..de242b245d 100644 --- a/ShellPkg/Application/ShellCTestApp/ShellCTestApp.inf +++ b/ShellPkg/Application/ShellCTestApp/ShellCTestApp.inf @@ -1,34 +1,34 @@ -## @file
-# This is the shell application
-#
-# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
-#
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
-#
-##
-
-[Defines]
- INF_VERSION = 0x00010006
- BASE_NAME = ShellCTestApp
- FILE_GUID = 7a6ca3b8-ee1b-489c-b300-24544a7bd418
- MODULE_TYPE = UEFI_APPLICATION
- VERSION_STRING = 1.0
- ENTRY_POINT = ShellCEntryLib
-
-#
-# The following information is for reference only and not required by the build tools.
-#
-# VALID_ARCHITECTURES = IA32 X64 EBC
-#
-
-[Sources]
- ShellCTestApp.c
-
-[Packages]
- MdePkg/MdePkg.dec
- ShellPkg/ShellPkg.dec
-
-[LibraryClasses]
- ShellCEntryLib
- UefiLib
+## @file +# This is the shell application +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = ShellCTestApp + FILE_GUID = 7a6ca3b8-ee1b-489c-b300-24544a7bd418 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = ShellCEntryLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + ShellCTestApp.c + +[Packages] + MdePkg/MdePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + ShellCEntryLib + UefiLib diff --git a/ShellPkg/Application/ShellCTestApp/TestArgv.nsh b/ShellPkg/Application/ShellCTestApp/TestArgv.nsh index f48c956a3b..5f2f3ed6bc 100644 --- a/ShellPkg/Application/ShellCTestApp/TestArgv.nsh +++ b/ShellPkg/Application/ShellCTestApp/TestArgv.nsh @@ -1,58 +1,58 @@ -#/** @file
-# This is a very simple shell script to test how the interpreter parses the parameters.
-#
-# Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
-#**/
-echo -on
-set Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA ValueOfGuid
-set Sharp_E8528E46_A008_4221_8DE0_D5AB42A9C580 ^#
-set Quote_E95DEE8B_E3AA_4155_9ED5_6916394104FC ^"
-set Var_ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE
-alias ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE ShellCTestApp
-
-#
-# '^' should escape all special characters (including space)
-# but has no impact to non-special characters
-#
-ShellCTestApp ^^
-ShellCTestApp ^#
-ShellCTestApp ^%Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA%
-ShellCTestApp ^"
-ShellCTestApp ^ 1
-ShellCTestApp ^
-ShellCTestApp ^1
-ShellCTestApp ^^^"
-ShellCTestApp ^^^
-
-#
-# '#' should be processed before %% replacement, and inside '"'
-#
-ShellCTestApp #%Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA%
-#ShellCTestApp "#"
-ShellCTestApp %Sharp_E8528E46_A008_4221_8DE0_D5AB42A9C580%
-
-#
-# '%' should be processed before grouping parameters
-#
-ShellCTestApp "%Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA% 2%Quote_E95DEE8B_E3AA_4155_9ED5_6916394104FC%
-
-#
-# alias should be processed after %% replacement
-#
-%Var_ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE%
-
-#
-# '"' should be stripped, space inside '"' should be kept,
-#
-ShellCTestApp "p 1"
-ShellCTestApp "p"1
-ShellCTestApp "p 1"e"x"""
-
-set -d Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA
-set -d Sharp_E8528E46_A008_4221_8DE0_D5AB42A9C580
-set -d Quote_E95DEE8B_E3AA_4155_9ED5_6916394104FC
-set -d Var_ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE
-alias -d ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE
+#/** @file +# This is a very simple shell script to test how the interpreter parses the parameters. +# +# Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ +echo -on +set Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA ValueOfGuid +set Sharp_E8528E46_A008_4221_8DE0_D5AB42A9C580 ^# +set Quote_E95DEE8B_E3AA_4155_9ED5_6916394104FC ^" +set Var_ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE +alias ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE ShellCTestApp + +# +# '^' should escape all special characters (including space) +# but has no impact to non-special characters +# +ShellCTestApp ^^ +ShellCTestApp ^# +ShellCTestApp ^%Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA% +ShellCTestApp ^" +ShellCTestApp ^ 1 +ShellCTestApp ^ +ShellCTestApp ^1 +ShellCTestApp ^^^" +ShellCTestApp ^^^ + +# +# '#' should be processed before %% replacement, and inside '"' +# +ShellCTestApp #%Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA% +#ShellCTestApp "#" +ShellCTestApp %Sharp_E8528E46_A008_4221_8DE0_D5AB42A9C580% + +# +# '%' should be processed before grouping parameters +# +ShellCTestApp "%Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA% 2%Quote_E95DEE8B_E3AA_4155_9ED5_6916394104FC% + +# +# alias should be processed after %% replacement +# +%Var_ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE% + +# +# '"' should be stripped, space inside '"' should be kept, +# +ShellCTestApp "p 1" +ShellCTestApp "p"1 +ShellCTestApp "p 1"e"x""" + +set -d Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA +set -d Sharp_E8528E46_A008_4221_8DE0_D5AB42A9C580 +set -d Quote_E95DEE8B_E3AA_4155_9ED5_6916394104FC +set -d Var_ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE +alias -d ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE echo -off
\ No newline at end of file diff --git a/ShellPkg/Application/ShellExecTestApp/SA.c b/ShellPkg/Application/ShellExecTestApp/SA.c index cfaeaae0d4..6bd092076e 100644 --- a/ShellPkg/Application/ShellExecTestApp/SA.c +++ b/ShellPkg/Application/ShellExecTestApp/SA.c @@ -1,32 +1,32 @@ -/** @file
- This is a simple shell application
-
- Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include <Uefi.h>
-#include <Library/UefiApplicationEntryPoint.h>
-#include <Library/UefiLib.h>
-
-/**
- as the real entry point for the application.
-
- @param[in] ImageHandle The firmware allocated handle for the EFI image.
- @param[in] SystemTable A pointer to the EFI System Table.
-
- @retval EFI_SUCCESS The entry point is executed successfully.
- @retval other Some error occurs when executing this entry point.
-
-**/
-EFI_STATUS
-EFIAPI
-UefiMain (
- IN EFI_HANDLE ImageHandle,
- IN EFI_SYSTEM_TABLE *SystemTable
- )
-{
- Print (L"ShellExecute - Pass");
- return EFI_SUCCESS;
-}
+/** @file + This is a simple shell application + + Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi.h> +#include <Library/UefiApplicationEntryPoint.h> +#include <Library/UefiLib.h> + +/** + as the real entry point for the application. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + Print (L"ShellExecute - Pass"); + return EFI_SUCCESS; +} diff --git a/ShellPkg/Application/ShellExecTestApp/SA.inf b/ShellPkg/Application/ShellExecTestApp/SA.inf index 49a4f29354..feb91f184c 100644 --- a/ShellPkg/Application/ShellExecTestApp/SA.inf +++ b/ShellPkg/Application/ShellExecTestApp/SA.inf @@ -1,36 +1,36 @@ -## @file
-# Sample UEFI Application Reference EDKII Module
-#
-# This is a simple shell application
-#
-# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
-#
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
-#
-##
-
-[Defines]
- INF_VERSION = 0x00010005
- BASE_NAME = EmptyApplication
- FILE_GUID = 8F7D7B1D-0E1C-4c98-B12E-4EC99C4081AC
- MODULE_TYPE = UEFI_APPLICATION
- VERSION_STRING = 1.0
- ENTRY_POINT = UefiMain
-
-#
-# The following information is for reference only and not required by the build tools.
-#
-# VALID_ARCHITECTURES = IA32 X64 EBC
-#
-
-[Sources]
- SA.c
-
-[Packages]
- MdePkg/MdePkg.dec
-
-[LibraryClasses]
- UefiApplicationEntryPoint
- UefiLib
-
+## @file +# Sample UEFI Application Reference EDKII Module +# +# This is a simple shell application +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EmptyApplication + FILE_GUID = 8F7D7B1D-0E1C-4c98-B12E-4EC99C4081AC + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = UefiMain + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + SA.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiApplicationEntryPoint + UefiLib + diff --git a/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.c b/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.c index 093633ee27..979c05b8a1 100644 --- a/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.c +++ b/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.c @@ -1,82 +1,82 @@ -/** @file
- This is a test application that demonstrates how to use the sorting functions.
-
- Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include <Uefi.h>
-#include <Library/UefiLib.h>
-#include <Library/DebugLib.h>
-#include <Library/ShellCEntryLib.h>
-#include <Library/SortLib.h>
-
-/**
- Test comparator.
-
- @param[in] b1 The first INTN
- @param[in] b2 The other INTN
-
- @retval 0 They are the same.
- @retval -1 b1 is less than b2
- @retval 1 b1 is greater then b2
-**/
-INTN
-EFIAPI
-Test (
- CONST VOID *b1,
- CONST VOID *b2
- )
-{
- if (*(INTN *)b1 == *(INTN *)b2) {
- return (0);
- }
-
- if (*(INTN *)b1 < *(INTN *)b2) {
- return (-1);
- }
-
- return (1);
-}
-
-/**
- UEFI application entry point which has an interface similar to a
- standard C main function.
-
- The ShellCEntryLib library instance wrappers the actual UEFI application
- entry point and calls this ShellAppMain function.
-
- @param Argc Argument count
- @param Argv The parsed arguments
-
- @retval 0 The application exited normally.
- @retval Other An error occurred.
-
-**/
-INTN
-EFIAPI
-ShellAppMain (
- IN UINTN Argc,
- IN CHAR16 **Argv
- )
-{
- INTN Array[10];
-
- Array[0] = 2;
- Array[1] = 3;
- Array[2] = 4;
- Array[3] = 1;
- Array[4] = 5;
- Array[5] = 6;
- Array[6] = 7;
- Array[7] = 8;
- Array[8] = 1;
- Array[9] = 5;
-
- Print (L"Array = %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\r\n", Array[0], Array[1], Array[2], Array[3], Array[4], Array[5], Array[6], Array[7], Array[8], Array[9]);
- PerformQuickSort (Array, 10, sizeof (INTN), Test);
- Print (L"POST-SORT\r\n");
- Print (L"Array = %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\r\n", Array[0], Array[1], Array[2], Array[3], Array[4], Array[5], Array[6], Array[7], Array[8], Array[9]);
- return 0;
-}
+/** @file + This is a test application that demonstrates how to use the sorting functions. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi.h> +#include <Library/UefiLib.h> +#include <Library/DebugLib.h> +#include <Library/ShellCEntryLib.h> +#include <Library/SortLib.h> + +/** + Test comparator. + + @param[in] b1 The first INTN + @param[in] b2 The other INTN + + @retval 0 They are the same. + @retval -1 b1 is less than b2 + @retval 1 b1 is greater then b2 +**/ +INTN +EFIAPI +Test ( + CONST VOID *b1, + CONST VOID *b2 + ) +{ + if (*(INTN *)b1 == *(INTN *)b2) { + return (0); + } + + if (*(INTN *)b1 < *(INTN *)b2) { + return (-1); + } + + return (1); +} + +/** + UEFI application entry point which has an interface similar to a + standard C main function. + + The ShellCEntryLib library instance wrappers the actual UEFI application + entry point and calls this ShellAppMain function. + + @param Argc Argument count + @param Argv The parsed arguments + + @retval 0 The application exited normally. + @retval Other An error occurred. + +**/ +INTN +EFIAPI +ShellAppMain ( + IN UINTN Argc, + IN CHAR16 **Argv + ) +{ + INTN Array[10]; + + Array[0] = 2; + Array[1] = 3; + Array[2] = 4; + Array[3] = 1; + Array[4] = 5; + Array[5] = 6; + Array[6] = 7; + Array[7] = 8; + Array[8] = 1; + Array[9] = 5; + + Print (L"Array = %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\r\n", Array[0], Array[1], Array[2], Array[3], Array[4], Array[5], Array[6], Array[7], Array[8], Array[9]); + PerformQuickSort (Array, 10, sizeof (INTN), Test); + Print (L"POST-SORT\r\n"); + Print (L"Array = %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\r\n", Array[0], Array[1], Array[2], Array[3], Array[4], Array[5], Array[6], Array[7], Array[8], Array[9]); + return 0; +} diff --git a/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.inf b/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.inf index 399b997230..9b2c12377b 100644 --- a/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.inf +++ b/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.inf @@ -1,37 +1,37 @@ -## @file
-# This is the shell sorting testing application
-#
-# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
-#
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
-#
-##
-
-[Defines]
- INF_VERSION = 0x00010006
- BASE_NAME = ShellSortTestApp
- FILE_GUID = 079E8E98-AE93-4b9a-8A71-1DC869F23E09
- MODULE_TYPE = UEFI_APPLICATION
- VERSION_STRING = 1.0
- ENTRY_POINT = ShellCEntryLib
-
-#
-# The following information is for reference only and not required by the build tools.
-#
-# VALID_ARCHITECTURES = IA32 X64 EBC
-#
-
-[Sources]
- ShellSortTestApp.c
-
-[Packages]
- MdePkg/MdePkg.dec
- ShellPkg/ShellPkg.dec
- MdeModulePkg/MdeModulePkg.dec
-
-[LibraryClasses]
- ShellCEntryLib
- UefiLib
- SortLib
-
+## @file +# This is the shell sorting testing application +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = ShellSortTestApp + FILE_GUID = 079E8E98-AE93-4b9a-8A71-1DC869F23E09 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = ShellCEntryLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + ShellSortTestApp.c + +[Packages] + MdePkg/MdePkg.dec + ShellPkg/ShellPkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + ShellCEntryLib + UefiLib + SortLib + |
