diff options
Diffstat (limited to 'ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c')
| -rw-r--r-- | ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c | 4656 |
1 files changed, 2328 insertions, 2328 deletions
diff --git a/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c b/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c index 75fbd50cbe..624322fe8a 100644 --- a/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c +++ b/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c @@ -1,2328 +1,2328 @@ -/** @file
- Provides interface to shell internal functions for shell commands.
-
- Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
- (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
- (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
-
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "UefiShellCommandLib.h"
-
-// STATIC local variables
-STATIC SHELL_COMMAND_INTERNAL_LIST_ENTRY mCommandList;
-STATIC SCRIPT_FILE_LIST mScriptList;
-STATIC ALIAS_LIST mAliasList;
-STATIC BOOLEAN mEchoState;
-STATIC BOOLEAN mExitRequested;
-STATIC UINT64 mExitCode;
-STATIC BOOLEAN mExitScript;
-STATIC CHAR16 *mProfileList;
-STATIC UINTN mProfileListSize;
-STATIC UINTN mFsMaxCount = 0;
-STATIC UINTN mBlkMaxCount = 0;
-STATIC BUFFER_LIST mFileHandleList;
-
-STATIC CONST CHAR8 Hex[] = {
- '0',
- '1',
- '2',
- '3',
- '4',
- '5',
- '6',
- '7',
- '8',
- '9',
- 'A',
- 'B',
- 'C',
- 'D',
- 'E',
- 'F'
-};
-
-// global variables required by library class.
-EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollation = NULL;
-SHELL_MAP_LIST gShellMapList;
-SHELL_MAP_LIST *gShellCurMapping = NULL;
-
-CONST CHAR16 *SupportLevel[] = {
- L"Minimal",
- L"Scripting",
- L"Basic",
- L"Interactive"
-};
-
-/**
- Function to make sure that the global protocol pointers are valid.
- must be called after constructor before accessing the pointers.
-**/
-EFI_STATUS
-EFIAPI
-CommandInit (
- VOID
- )
-{
- UINTN NumHandles;
- EFI_HANDLE *Handles;
- EFI_UNICODE_COLLATION_PROTOCOL *Uc;
- CHAR8 *BestLanguage;
- UINTN Index;
- EFI_STATUS Status;
- CHAR8 *PlatformLang;
-
- if (gUnicodeCollation == NULL) {
- GetEfiGlobalVariable2 (EFI_PLATFORM_LANG_VARIABLE_NAME, (VOID **)&PlatformLang, NULL);
-
- Status = gBS->LocateHandleBuffer (
- ByProtocol,
- &gEfiUnicodeCollation2ProtocolGuid,
- NULL,
- &NumHandles,
- &Handles
- );
- if (EFI_ERROR (Status)) {
- NumHandles = 0;
- Handles = NULL;
- }
-
- for (Index = 0; Index < NumHandles; Index++) {
- //
- // Open Unicode Collation Protocol
- //
- Status = gBS->OpenProtocol (
- Handles[Index],
- &gEfiUnicodeCollation2ProtocolGuid,
- (VOID **)&Uc,
- gImageHandle,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL
- );
- if (EFI_ERROR (Status)) {
- continue;
- }
-
- //
- // Without clue provided use the first Unicode Collation2 protocol.
- // This may happen when PlatformLang is NULL or when no installed Unicode
- // Collation2 protocol instance supports PlatformLang.
- //
- if (gUnicodeCollation == NULL) {
- gUnicodeCollation = Uc;
- }
-
- if (PlatformLang == NULL) {
- break;
- }
-
- //
- // Find the best matching matching language from the supported languages
- // of Unicode Collation2 protocol.
- //
- BestLanguage = GetBestLanguage (
- Uc->SupportedLanguages,
- FALSE,
- PlatformLang,
- NULL
- );
- if (BestLanguage != NULL) {
- FreePool (BestLanguage);
- gUnicodeCollation = Uc;
- break;
- }
- }
-
- if (Handles != NULL) {
- FreePool (Handles);
- }
-
- if (PlatformLang != NULL) {
- FreePool (PlatformLang);
- }
- }
-
- return (gUnicodeCollation == NULL) ? EFI_UNSUPPORTED : EFI_SUCCESS;
-}
-
-/**
- Constructor for the Shell Command library.
-
- Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell.
-
- @param ImageHandle the image handle of the process
- @param SystemTable the EFI System Table pointer
-
- @retval EFI_SUCCESS the initialization was complete successfully
-**/
-RETURN_STATUS
-EFIAPI
-ShellCommandLibConstructor (
- IN EFI_HANDLE ImageHandle,
- IN EFI_SYSTEM_TABLE *SystemTable
- )
-{
- EFI_STATUS Status;
-
- InitializeListHead (&gShellMapList.Link);
- InitializeListHead (&mCommandList.Link);
- InitializeListHead (&mAliasList.Link);
- InitializeListHead (&mScriptList.Link);
- InitializeListHead (&mFileHandleList.Link);
- mEchoState = TRUE;
-
- mExitRequested = FALSE;
- mExitScript = FALSE;
- mProfileListSize = 0;
- mProfileList = NULL;
-
- Status = CommandInit ();
- if (EFI_ERROR (Status)) {
- return EFI_DEVICE_ERROR;
- }
-
- return (RETURN_SUCCESS);
-}
-
-/**
- Frees list of file handles.
-
- @param[in] List The list to free.
-**/
-VOID
-FreeFileHandleList (
- IN BUFFER_LIST *List
- )
-{
- BUFFER_LIST *BufferListEntry;
-
- if (List == NULL) {
- return;
- }
-
- //
- // enumerate through the buffer list and free all memory
- //
- for ( BufferListEntry = (BUFFER_LIST *)GetFirstNode (&List->Link)
- ; !IsListEmpty (&List->Link)
- ; BufferListEntry = (BUFFER_LIST *)GetFirstNode (&List->Link)
- )
- {
- RemoveEntryList (&BufferListEntry->Link);
- ASSERT (BufferListEntry->Buffer != NULL);
- SHELL_FREE_NON_NULL (((SHELL_COMMAND_FILE_HANDLE *)(BufferListEntry->Buffer))->Path);
- SHELL_FREE_NON_NULL (BufferListEntry->Buffer);
- SHELL_FREE_NON_NULL (BufferListEntry);
- }
-}
-
-/**
- Destructor for the library. free any resources.
-
- @param ImageHandle the image handle of the process
- @param SystemTable the EFI System Table pointer
-
- @retval RETURN_SUCCESS this function always returns success
-**/
-RETURN_STATUS
-EFIAPI
-ShellCommandLibDestructor (
- IN EFI_HANDLE ImageHandle,
- IN EFI_SYSTEM_TABLE *SystemTable
- )
-{
- SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
- ALIAS_LIST *Node2;
- SCRIPT_FILE_LIST *Node3;
- SHELL_MAP_LIST *MapNode;
-
- //
- // enumerate throught the list and free all the memory
- //
- while (!IsListEmpty (&mCommandList.Link)) {
- Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link);
- RemoveEntryList (&Node->Link);
- SHELL_FREE_NON_NULL (Node->CommandString);
- FreePool (Node);
- DEBUG_CODE (
- Node = NULL;
- );
- }
-
- //
- // enumerate through the alias list and free all memory
- //
- while (!IsListEmpty (&mAliasList.Link)) {
- Node2 = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link);
- RemoveEntryList (&Node2->Link);
- SHELL_FREE_NON_NULL (Node2->CommandString);
- SHELL_FREE_NON_NULL (Node2->Alias);
- SHELL_FREE_NON_NULL (Node2);
- DEBUG_CODE (
- Node2 = NULL;
- );
- }
-
- //
- // enumerate throught the list and free all the memory
- //
- while (!IsListEmpty (&mScriptList.Link)) {
- Node3 = (SCRIPT_FILE_LIST *)GetFirstNode (&mScriptList.Link);
- RemoveEntryList (&Node3->Link);
- DeleteScriptFileStruct (Node3->Data);
- FreePool (Node3);
- }
-
- //
- // enumerate throught the mappings list and free all the memory
- //
- if (!IsListEmpty (&gShellMapList.Link)) {
- for (MapNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link)
- ; !IsListEmpty (&gShellMapList.Link)
- ; MapNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link)
- )
- {
- ASSERT (MapNode != NULL);
- RemoveEntryList (&MapNode->Link);
- SHELL_FREE_NON_NULL (MapNode->DevicePath);
- SHELL_FREE_NON_NULL (MapNode->MapName);
- SHELL_FREE_NON_NULL (MapNode->CurrentDirectoryPath);
- FreePool (MapNode);
- }
- }
-
- if (!IsListEmpty (&mFileHandleList.Link)) {
- FreeFileHandleList (&mFileHandleList);
- }
-
- if (mProfileList != NULL) {
- FreePool (mProfileList);
- }
-
- gUnicodeCollation = NULL;
- gShellCurMapping = NULL;
-
- return (RETURN_SUCCESS);
-}
-
-/**
- Find a dynamic command protocol instance given a command name string.
-
- @param CommandString the command name string
-
- @return instance the command protocol instance, if dynamic command instance found
- @retval NULL no dynamic command protocol instance found for name
-**/
-CONST EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *
-ShellCommandFindDynamicCommand (
- IN CONST CHAR16 *CommandString
- )
-{
- EFI_STATUS Status;
- EFI_HANDLE *CommandHandleList;
- EFI_HANDLE *NextCommand;
- EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
-
- CommandHandleList = GetHandleListByProtocol (&gEfiShellDynamicCommandProtocolGuid);
- if (CommandHandleList == NULL) {
- //
- // not found or out of resources
- //
- return NULL;
- }
-
- for (NextCommand = CommandHandleList; *NextCommand != NULL; NextCommand++) {
- Status = gBS->HandleProtocol (
- *NextCommand,
- &gEfiShellDynamicCommandProtocolGuid,
- (VOID **)&DynamicCommand
- );
-
- if (EFI_ERROR (Status)) {
- continue;
- }
-
- if (gUnicodeCollation->StriColl (
- gUnicodeCollation,
- (CHAR16 *)CommandString,
- (CHAR16 *)DynamicCommand->CommandName
- ) == 0
- )
- {
- FreePool (CommandHandleList);
- return (DynamicCommand);
- }
- }
-
- FreePool (CommandHandleList);
- return (NULL);
-}
-
-/**
- Checks if a command exists as a dynamic command protocol instance
-
- @param[in] CommandString The command string to check for on the list.
-**/
-BOOLEAN
-ShellCommandDynamicCommandExists (
- IN CONST CHAR16 *CommandString
- )
-{
- return (BOOLEAN)((ShellCommandFindDynamicCommand (CommandString) != NULL));
-}
-
-/**
- Checks if a command is already on the internal command list.
-
- @param[in] CommandString The command string to check for on the list.
-**/
-BOOLEAN
-ShellCommandIsCommandOnInternalList (
- IN CONST CHAR16 *CommandString
- )
-{
- SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
-
- //
- // assert for NULL parameter
- //
- ASSERT (CommandString != NULL);
-
- //
- // check for the command
- //
- for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
- ; !IsNull (&mCommandList.Link, &Node->Link)
- ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Node->Link)
- )
- {
- ASSERT (Node->CommandString != NULL);
- if (gUnicodeCollation->StriColl (
- gUnicodeCollation,
- (CHAR16 *)CommandString,
- Node->CommandString
- ) == 0
- )
- {
- return (TRUE);
- }
- }
-
- return (FALSE);
-}
-
-/**
- Checks if a command exists, either internally or through the dynamic command protocol.
-
- @param[in] CommandString The command string to check for on the list.
-**/
-BOOLEAN
-EFIAPI
-ShellCommandIsCommandOnList (
- IN CONST CHAR16 *CommandString
- )
-{
- if (ShellCommandIsCommandOnInternalList (CommandString)) {
- return TRUE;
- }
-
- return ShellCommandDynamicCommandExists (CommandString);
-}
-
-/**
- Get the help text for a dynamic command.
-
- @param[in] CommandString The command name.
-
- @retval NULL No help text was found.
- @return String of help text. Caller required to free.
-**/
-CHAR16 *
-ShellCommandGetDynamicCommandHelp (
- IN CONST CHAR16 *CommandString
- )
-{
- EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
-
- DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *)ShellCommandFindDynamicCommand (CommandString);
- if (DynamicCommand == NULL) {
- return (NULL);
- }
-
- //
- // TODO: how to get proper language?
- //
- return DynamicCommand->GetHelp (DynamicCommand, "en");
-}
-
-/**
- Get the help text for an internal command.
-
- @param[in] CommandString The command name.
-
- @retval NULL No help text was found.
- @return String of help text. Caller reuiqred to free.
-**/
-CHAR16 *
-ShellCommandGetInternalCommandHelp (
- IN CONST CHAR16 *CommandString
- )
-{
- SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
-
- //
- // assert for NULL parameter
- //
- ASSERT (CommandString != NULL);
-
- //
- // check for the command
- //
- for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
- ; !IsNull (&mCommandList.Link, &Node->Link)
- ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Node->Link)
- )
- {
- ASSERT (Node->CommandString != NULL);
- if (gUnicodeCollation->StriColl (
- gUnicodeCollation,
- (CHAR16 *)CommandString,
- Node->CommandString
- ) == 0
- )
- {
- return (HiiGetString (Node->HiiHandle, Node->ManFormatHelp, NULL));
- }
- }
-
- return (NULL);
-}
-
-/**
- Get the help text for a command.
-
- @param[in] CommandString The command name.
-
- @retval NULL No help text was found.
- @return String of help text.Caller reuiqred to free.
-**/
-CHAR16 *
-EFIAPI
-ShellCommandGetCommandHelp (
- IN CONST CHAR16 *CommandString
- )
-{
- CHAR16 *HelpStr;
-
- HelpStr = ShellCommandGetInternalCommandHelp (CommandString);
-
- if (HelpStr == NULL) {
- HelpStr = ShellCommandGetDynamicCommandHelp (CommandString);
- }
-
- return HelpStr;
-}
-
-/**
- Registers handlers of type SHELL_RUN_COMMAND and
- SHELL_GET_MAN_FILENAME for each shell command.
-
- If the ShellSupportLevel is greater than the value of the
- PcdShellSupportLevel then return RETURN_UNSUPPORTED.
-
- Registers the handlers specified by GetHelpInfoHandler and CommandHandler
- with the command specified by CommandString. If the command named by
- CommandString has already been registered, then return
- RETURN_ALREADY_STARTED.
-
- If there are not enough resources available to register the handlers then
- RETURN_OUT_OF_RESOURCES is returned.
-
- If CommandString is NULL, then ASSERT().
- If GetHelpInfoHandler is NULL, then ASSERT().
- If CommandHandler is NULL, then ASSERT().
- If ProfileName is NULL, then ASSERT().
-
- @param[in] CommandString Pointer to the command name. This is the
- name to look for on the command line in
- the shell.
- @param[in] CommandHandler Pointer to a function that runs the
- specified command.
- @param[in] GetManFileName Pointer to a function that provides man
- filename.
- @param[in] ShellMinSupportLevel minimum Shell Support Level which has this
- function.
- @param[in] ProfileName profile name to require for support of this
- function.
- @param[in] CanAffectLE indicates whether this command's return value
- can change the LASTERROR environment variable.
- @param[in] HiiHandle Handle of this command's HII entry.
- @param[in] ManFormatHelp HII locator for the help text.
-
- @retval RETURN_SUCCESS The handlers were registered.
- @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
- register the shell command.
- @retval RETURN_UNSUPPORTED the ShellMinSupportLevel was higher than the
- currently allowed support level.
- @retval RETURN_ALREADY_STARTED The CommandString represents a command that
- is already registered. Only 1 handler set for
- a given command is allowed.
- @sa SHELL_GET_MAN_FILENAME
- @sa SHELL_RUN_COMMAND
-**/
-RETURN_STATUS
-EFIAPI
-ShellCommandRegisterCommandName (
- IN CONST CHAR16 *CommandString,
- IN SHELL_RUN_COMMAND CommandHandler,
- IN SHELL_GET_MAN_FILENAME GetManFileName,
- IN UINT32 ShellMinSupportLevel,
- IN CONST CHAR16 *ProfileName,
- IN CONST BOOLEAN CanAffectLE,
- IN CONST EFI_HII_HANDLE HiiHandle,
- IN CONST EFI_STRING_ID ManFormatHelp
- )
-{
- SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
- SHELL_COMMAND_INTERNAL_LIST_ENTRY *Command;
- SHELL_COMMAND_INTERNAL_LIST_ENTRY *PrevCommand;
- INTN LexicalMatchValue;
-
- //
- // Initialize local variables.
- //
- Command = NULL;
- PrevCommand = NULL;
- LexicalMatchValue = 0;
-
- //
- // ASSERTs for NULL parameters
- //
- ASSERT (CommandString != NULL);
- ASSERT (GetManFileName != NULL);
- ASSERT (CommandHandler != NULL);
- ASSERT (ProfileName != NULL);
-
- //
- // check for shell support level
- //
- if (PcdGet8 (PcdShellSupportLevel) < ShellMinSupportLevel) {
- return (RETURN_UNSUPPORTED);
- }
-
- //
- // check for already on the list
- //
- if (ShellCommandIsCommandOnList (CommandString)) {
- return (RETURN_ALREADY_STARTED);
- }
-
- //
- // allocate memory for new struct
- //
- Node = AllocateZeroPool (sizeof (SHELL_COMMAND_INTERNAL_LIST_ENTRY));
- if (Node == NULL) {
- return RETURN_OUT_OF_RESOURCES;
- }
-
- Node->CommandString = AllocateCopyPool (StrSize (CommandString), CommandString);
- if (Node->CommandString == NULL) {
- FreePool (Node);
- return RETURN_OUT_OF_RESOURCES;
- }
-
- Node->GetManFileName = GetManFileName;
- Node->CommandHandler = CommandHandler;
- Node->LastError = CanAffectLE;
- Node->HiiHandle = HiiHandle;
- Node->ManFormatHelp = ManFormatHelp;
-
- if ( (StrLen (ProfileName) > 0)
- && (( (mProfileList != NULL)
- && (StrStr (mProfileList, ProfileName) == NULL)) || (mProfileList == NULL))
- )
- {
- ASSERT ((mProfileList == NULL && mProfileListSize == 0) || (mProfileList != NULL));
- if (mProfileList == NULL) {
- //
- // If this is the first make a leading ';'
- //
- StrnCatGrow (&mProfileList, &mProfileListSize, L";", 0);
- }
-
- StrnCatGrow (&mProfileList, &mProfileListSize, ProfileName, 0);
- StrnCatGrow (&mProfileList, &mProfileListSize, L";", 0);
- }
-
- //
- // Insert a new entry on top of the list
- //
- InsertHeadList (&mCommandList.Link, &Node->Link);
-
- //
- // Move a new registered command to its sorted ordered location in the list
- //
- for (Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link),
- PrevCommand = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
- ; !IsNull (&mCommandList.Link, &Command->Link)
- ; Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Command->Link))
- {
- //
- // Get Lexical Comparison Value between PrevCommand and Command list entry
- //
- LexicalMatchValue = gUnicodeCollation->StriColl (
- gUnicodeCollation,
- PrevCommand->CommandString,
- Command->CommandString
- );
-
- //
- // Swap PrevCommand and Command list entry if PrevCommand list entry
- // is alphabetically greater than Command list entry
- //
- if (LexicalMatchValue > 0) {
- Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)SwapListEntries (&PrevCommand->Link, &Command->Link);
- } else if (LexicalMatchValue < 0) {
- //
- // PrevCommand entry is lexically lower than Command entry
- //
- break;
- }
- }
-
- return (RETURN_SUCCESS);
-}
-
-/**
- Function to get the current Profile string.
-
- @retval NULL There are no installed profiles.
- @return A semi-colon delimited list of profiles.
-**/
-CONST CHAR16 *
-EFIAPI
-ShellCommandGetProfileList (
- VOID
- )
-{
- return (mProfileList);
-}
-
-/**
- Checks if a command string has been registered for CommandString and if so it runs
- the previously registered handler for that command with the command line.
-
- If CommandString is NULL, then ASSERT().
-
- If Sections is specified, then each section name listed will be compared in a casesensitive
- manner, to the section names described in Appendix B UEFI Shell 2.0 spec. 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[in] CommandString Pointer to the command name. This is the name
- found on the command line in the shell.
- @param[in, out] RetVal Pointer to the return vaule from the command handler.
-
- @param[in, out] CanAffectLE indicates whether this command's return value
- needs to be placed into LASTERROR environment variable.
-
- @retval RETURN_SUCCESS The handler was run.
- @retval RETURN_NOT_FOUND The CommandString did not match a registered
- command name.
- @sa SHELL_RUN_COMMAND
-**/
-RETURN_STATUS
-EFIAPI
-ShellCommandRunCommandHandler (
- IN CONST CHAR16 *CommandString,
- IN OUT SHELL_STATUS *RetVal,
- IN OUT BOOLEAN *CanAffectLE OPTIONAL
- )
-{
- SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
- EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
-
- //
- // assert for NULL parameters
- //
- ASSERT (CommandString != NULL);
-
- //
- // check for the command
- //
- for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
- ; !IsNull (&mCommandList.Link, &Node->Link)
- ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Node->Link)
- )
- {
- ASSERT (Node->CommandString != NULL);
- if (gUnicodeCollation->StriColl (
- gUnicodeCollation,
- (CHAR16 *)CommandString,
- Node->CommandString
- ) == 0
- )
- {
- if (CanAffectLE != NULL) {
- *CanAffectLE = Node->LastError;
- }
-
- if (RetVal != NULL) {
- *RetVal = Node->CommandHandler (NULL, gST);
- } else {
- Node->CommandHandler (NULL, gST);
- }
-
- return (RETURN_SUCCESS);
- }
- }
-
- //
- // An internal command was not found, try to find a dynamic command
- //
- DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *)ShellCommandFindDynamicCommand (CommandString);
- if (DynamicCommand != NULL) {
- if (RetVal != NULL) {
- *RetVal = DynamicCommand->Handler (DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
- } else {
- DynamicCommand->Handler (DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
- }
-
- return (RETURN_SUCCESS);
- }
-
- return (RETURN_NOT_FOUND);
-}
-
-/**
- Checks if a command string has been registered for CommandString and if so it
- returns the MAN filename specified for that command.
-
- If CommandString is NULL, then ASSERT().
-
- @param[in] CommandString Pointer to the command name. This is the name
- found on the command line in the shell.\
-
- @retval NULL the commandString was not a registered command.
- @return other the name of the MAN file.
- @sa SHELL_GET_MAN_FILENAME
-**/
-CONST CHAR16 *
-EFIAPI
-ShellCommandGetManFileNameHandler (
- IN CONST CHAR16 *CommandString
- )
-{
- SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
-
- //
- // assert for NULL parameters
- //
- ASSERT (CommandString != NULL);
-
- //
- // check for the command
- //
- for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
- ; !IsNull (&mCommandList.Link, &Node->Link)
- ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Node->Link)
- )
- {
- ASSERT (Node->CommandString != NULL);
- if (gUnicodeCollation->StriColl (
- gUnicodeCollation,
- (CHAR16 *)CommandString,
- Node->CommandString
- ) == 0
- )
- {
- return (Node->GetManFileName ());
- }
- }
-
- return (NULL);
-}
-
-/**
- Get the list of all available shell internal commands. This is a linked list
- (via LIST_ENTRY structure). enumerate through it using the BaseLib linked
- list functions. do not modify the values.
-
- @param[in] Sort TRUE to alphabetically sort the values first. FALSE otherwise.
-
- @return a Linked list of all available shell commands.
-**/
-CONST COMMAND_LIST *
-EFIAPI
-ShellCommandGetCommandList (
- IN CONST BOOLEAN Sort
- )
-{
- // if (!Sort) {
- // return ((COMMAND_LIST*)(&mCommandList));
- // }
- return ((COMMAND_LIST *)(&mCommandList));
-}
-
-/**
- Registers aliases to be set as part of the initialization of the shell application.
-
- If Command is NULL, then ASSERT().
- If Alias is NULL, then ASSERT().
-
- @param[in] Command Pointer to the Command
- @param[in] Alias Pointer to Alias
-
- @retval RETURN_SUCCESS The handlers were registered.
- @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
- register the shell command.
-**/
-RETURN_STATUS
-EFIAPI
-ShellCommandRegisterAlias (
- IN CONST CHAR16 *Command,
- IN CONST CHAR16 *Alias
- )
-{
- ALIAS_LIST *Node;
- ALIAS_LIST *CommandAlias;
- ALIAS_LIST *PrevCommandAlias;
- INTN LexicalMatchValue;
-
- //
- // Asserts for NULL
- //
- ASSERT (Command != NULL);
- ASSERT (Alias != NULL);
-
- //
- // allocate memory for new struct
- //
- Node = AllocateZeroPool (sizeof (ALIAS_LIST));
- if (Node == NULL) {
- return RETURN_OUT_OF_RESOURCES;
- }
-
- Node->CommandString = AllocateCopyPool (StrSize (Command), Command);
- if (Node->CommandString == NULL) {
- FreePool (Node);
- return RETURN_OUT_OF_RESOURCES;
- }
-
- Node->Alias = AllocateCopyPool (StrSize (Alias), Alias);
- if (Node->Alias == NULL) {
- FreePool (Node->CommandString);
- FreePool (Node);
- return RETURN_OUT_OF_RESOURCES;
- }
-
- InsertHeadList (&mAliasList.Link, &Node->Link);
-
- //
- // Move a new pre-defined registered alias to its sorted ordered location in the list
- //
- for ( CommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link),
- PrevCommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link)
- ; !IsNull (&mAliasList.Link, &CommandAlias->Link)
- ; CommandAlias = (ALIAS_LIST *)GetNextNode (&mAliasList.Link, &CommandAlias->Link))
- {
- //
- // Get Lexical comparison value between PrevCommandAlias and CommandAlias List Entry
- //
- LexicalMatchValue = gUnicodeCollation->StriColl (
- gUnicodeCollation,
- PrevCommandAlias->Alias,
- CommandAlias->Alias
- );
-
- //
- // Swap PrevCommandAlias and CommandAlias list entry if PrevCommandAlias list entry
- // is alphabetically greater than CommandAlias list entry
- //
- if (LexicalMatchValue > 0) {
- CommandAlias = (ALIAS_LIST *)SwapListEntries (&PrevCommandAlias->Link, &CommandAlias->Link);
- } else if (LexicalMatchValue < 0) {
- //
- // PrevCommandAlias entry is lexically lower than CommandAlias entry
- //
- break;
- }
- }
-
- return (RETURN_SUCCESS);
-}
-
-/**
- Get the list of all shell alias commands. This is a linked list
- (via LIST_ENTRY structure). enumerate through it using the BaseLib linked
- list functions. do not modify the values.
-
- @return a Linked list of all requested shell alias'.
-**/
-CONST ALIAS_LIST *
-EFIAPI
-ShellCommandGetInitAliasList (
- VOID
- )
-{
- return (&mAliasList);
-}
-
-/**
- Determine if a given alias is on the list of built in alias'.
-
- @param[in] Alias The alias to test for
-
- @retval TRUE The alias is a built in alias
- @retval FALSE The alias is not a built in alias
-**/
-BOOLEAN
-EFIAPI
-ShellCommandIsOnAliasList (
- IN CONST CHAR16 *Alias
- )
-{
- ALIAS_LIST *Node;
-
- //
- // assert for NULL parameter
- //
- ASSERT (Alias != NULL);
-
- //
- // check for the Alias
- //
- for ( Node = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link)
- ; !IsNull (&mAliasList.Link, &Node->Link)
- ; Node = (ALIAS_LIST *)GetNextNode (&mAliasList.Link, &Node->Link)
- )
- {
- ASSERT (Node->CommandString != NULL);
- ASSERT (Node->Alias != NULL);
- if (gUnicodeCollation->StriColl (
- gUnicodeCollation,
- (CHAR16 *)Alias,
- Node->CommandString
- ) == 0
- )
- {
- return (TRUE);
- }
-
- if (gUnicodeCollation->StriColl (
- gUnicodeCollation,
- (CHAR16 *)Alias,
- Node->Alias
- ) == 0
- )
- {
- return (TRUE);
- }
- }
-
- return (FALSE);
-}
-
-/**
- Function to determine current state of ECHO. Echo determines if lines from scripts
- and ECHO commands are enabled.
-
- @retval TRUE Echo is currently enabled
- @retval FALSE Echo is currently disabled
-**/
-BOOLEAN
-EFIAPI
-ShellCommandGetEchoState (
- VOID
- )
-{
- return (mEchoState);
-}
-
-/**
- Function to set current state of ECHO. Echo determines if lines from scripts
- and ECHO commands are enabled.
-
- If State is TRUE, Echo will be enabled.
- If State is FALSE, Echo will be disabled.
-
- @param[in] State How to set echo.
-**/
-VOID
-EFIAPI
-ShellCommandSetEchoState (
- IN BOOLEAN State
- )
-{
- mEchoState = State;
-}
-
-/**
- Indicate that the current shell or script should exit.
-
- @param[in] ScriptOnly TRUE if exiting a script; FALSE otherwise.
- @param[in] ErrorCode The 64 bit error code to return.
-**/
-VOID
-EFIAPI
-ShellCommandRegisterExit (
- IN BOOLEAN ScriptOnly,
- IN CONST UINT64 ErrorCode
- )
-{
- mExitRequested = (BOOLEAN)(!mExitRequested);
- if (mExitRequested) {
- mExitScript = ScriptOnly;
- } else {
- mExitScript = FALSE;
- }
-
- mExitCode = ErrorCode;
-}
-
-/**
- Retrieve the Exit indicator.
-
- @retval TRUE Exit was indicated.
- @retval FALSE Exis was not indicated.
-**/
-BOOLEAN
-EFIAPI
-ShellCommandGetExit (
- VOID
- )
-{
- return (mExitRequested);
-}
-
-/**
- Retrieve the Exit code.
-
- If ShellCommandGetExit returns FALSE than the return from this is undefined.
-
- @return the value passed into RegisterExit.
-**/
-UINT64
-EFIAPI
-ShellCommandGetExitCode (
- VOID
- )
-{
- return (mExitCode);
-}
-
-/**
- Retrieve the Exit script indicator.
-
- If ShellCommandGetExit returns FALSE than the return from this is undefined.
-
- @retval TRUE ScriptOnly was indicated.
- @retval FALSE ScriptOnly was not indicated.
-**/
-BOOLEAN
-EFIAPI
-ShellCommandGetScriptExit (
- VOID
- )
-{
- return (mExitScript);
-}
-
-/**
- Function to cleanup all memory from a SCRIPT_FILE structure.
-
- @param[in] Script The pointer to the structure to cleanup.
-**/
-VOID
-EFIAPI
-DeleteScriptFileStruct (
- IN SCRIPT_FILE *Script
- )
-{
- UINT8 LoopVar;
-
- if (Script == NULL) {
- return;
- }
-
- for (LoopVar = 0; LoopVar < Script->Argc; LoopVar++) {
- SHELL_FREE_NON_NULL (Script->Argv[LoopVar]);
- }
-
- if (Script->Argv != NULL) {
- SHELL_FREE_NON_NULL (Script->Argv);
- }
-
- Script->CurrentCommand = NULL;
- while (!IsListEmpty (&Script->CommandList)) {
- Script->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode (&Script->CommandList);
- if (Script->CurrentCommand != NULL) {
- RemoveEntryList (&Script->CurrentCommand->Link);
- if (Script->CurrentCommand->Cl != NULL) {
- SHELL_FREE_NON_NULL (Script->CurrentCommand->Cl);
- }
-
- if (Script->CurrentCommand->Data != NULL) {
- SHELL_FREE_NON_NULL (Script->CurrentCommand->Data);
- }
-
- SHELL_FREE_NON_NULL (Script->CurrentCommand);
- }
- }
-
- SHELL_FREE_NON_NULL (Script->ScriptName);
- SHELL_FREE_NON_NULL (Script);
-}
-
-/**
- Function to return a pointer to the currently running script file object.
-
- @retval NULL A script file is not currently running.
- @return A pointer to the current script file object.
-**/
-SCRIPT_FILE *
-EFIAPI
-ShellCommandGetCurrentScriptFile (
- VOID
- )
-{
- SCRIPT_FILE_LIST *List;
-
- if (IsListEmpty (&mScriptList.Link)) {
- return (NULL);
- }
-
- List = ((SCRIPT_FILE_LIST *)GetFirstNode (&mScriptList.Link));
- return (List->Data);
-}
-
-/**
- Function to set a new script as the currently running one.
-
- This function will correctly stack and unstack nested scripts.
-
- @param[in] Script Pointer to new script information structure. if NULL
- will remove and de-allocate the top-most Script structure.
-
- @return A pointer to the current running script file after this
- change. NULL if removing the final script.
-**/
-SCRIPT_FILE *
-EFIAPI
-ShellCommandSetNewScript (
- IN SCRIPT_FILE *Script OPTIONAL
- )
-{
- SCRIPT_FILE_LIST *Node;
-
- if (Script == NULL) {
- if (IsListEmpty (&mScriptList.Link)) {
- return (NULL);
- }
-
- Node = (SCRIPT_FILE_LIST *)GetFirstNode (&mScriptList.Link);
- RemoveEntryList (&Node->Link);
- DeleteScriptFileStruct (Node->Data);
- FreePool (Node);
- } else {
- Node = AllocateZeroPool (sizeof (SCRIPT_FILE_LIST));
- if (Node == NULL) {
- return (NULL);
- }
-
- Node->Data = Script;
- InsertHeadList (&mScriptList.Link, &Node->Link);
- }
-
- return (ShellCommandGetCurrentScriptFile ());
-}
-
-/**
- Function to generate the next default mapping name.
-
- If the return value is not NULL then it must be callee freed.
-
- @param Type What kind of mapping name to make.
-
- @retval NULL a memory allocation failed.
- @return a new map name string
-**/
-CHAR16 *
-EFIAPI
-ShellCommandCreateNewMappingName (
- IN CONST SHELL_MAPPING_TYPE Type
- )
-{
- CHAR16 *String;
-
- ASSERT (Type < MappingTypeMax);
-
- String = NULL;
-
- String = AllocateZeroPool (PcdGet8 (PcdShellMapNameLength) * sizeof (String[0]));
- UnicodeSPrint (
- String,
- PcdGet8 (PcdShellMapNameLength) * sizeof (String[0]),
- Type == MappingTypeFileSystem ? L"FS%d:" : L"BLK%d:",
- Type == MappingTypeFileSystem ? mFsMaxCount++ : mBlkMaxCount++
- );
-
- return (String);
-}
-
-/**
- Function to add a map node to the list of map items and update the "path" environment variable (optionally).
-
- If Path is TRUE (during initialization only), the path environment variable will also be updated to include
- default paths on the new map name...
-
- Path should be FALSE when this function is called from the protocol SetMap function.
-
- @param[in] Name The human readable mapped name.
- @param[in] DevicePath The Device Path for this map.
- @param[in] Flags The Flags attribute for this map item.
- @param[in] Path TRUE to update path, FALSE to skip this step (should only be TRUE during initialization).
-
- @retval EFI_SUCCESS The addition was successful.
- @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
- @retval EFI_INVALID_PARAMETER A parameter was invalid.
-**/
-EFI_STATUS
-EFIAPI
-ShellCommandAddMapItemAndUpdatePath (
- IN CONST CHAR16 *Name,
- IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
- IN CONST UINT64 Flags,
- IN CONST BOOLEAN Path
- )
-{
- EFI_STATUS Status;
- SHELL_MAP_LIST *MapListNode;
- CONST CHAR16 *OriginalPath;
- CHAR16 *NewPath;
- UINTN NewPathSize;
-
- NewPathSize = 0;
- NewPath = NULL;
- OriginalPath = NULL;
- Status = EFI_SUCCESS;
-
- MapListNode = AllocateZeroPool (sizeof (SHELL_MAP_LIST));
- if (MapListNode == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- } else {
- MapListNode->Flags = Flags;
- MapListNode->MapName = AllocateCopyPool (StrSize (Name), Name);
- MapListNode->DevicePath = DuplicateDevicePath (DevicePath);
- if ((MapListNode->MapName == NULL) || (MapListNode->DevicePath == NULL)) {
- Status = EFI_OUT_OF_RESOURCES;
- } else {
- InsertTailList (&gShellMapList.Link, &MapListNode->Link);
- }
- }
-
- if (EFI_ERROR (Status)) {
- if (MapListNode != NULL) {
- if (MapListNode->DevicePath != NULL) {
- FreePool (MapListNode->DevicePath);
- }
-
- if (MapListNode->MapName != NULL) {
- FreePool (MapListNode->MapName);
- }
-
- FreePool (MapListNode);
- }
- } else if (Path) {
- //
- // Since there was no error and Path was TRUE
- // Now add the correct path for that mapping
- //
- OriginalPath = gEfiShellProtocol->GetEnv (L"path");
- ASSERT ((NewPath == NULL && NewPathSize == 0) || (NewPath != NULL));
- if (OriginalPath != NULL) {
- StrnCatGrow (&NewPath, &NewPathSize, OriginalPath, 0);
- StrnCatGrow (&NewPath, &NewPathSize, L";", 0);
- }
-
- StrnCatGrow (&NewPath, &NewPathSize, Name, 0);
- StrnCatGrow (&NewPath, &NewPathSize, L"\\efi\\tools\\;", 0);
- StrnCatGrow (&NewPath, &NewPathSize, Name, 0);
- StrnCatGrow (&NewPath, &NewPathSize, L"\\efi\\boot\\;", 0);
- StrnCatGrow (&NewPath, &NewPathSize, Name, 0);
- StrnCatGrow (&NewPath, &NewPathSize, L"\\", 0);
-
- Status = gEfiShellProtocol->SetEnv (L"path", NewPath, TRUE);
- ASSERT_EFI_ERROR (Status);
- FreePool (NewPath);
- }
-
- return (Status);
-}
-
-/**
- Creates the default map names for each device path in the system with
- a protocol depending on the Type.
-
- Creates the consistent map names for each device path in the system with
- a protocol depending on the Type.
-
- Note: This will reset all mappings in the system("map -r").
-
- Also sets up the default path environment variable if Type is FileSystem.
-
- @retval EFI_SUCCESS All map names were created successfully.
- @retval EFI_NOT_FOUND No protocols were found in the system.
- @return Error returned from gBS->LocateHandle().
-
- @sa LocateHandle
-**/
-EFI_STATUS
-EFIAPI
-ShellCommandCreateInitialMappingsAndPaths (
- VOID
- )
-{
- EFI_STATUS Status;
- EFI_HANDLE *HandleList;
- UINTN Count;
- EFI_DEVICE_PATH_PROTOCOL **DevicePathList;
- CHAR16 *NewDefaultName;
- CHAR16 *NewConsistName;
- EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable;
- SHELL_MAP_LIST *MapListNode;
- CONST CHAR16 *CurDir;
- CHAR16 *SplitCurDir;
- CHAR16 *MapName;
- SHELL_MAP_LIST *MapListItem;
-
- ConsistMappingTable = NULL;
- SplitCurDir = NULL;
- MapName = NULL;
- MapListItem = NULL;
- HandleList = NULL;
-
- //
- // Reset the static members back to zero
- //
- mFsMaxCount = 0;
- mBlkMaxCount = 0;
-
- gEfiShellProtocol->SetEnv (L"path", L"", TRUE);
-
- //
- // First empty out the existing list.
- //
- if (!IsListEmpty (&gShellMapList.Link)) {
- for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link)
- ; !IsListEmpty (&gShellMapList.Link)
- ; MapListNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link)
- )
- {
- RemoveEntryList (&MapListNode->Link);
- SHELL_FREE_NON_NULL (MapListNode->DevicePath);
- SHELL_FREE_NON_NULL (MapListNode->MapName);
- SHELL_FREE_NON_NULL (MapListNode->CurrentDirectoryPath);
- FreePool (MapListNode);
- } // for loop
- }
-
- //
- // Find each handle with Simple File System
- //
- HandleList = GetHandleListByProtocol (&gEfiSimpleFileSystemProtocolGuid);
- if (HandleList != NULL) {
- //
- // Do a count of the handles
- //
- for (Count = 0; HandleList[Count] != NULL; Count++) {
- }
-
- //
- // Get all Device Paths
- //
- DevicePathList = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL *) * Count);
- if (DevicePathList == NULL) {
- SHELL_FREE_NON_NULL (HandleList);
- return EFI_OUT_OF_RESOURCES;
- }
-
- for (Count = 0; HandleList[Count] != NULL; Count++) {
- DevicePathList[Count] = DevicePathFromHandle (HandleList[Count]);
- }
-
- //
- // Sort all DevicePaths
- //
- PerformQuickSort (DevicePathList, Count, sizeof (EFI_DEVICE_PATH_PROTOCOL *), DevicePathCompare);
-
- if (!EFI_ERROR (ShellCommandConsistMappingInitialize (&ConsistMappingTable))) {
- //
- // Assign new Mappings to all...
- //
- for (Count = 0; HandleList[Count] != NULL; Count++) {
- //
- // Get default name first
- //
- NewDefaultName = ShellCommandCreateNewMappingName (MappingTypeFileSystem);
- ASSERT (NewDefaultName != NULL);
- Status = ShellCommandAddMapItemAndUpdatePath (NewDefaultName, DevicePathList[Count], 0, TRUE);
- ASSERT_EFI_ERROR (Status);
- FreePool (NewDefaultName);
-
- //
- // Now do consistent name
- //
- NewConsistName = ShellCommandConsistMappingGenMappingName (DevicePathList[Count], ConsistMappingTable);
- if (NewConsistName != NULL) {
- Status = ShellCommandAddMapItemAndUpdatePath (NewConsistName, DevicePathList[Count], 0, FALSE);
- ASSERT_EFI_ERROR (Status);
- FreePool (NewConsistName);
- }
- }
- }
-
- if (ConsistMappingTable != NULL) {
- ShellCommandConsistMappingUnInitialize (ConsistMappingTable);
- }
-
- SHELL_FREE_NON_NULL (HandleList);
- SHELL_FREE_NON_NULL (DevicePathList);
-
- HandleList = NULL;
-
- //
- // gShellCurMapping point to node of current file system in the gShellMapList. When reset all mappings,
- // all nodes in the gShellMapList will be free. Then gShellCurMapping will be a dangling pointer, So,
- // after created new mappings, we should reset the gShellCurMapping pointer back to node of current file system.
- //
- if (gShellCurMapping != NULL) {
- gShellCurMapping = NULL;
- CurDir = gEfiShellProtocol->GetEnv (L"cwd");
- if (CurDir != NULL) {
- MapName = AllocateCopyPool (StrSize (CurDir), CurDir);
- if (MapName == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- SplitCurDir = StrStr (MapName, L":");
- if (SplitCurDir == NULL) {
- SHELL_FREE_NON_NULL (MapName);
- return EFI_UNSUPPORTED;
- }
-
- *(SplitCurDir + 1) = CHAR_NULL;
- MapListItem = ShellCommandFindMapItem (MapName);
- if (MapListItem != NULL) {
- gShellCurMapping = MapListItem;
- }
-
- SHELL_FREE_NON_NULL (MapName);
- }
- }
- } else {
- Count = (UINTN)-1;
- }
-
- //
- // Find each handle with Block Io
- //
- HandleList = GetHandleListByProtocol (&gEfiBlockIoProtocolGuid);
- if (HandleList != NULL) {
- for (Count = 0; HandleList[Count] != NULL; Count++) {
- }
-
- //
- // Get all Device Paths
- //
- DevicePathList = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL *) * Count);
- if (DevicePathList == NULL) {
- SHELL_FREE_NON_NULL (HandleList);
- return EFI_OUT_OF_RESOURCES;
- }
-
- for (Count = 0; HandleList[Count] != NULL; Count++) {
- DevicePathList[Count] = DevicePathFromHandle (HandleList[Count]);
- }
-
- //
- // Sort all DevicePaths
- //
- PerformQuickSort (DevicePathList, Count, sizeof (EFI_DEVICE_PATH_PROTOCOL *), DevicePathCompare);
-
- //
- // Assign new Mappings to all...
- //
- for (Count = 0; HandleList[Count] != NULL; Count++) {
- //
- // Get default name first
- //
- NewDefaultName = ShellCommandCreateNewMappingName (MappingTypeBlockIo);
- ASSERT (NewDefaultName != NULL);
- Status = ShellCommandAddMapItemAndUpdatePath (NewDefaultName, DevicePathList[Count], 0, FALSE);
- ASSERT_EFI_ERROR (Status);
- FreePool (NewDefaultName);
- }
-
- SHELL_FREE_NON_NULL (HandleList);
- SHELL_FREE_NON_NULL (DevicePathList);
- } else if (Count == (UINTN)-1) {
- return (EFI_NOT_FOUND);
- }
-
- return (EFI_SUCCESS);
-}
-
-/**
- Add mappings for any devices without one. Do not change any existing maps.
-
- @retval EFI_SUCCESS The operation was successful.
-**/
-EFI_STATUS
-EFIAPI
-ShellCommandUpdateMapping (
- VOID
- )
-{
- EFI_STATUS Status;
- EFI_HANDLE *HandleList;
- UINTN Count;
- EFI_DEVICE_PATH_PROTOCOL **DevicePathList;
- CHAR16 *NewDefaultName;
- CHAR16 *NewConsistName;
- EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable;
-
- HandleList = NULL;
- Status = EFI_SUCCESS;
-
- //
- // remove mappings that represent removed devices.
- //
-
- //
- // Find each handle with Simple File System
- //
- HandleList = GetHandleListByProtocol (&gEfiSimpleFileSystemProtocolGuid);
- if (HandleList != NULL) {
- //
- // Do a count of the handles
- //
- for (Count = 0; HandleList[Count] != NULL; Count++) {
- }
-
- //
- // Get all Device Paths
- //
- DevicePathList = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL *) * Count);
- if (DevicePathList == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
-
- for (Count = 0; HandleList[Count] != NULL; Count++) {
- DevicePathList[Count] = DevicePathFromHandle (HandleList[Count]);
- }
-
- //
- // Sort all DevicePaths
- //
- PerformQuickSort (DevicePathList, Count, sizeof (EFI_DEVICE_PATH_PROTOCOL *), DevicePathCompare);
-
- Status = ShellCommandConsistMappingInitialize (&ConsistMappingTable);
-
- //
- // Assign new Mappings to remainders
- //
- for (Count = 0; !EFI_ERROR (Status) && HandleList[Count] != NULL; Count++) {
- //
- // Skip ones that already have
- //
- if (gEfiShellProtocol->GetMapFromDevicePath (&DevicePathList[Count]) != NULL) {
- continue;
- }
-
- //
- // Get default name
- //
- NewDefaultName = ShellCommandCreateNewMappingName (MappingTypeFileSystem);
- if (NewDefaultName == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- break;
- }
-
- //
- // Call shell protocol SetMap function now...
- //
- Status = gEfiShellProtocol->SetMap (DevicePathList[Count], NewDefaultName);
-
- if (!EFI_ERROR (Status)) {
- //
- // Now do consistent name
- //
- NewConsistName = ShellCommandConsistMappingGenMappingName (DevicePathList[Count], ConsistMappingTable);
- if (NewConsistName != NULL) {
- Status = gEfiShellProtocol->SetMap (DevicePathList[Count], NewConsistName);
- FreePool (NewConsistName);
- }
- }
-
- FreePool (NewDefaultName);
- }
-
- ShellCommandConsistMappingUnInitialize (ConsistMappingTable);
- SHELL_FREE_NON_NULL (HandleList);
- SHELL_FREE_NON_NULL (DevicePathList);
-
- HandleList = NULL;
- } else {
- Count = (UINTN)-1;
- }
-
- //
- // Do it all over again for gEfiBlockIoProtocolGuid
- //
-
- return (Status);
-}
-
-/**
- Converts a SHELL_FILE_HANDLE to an EFI_FILE_PROTOCOL*.
-
- @param[in] Handle The SHELL_FILE_HANDLE to convert.
-
- @return a EFI_FILE_PROTOCOL* representing the same file.
-**/
-EFI_FILE_PROTOCOL *
-EFIAPI
-ConvertShellHandleToEfiFileProtocol (
- IN CONST SHELL_FILE_HANDLE Handle
- )
-{
- return ((EFI_FILE_PROTOCOL *)(Handle));
-}
-
-/**
- Converts a EFI_FILE_PROTOCOL* to an SHELL_FILE_HANDLE.
-
- @param[in] Handle The pointer to EFI_FILE_PROTOCOL to convert.
- @param[in] Path The path to the file for verification.
-
- @return A SHELL_FILE_HANDLE representing the same file.
- @retval NULL There was not enough memory.
-**/
-SHELL_FILE_HANDLE
-EFIAPI
-ConvertEfiFileProtocolToShellHandle (
- IN CONST EFI_FILE_PROTOCOL *Handle,
- IN CONST CHAR16 *Path
- )
-{
- SHELL_COMMAND_FILE_HANDLE *Buffer;
- BUFFER_LIST *NewNode;
-
- if (Path != NULL) {
- Buffer = AllocateZeroPool (sizeof (SHELL_COMMAND_FILE_HANDLE));
- if (Buffer == NULL) {
- return (NULL);
- }
-
- NewNode = AllocateZeroPool (sizeof (BUFFER_LIST));
- if (NewNode == NULL) {
- SHELL_FREE_NON_NULL (Buffer);
- return (NULL);
- }
-
- Buffer->FileHandle = (EFI_FILE_PROTOCOL *)Handle;
- Buffer->Path = StrnCatGrow (&Buffer->Path, NULL, Path, 0);
- if (Buffer->Path == NULL) {
- SHELL_FREE_NON_NULL (NewNode);
- SHELL_FREE_NON_NULL (Buffer);
- return (NULL);
- }
-
- NewNode->Buffer = Buffer;
-
- InsertHeadList (&mFileHandleList.Link, &NewNode->Link);
- }
-
- return ((SHELL_FILE_HANDLE)(Handle));
-}
-
-/**
- Find the path that was logged with the specified SHELL_FILE_HANDLE.
-
- @param[in] Handle The SHELL_FILE_HANDLE to query on.
-
- @return A pointer to the path for the file.
-**/
-CONST CHAR16 *
-EFIAPI
-ShellFileHandleGetPath (
- IN CONST SHELL_FILE_HANDLE Handle
- )
-{
- BUFFER_LIST *Node;
-
- for (Node = (BUFFER_LIST *)GetFirstNode (&mFileHandleList.Link)
- ; !IsNull (&mFileHandleList.Link, &Node->Link)
- ; Node = (BUFFER_LIST *)GetNextNode (&mFileHandleList.Link, &Node->Link)
- )
- {
- if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)) {
- return (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
- }
- }
-
- return (NULL);
-}
-
-/**
- Remove a SHELL_FILE_HANDLE from the list of SHELL_FILE_HANDLES.
-
- @param[in] Handle The SHELL_FILE_HANDLE to remove.
-
- @retval TRUE The item was removed.
- @retval FALSE The item was not found.
-**/
-BOOLEAN
-EFIAPI
-ShellFileHandleRemove (
- IN CONST SHELL_FILE_HANDLE Handle
- )
-{
- BUFFER_LIST *Node;
-
- for (Node = (BUFFER_LIST *)GetFirstNode (&mFileHandleList.Link)
- ; !IsNull (&mFileHandleList.Link, &Node->Link)
- ; Node = (BUFFER_LIST *)GetNextNode (&mFileHandleList.Link, &Node->Link)
- )
- {
- if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)) {
- RemoveEntryList (&Node->Link);
- SHELL_FREE_NON_NULL (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
- SHELL_FREE_NON_NULL (Node->Buffer);
- SHELL_FREE_NON_NULL (Node);
- return (TRUE);
- }
- }
-
- return (FALSE);
-}
-
-/**
- Function to determine if a SHELL_FILE_HANDLE is at the end of the file.
-
- This will NOT work on directories.
-
- If Handle is NULL, then ASSERT.
-
- @param[in] Handle the file handle
-
- @retval TRUE the position is at the end of the file
- @retval FALSE the position is not at the end of the file
-**/
-BOOLEAN
-EFIAPI
-ShellFileHandleEof (
- IN SHELL_FILE_HANDLE Handle
- )
-{
- EFI_FILE_INFO *Info;
- UINT64 Pos;
- BOOLEAN RetVal;
-
- //
- // ASSERT if Handle is NULL
- //
- ASSERT (Handle != NULL);
-
- gEfiShellProtocol->GetFilePosition (Handle, &Pos);
- Info = gEfiShellProtocol->GetFileInfo (Handle);
- gEfiShellProtocol->SetFilePosition (Handle, Pos);
-
- if (Info == NULL) {
- return (FALSE);
- }
-
- if (Pos == Info->FileSize) {
- RetVal = TRUE;
- } else {
- RetVal = FALSE;
- }
-
- FreePool (Info);
-
- return (RetVal);
-}
-
-/**
- Frees any BUFFER_LIST defined type.
-
- @param[in] List The BUFFER_LIST object to free.
-**/
-VOID
-EFIAPI
-FreeBufferList (
- IN BUFFER_LIST *List
- )
-{
- BUFFER_LIST *BufferListEntry;
-
- if (List == NULL) {
- return;
- }
-
- //
- // enumerate through the buffer list and free all memory
- //
- for ( BufferListEntry = (BUFFER_LIST *)GetFirstNode (&List->Link)
- ; !IsListEmpty (&List->Link)
- ; BufferListEntry = (BUFFER_LIST *)GetFirstNode (&List->Link)
- )
- {
- RemoveEntryList (&BufferListEntry->Link);
- if (BufferListEntry->Buffer != NULL) {
- FreePool (BufferListEntry->Buffer);
- }
-
- FreePool (BufferListEntry);
- }
-}
-
-/**
- Dump some hexadecimal data to the screen.
-
- @param[in] Indent How many spaces to indent the output.
- @param[in] Offset The offset of the printing.
- @param[in] DataSize The size in bytes of UserData.
- @param[in] UserData The data to print out.
-**/
-VOID
-EFIAPI
-DumpHex (
- IN UINTN Indent,
- IN UINTN Offset,
- IN UINTN DataSize,
- IN VOID *UserData
- )
-{
- UINT8 *Data;
-
- CHAR8 Val[50];
-
- CHAR8 Str[20];
-
- UINT8 TempByte;
- UINTN Size;
- UINTN Index;
-
- Data = UserData;
- while (DataSize != 0) {
- Size = 16;
- if (Size > DataSize) {
- Size = DataSize;
- }
-
- for (Index = 0; Index < Size; Index += 1) {
- TempByte = Data[Index];
- Val[Index * 3 + 0] = Hex[TempByte >> 4];
- Val[Index * 3 + 1] = Hex[TempByte & 0xF];
- Val[Index * 3 + 2] = (CHAR8)((Index == 7) ? '-' : ' ');
- Str[Index] = (CHAR8)((TempByte < ' ' || TempByte > '~') ? '.' : TempByte);
- }
-
- Val[Index * 3] = 0;
- Str[Index] = 0;
- ShellPrintEx (-1, -1, L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str);
-
- Data += Size;
- Offset += Size;
- DataSize -= Size;
- }
-}
-
-/**
- Dump HEX data into buffer.
-
- @param[in] Buffer HEX data to be dumped in Buffer.
- @param[in] Indent How many spaces to indent the output.
- @param[in] Offset The offset of the printing.
- @param[in] DataSize The size in bytes of UserData.
- @param[in] UserData The data to print out.
-**/
-CHAR16 *
-EFIAPI
-CatSDumpHex (
- IN CHAR16 *Buffer,
- IN UINTN Indent,
- IN UINTN Offset,
- IN UINTN DataSize,
- IN VOID *UserData
- )
-{
- UINT8 *Data;
- UINT8 TempByte;
- UINTN Size;
- UINTN Index;
- CHAR8 Val[50];
- CHAR8 Str[20];
- CHAR16 *RetVal;
- CHAR16 *TempRetVal;
-
- Data = UserData;
- RetVal = Buffer;
- while (DataSize != 0) {
- Size = 16;
- if (Size > DataSize) {
- Size = DataSize;
- }
-
- for (Index = 0; Index < Size; Index += 1) {
- TempByte = Data[Index];
- Val[Index * 3 + 0] = Hex[TempByte >> 4];
- Val[Index * 3 + 1] = Hex[TempByte & 0xF];
- Val[Index * 3 + 2] = (CHAR8)((Index == 7) ? '-' : ' ');
- Str[Index] = (CHAR8)((TempByte < ' ' || TempByte > 'z') ? '.' : TempByte);
- }
-
- Val[Index * 3] = 0;
- Str[Index] = 0;
- TempRetVal = CatSPrint (RetVal, L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str);
- SHELL_FREE_NON_NULL (RetVal);
- RetVal = TempRetVal;
-
- Data += Size;
- Offset += Size;
- DataSize -= Size;
- }
-
- return RetVal;
-}
-
-/**
- ORDERED_COLLECTION_USER_COMPARE function for SHELL_SORT_UNIQUE_NAME objects.
-
- @param[in] Unique1AsVoid The first SHELL_SORT_UNIQUE_NAME object (Unique1),
- passed in as a pointer-to-VOID.
-
- @param[in] Unique2AsVoid The second SHELL_SORT_UNIQUE_NAME object (Unique2),
- passed in as a pointer-to-VOID.
-
- @retval <0 If Unique1 compares less than Unique2.
-
- @retval 0 If Unique1 compares equal to Unique2.
-
- @retval >0 If Unique1 compares greater than Unique2.
-**/
-STATIC
-INTN
-EFIAPI
-UniqueNameCompare (
- IN CONST VOID *Unique1AsVoid,
- IN CONST VOID *Unique2AsVoid
- )
-{
- CONST SHELL_SORT_UNIQUE_NAME *Unique1;
- CONST SHELL_SORT_UNIQUE_NAME *Unique2;
-
- Unique1 = Unique1AsVoid;
- Unique2 = Unique2AsVoid;
-
- //
- // We need to cast away CONST for EFI_UNICODE_COLLATION_STRICOLL.
- //
- return gUnicodeCollation->StriColl (
- gUnicodeCollation,
- (CHAR16 *)Unique1->Alias,
- (CHAR16 *)Unique2->Alias
- );
-}
-
-/**
- ORDERED_COLLECTION_KEY_COMPARE function for SHELL_SORT_UNIQUE_NAME objects.
-
- @param[in] UniqueAliasAsVoid The CHAR16 string UniqueAlias, passed in as a
- pointer-to-VOID.
-
- @param[in] UniqueAsVoid The SHELL_SORT_UNIQUE_NAME object (Unique),
- passed in as a pointer-to-VOID.
-
- @retval <0 If UniqueAlias compares less than Unique->Alias.
-
- @retval 0 If UniqueAlias compares equal to Unique->Alias.
-
- @retval >0 If UniqueAlias compares greater than Unique->Alias.
-**/
-STATIC
-INTN
-EFIAPI
-UniqueNameAliasCompare (
- IN CONST VOID *UniqueAliasAsVoid,
- IN CONST VOID *UniqueAsVoid
- )
-{
- CONST CHAR16 *UniqueAlias;
- CONST SHELL_SORT_UNIQUE_NAME *Unique;
-
- UniqueAlias = UniqueAliasAsVoid;
- Unique = UniqueAsVoid;
-
- //
- // We need to cast away CONST for EFI_UNICODE_COLLATION_STRICOLL.
- //
- return gUnicodeCollation->StriColl (
- gUnicodeCollation,
- (CHAR16 *)UniqueAlias,
- (CHAR16 *)Unique->Alias
- );
-}
-
-/**
- Sort an EFI_SHELL_FILE_INFO list, optionally moving duplicates to a separate
- list.
-
- @param[in,out] FileList The list of EFI_SHELL_FILE_INFO objects to sort.
-
- If FileList is NULL on input, then FileList is
- considered an empty, hence already sorted, list.
-
- Otherwise, if (*FileList) is NULL on input, then
- EFI_INVALID_PARAMETER is returned.
-
- Otherwise, the caller is responsible for having
- initialized (*FileList)->Link with
- InitializeListHead(). No other fields in the
- (**FileList) head element are accessed by this
- function.
-
- On output, (*FileList) is sorted according to Order.
- If Duplicates is NULL on input, then duplicate
- elements are preserved, sorted stably, on
- (*FileList). If Duplicates is not NULL on input,
- then duplicates are moved (stably sorted) to the
- new, dynamically allocated (*Duplicates) list.
-
- @param[out] Duplicates If Duplicates is NULL on input, (*FileList) will be
- a monotonically ordered list on output, with
- duplicates stably sorted.
-
- If Duplicates is not NULL on input, (*FileList) will
- be a strictly monotonically oredered list on output,
- with duplicates separated (stably sorted) to
- (*Duplicates). All fields except Link will be
- zero-initialized in the (**Duplicates) head element.
- If no duplicates exist, then (*Duplicates) is set to
- NULL on output.
-
- @param[in] Order Determines the comparison operation between
- EFI_SHELL_FILE_INFO objects.
-
- @retval EFI_INVALID_PARAMETER (UINTN)Order is greater than or equal to
- (UINTN)ShellSortFileListMax. Neither the
- (*FileList) nor the (*Duplicates) list has
- been modified.
-
- @retval EFI_INVALID_PARAMETER (*FileList) was NULL on input. Neither the
- (*FileList) nor the (*Duplicates) list has
- been modified.
-
- @retval EFI_OUT_OF_RESOURCES Memory allocation failed. Neither the
- (*FileList) nor the (*Duplicates) list has
- been modified.
-
- @retval EFI_SUCCESS Sorting successful, including the case when
- FileList is NULL on input.
-**/
-EFI_STATUS
-EFIAPI
-ShellSortFileList (
- IN OUT EFI_SHELL_FILE_INFO **FileList,
- OUT EFI_SHELL_FILE_INFO **Duplicates OPTIONAL,
- IN SHELL_SORT_FILE_LIST Order
- )
-{
- LIST_ENTRY *FilesHead;
- ORDERED_COLLECTION *Sort;
- LIST_ENTRY *FileEntry;
- EFI_SHELL_FILE_INFO *FileInfo;
- SHELL_SORT_UNIQUE_NAME *Unique;
- EFI_STATUS Status;
- EFI_SHELL_FILE_INFO *Dupes;
- LIST_ENTRY *NextFileEntry;
- CONST CHAR16 *Alias;
- ORDERED_COLLECTION_ENTRY *SortEntry;
- LIST_ENTRY *TargetFileList;
- ORDERED_COLLECTION_ENTRY *NextSortEntry;
- VOID *UniqueAsVoid;
-
- if ((UINTN)Order >= (UINTN)ShellSortFileListMax) {
- return EFI_INVALID_PARAMETER;
- }
-
- if (FileList == NULL) {
- //
- // FileList is considered empty, hence already sorted, with no duplicates.
- //
- if (Duplicates != NULL) {
- *Duplicates = NULL;
- }
-
- return EFI_SUCCESS;
- }
-
- if (*FileList == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- FilesHead = &(*FileList)->Link;
-
- //
- // Collect all the unique names.
- //
- Sort = OrderedCollectionInit (UniqueNameCompare, UniqueNameAliasCompare);
- if (Sort == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- BASE_LIST_FOR_EACH (FileEntry, FilesHead) {
- FileInfo = (EFI_SHELL_FILE_INFO *)FileEntry;
-
- //
- // Try to record the name of this file as a unique name.
- //
- Unique = AllocatePool (sizeof (*Unique));
- if (Unique == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto UninitSort;
- }
-
- Unique->Alias = ((Order == ShellSortFileListByFileName) ?
- FileInfo->FileName :
- FileInfo->FullName);
- InitializeListHead (&Unique->SameNameList);
-
- Status = OrderedCollectionInsert (Sort, NULL, Unique);
- if (EFI_ERROR (Status)) {
- //
- // Only two errors are possible: memory allocation failed, or this name
- // has been encountered before. In either case, the
- // SHELL_SORT_UNIQUE_NAME object being constructed has to be released.
- //
- FreePool (Unique);
- //
- // Memory allocation failure is fatal, while having seen the same name
- // before is normal.
- //
- if (Status == EFI_OUT_OF_RESOURCES) {
- goto UninitSort;
- }
-
- ASSERT (Status == EFI_ALREADY_STARTED);
- }
- }
-
- //
- // Set Dupes to suppress incorrect compiler/analyzer warnings.
- //
- Dupes = NULL;
-
- //
- // If separation of duplicates has been requested, allocate the list for
- // them.
- //
- if (Duplicates != NULL) {
- Dupes = AllocateZeroPool (sizeof (*Dupes));
- if (Dupes == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto UninitSort;
- }
-
- InitializeListHead (&Dupes->Link);
- }
-
- //
- // No memory allocation beyond this point; thus, no chance to fail. We can
- // now migrate the EFI_SHELL_FILE_INFO objects from (*FileList) to Sort.
- //
- BASE_LIST_FOR_EACH_SAFE (FileEntry, NextFileEntry, FilesHead) {
- FileInfo = (EFI_SHELL_FILE_INFO *)FileEntry;
- //
- // Look up the SHELL_SORT_UNIQUE_NAME that matches FileInfo's name.
- //
- Alias = ((Order == ShellSortFileListByFileName) ?
- FileInfo->FileName :
- FileInfo->FullName);
- SortEntry = OrderedCollectionFind (Sort, Alias);
- ASSERT (SortEntry != NULL);
- Unique = OrderedCollectionUserStruct (SortEntry);
- //
- // Move FileInfo from (*FileList) to the end of the list of files whose
- // names all compare identical to FileInfo's name.
- //
- RemoveEntryList (&FileInfo->Link);
- InsertTailList (&Unique->SameNameList, &FileInfo->Link);
- }
-
- //
- // All EFI_SHELL_FILE_INFO objects originally in (*FileList) have been
- // distributed to Sort. Now migrate them back to (*FileList), advancing in
- // unique name order.
- //
- for (SortEntry = OrderedCollectionMin (Sort);
- SortEntry != NULL;
- SortEntry = OrderedCollectionNext (SortEntry))
- {
- Unique = OrderedCollectionUserStruct (SortEntry);
- //
- // The first FileInfo encountered for each unique name goes back on
- // (*FileList) unconditionally. Further FileInfo instances for the same
- // unique name -- that is, duplicates -- are either returned to (*FileList)
- // or separated, dependent on the caller's request.
- //
- TargetFileList = FilesHead;
- BASE_LIST_FOR_EACH_SAFE (FileEntry, NextFileEntry, &Unique->SameNameList) {
- RemoveEntryList (FileEntry);
- InsertTailList (TargetFileList, FileEntry);
- if (Duplicates != NULL) {
- TargetFileList = &Dupes->Link;
- }
- }
- }
-
- //
- // We're done. If separation of duplicates has been requested, output the
- // list of duplicates -- and free that list at once, if it's empty (i.e., if
- // no duplicates have been found).
- //
- if (Duplicates != NULL) {
- if (IsListEmpty (&Dupes->Link)) {
- FreePool (Dupes);
- *Duplicates = NULL;
- } else {
- *Duplicates = Dupes;
- }
- }
-
- Status = EFI_SUCCESS;
-
- //
- // Fall through.
- //
-UninitSort:
- for (SortEntry = OrderedCollectionMin (Sort);
- SortEntry != NULL;
- SortEntry = NextSortEntry)
- {
- NextSortEntry = OrderedCollectionNext (SortEntry);
- OrderedCollectionDelete (Sort, SortEntry, &UniqueAsVoid);
- Unique = UniqueAsVoid;
- ASSERT (IsListEmpty (&Unique->SameNameList));
- FreePool (Unique);
- }
-
- OrderedCollectionUninit (Sort);
-
- return Status;
-}
+/** @file + Provides interface to shell internal functions for shell commands. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR> + (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "UefiShellCommandLib.h" + +// STATIC local variables +STATIC SHELL_COMMAND_INTERNAL_LIST_ENTRY mCommandList; +STATIC SCRIPT_FILE_LIST mScriptList; +STATIC ALIAS_LIST mAliasList; +STATIC BOOLEAN mEchoState; +STATIC BOOLEAN mExitRequested; +STATIC UINT64 mExitCode; +STATIC BOOLEAN mExitScript; +STATIC CHAR16 *mProfileList; +STATIC UINTN mProfileListSize; +STATIC UINTN mFsMaxCount = 0; +STATIC UINTN mBlkMaxCount = 0; +STATIC BUFFER_LIST mFileHandleList; + +STATIC CONST CHAR8 Hex[] = { + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + 'A', + 'B', + 'C', + 'D', + 'E', + 'F' +}; + +// global variables required by library class. +EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollation = NULL; +SHELL_MAP_LIST gShellMapList; +SHELL_MAP_LIST *gShellCurMapping = NULL; + +CONST CHAR16 *SupportLevel[] = { + L"Minimal", + L"Scripting", + L"Basic", + L"Interactive" +}; + +/** + Function to make sure that the global protocol pointers are valid. + must be called after constructor before accessing the pointers. +**/ +EFI_STATUS +EFIAPI +CommandInit ( + VOID + ) +{ + UINTN NumHandles; + EFI_HANDLE *Handles; + EFI_UNICODE_COLLATION_PROTOCOL *Uc; + CHAR8 *BestLanguage; + UINTN Index; + EFI_STATUS Status; + CHAR8 *PlatformLang; + + if (gUnicodeCollation == NULL) { + GetEfiGlobalVariable2 (EFI_PLATFORM_LANG_VARIABLE_NAME, (VOID **)&PlatformLang, NULL); + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiUnicodeCollation2ProtocolGuid, + NULL, + &NumHandles, + &Handles + ); + if (EFI_ERROR (Status)) { + NumHandles = 0; + Handles = NULL; + } + + for (Index = 0; Index < NumHandles; Index++) { + // + // Open Unicode Collation Protocol + // + Status = gBS->OpenProtocol ( + Handles[Index], + &gEfiUnicodeCollation2ProtocolGuid, + (VOID **)&Uc, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + continue; + } + + // + // Without clue provided use the first Unicode Collation2 protocol. + // This may happen when PlatformLang is NULL or when no installed Unicode + // Collation2 protocol instance supports PlatformLang. + // + if (gUnicodeCollation == NULL) { + gUnicodeCollation = Uc; + } + + if (PlatformLang == NULL) { + break; + } + + // + // Find the best matching matching language from the supported languages + // of Unicode Collation2 protocol. + // + BestLanguage = GetBestLanguage ( + Uc->SupportedLanguages, + FALSE, + PlatformLang, + NULL + ); + if (BestLanguage != NULL) { + FreePool (BestLanguage); + gUnicodeCollation = Uc; + break; + } + } + + if (Handles != NULL) { + FreePool (Handles); + } + + if (PlatformLang != NULL) { + FreePool (PlatformLang); + } + } + + return (gUnicodeCollation == NULL) ? EFI_UNSUPPORTED : EFI_SUCCESS; +} + +/** + Constructor for the Shell Command library. + + Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell. + + @param ImageHandle the image handle of the process + @param SystemTable the EFI System Table pointer + + @retval EFI_SUCCESS the initialization was complete successfully +**/ +RETURN_STATUS +EFIAPI +ShellCommandLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + InitializeListHead (&gShellMapList.Link); + InitializeListHead (&mCommandList.Link); + InitializeListHead (&mAliasList.Link); + InitializeListHead (&mScriptList.Link); + InitializeListHead (&mFileHandleList.Link); + mEchoState = TRUE; + + mExitRequested = FALSE; + mExitScript = FALSE; + mProfileListSize = 0; + mProfileList = NULL; + + Status = CommandInit (); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + return (RETURN_SUCCESS); +} + +/** + Frees list of file handles. + + @param[in] List The list to free. +**/ +VOID +FreeFileHandleList ( + IN BUFFER_LIST *List + ) +{ + BUFFER_LIST *BufferListEntry; + + if (List == NULL) { + return; + } + + // + // enumerate through the buffer list and free all memory + // + for ( BufferListEntry = (BUFFER_LIST *)GetFirstNode (&List->Link) + ; !IsListEmpty (&List->Link) + ; BufferListEntry = (BUFFER_LIST *)GetFirstNode (&List->Link) + ) + { + RemoveEntryList (&BufferListEntry->Link); + ASSERT (BufferListEntry->Buffer != NULL); + SHELL_FREE_NON_NULL (((SHELL_COMMAND_FILE_HANDLE *)(BufferListEntry->Buffer))->Path); + SHELL_FREE_NON_NULL (BufferListEntry->Buffer); + SHELL_FREE_NON_NULL (BufferListEntry); + } +} + +/** + Destructor for the library. free any resources. + + @param ImageHandle the image handle of the process + @param SystemTable the EFI System Table pointer + + @retval RETURN_SUCCESS this function always returns success +**/ +RETURN_STATUS +EFIAPI +ShellCommandLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; + ALIAS_LIST *Node2; + SCRIPT_FILE_LIST *Node3; + SHELL_MAP_LIST *MapNode; + + // + // enumerate throught the list and free all the memory + // + while (!IsListEmpty (&mCommandList.Link)) { + Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link); + RemoveEntryList (&Node->Link); + SHELL_FREE_NON_NULL (Node->CommandString); + FreePool (Node); + DEBUG_CODE ( + Node = NULL; + ); + } + + // + // enumerate through the alias list and free all memory + // + while (!IsListEmpty (&mAliasList.Link)) { + Node2 = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link); + RemoveEntryList (&Node2->Link); + SHELL_FREE_NON_NULL (Node2->CommandString); + SHELL_FREE_NON_NULL (Node2->Alias); + SHELL_FREE_NON_NULL (Node2); + DEBUG_CODE ( + Node2 = NULL; + ); + } + + // + // enumerate throught the list and free all the memory + // + while (!IsListEmpty (&mScriptList.Link)) { + Node3 = (SCRIPT_FILE_LIST *)GetFirstNode (&mScriptList.Link); + RemoveEntryList (&Node3->Link); + DeleteScriptFileStruct (Node3->Data); + FreePool (Node3); + } + + // + // enumerate throught the mappings list and free all the memory + // + if (!IsListEmpty (&gShellMapList.Link)) { + for (MapNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link) + ; !IsListEmpty (&gShellMapList.Link) + ; MapNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link) + ) + { + ASSERT (MapNode != NULL); + RemoveEntryList (&MapNode->Link); + SHELL_FREE_NON_NULL (MapNode->DevicePath); + SHELL_FREE_NON_NULL (MapNode->MapName); + SHELL_FREE_NON_NULL (MapNode->CurrentDirectoryPath); + FreePool (MapNode); + } + } + + if (!IsListEmpty (&mFileHandleList.Link)) { + FreeFileHandleList (&mFileHandleList); + } + + if (mProfileList != NULL) { + FreePool (mProfileList); + } + + gUnicodeCollation = NULL; + gShellCurMapping = NULL; + + return (RETURN_SUCCESS); +} + +/** + Find a dynamic command protocol instance given a command name string. + + @param CommandString the command name string + + @return instance the command protocol instance, if dynamic command instance found + @retval NULL no dynamic command protocol instance found for name +**/ +CONST EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL * +ShellCommandFindDynamicCommand ( + IN CONST CHAR16 *CommandString + ) +{ + EFI_STATUS Status; + EFI_HANDLE *CommandHandleList; + EFI_HANDLE *NextCommand; + EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand; + + CommandHandleList = GetHandleListByProtocol (&gEfiShellDynamicCommandProtocolGuid); + if (CommandHandleList == NULL) { + // + // not found or out of resources + // + return NULL; + } + + for (NextCommand = CommandHandleList; *NextCommand != NULL; NextCommand++) { + Status = gBS->HandleProtocol ( + *NextCommand, + &gEfiShellDynamicCommandProtocolGuid, + (VOID **)&DynamicCommand + ); + + if (EFI_ERROR (Status)) { + continue; + } + + if (gUnicodeCollation->StriColl ( + gUnicodeCollation, + (CHAR16 *)CommandString, + (CHAR16 *)DynamicCommand->CommandName + ) == 0 + ) + { + FreePool (CommandHandleList); + return (DynamicCommand); + } + } + + FreePool (CommandHandleList); + return (NULL); +} + +/** + Checks if a command exists as a dynamic command protocol instance + + @param[in] CommandString The command string to check for on the list. +**/ +BOOLEAN +ShellCommandDynamicCommandExists ( + IN CONST CHAR16 *CommandString + ) +{ + return (BOOLEAN)((ShellCommandFindDynamicCommand (CommandString) != NULL)); +} + +/** + Checks if a command is already on the internal command list. + + @param[in] CommandString The command string to check for on the list. +**/ +BOOLEAN +ShellCommandIsCommandOnInternalList ( + IN CONST CHAR16 *CommandString + ) +{ + SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; + + // + // assert for NULL parameter + // + ASSERT (CommandString != NULL); + + // + // check for the command + // + for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link) + ; !IsNull (&mCommandList.Link, &Node->Link) + ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Node->Link) + ) + { + ASSERT (Node->CommandString != NULL); + if (gUnicodeCollation->StriColl ( + gUnicodeCollation, + (CHAR16 *)CommandString, + Node->CommandString + ) == 0 + ) + { + return (TRUE); + } + } + + return (FALSE); +} + +/** + Checks if a command exists, either internally or through the dynamic command protocol. + + @param[in] CommandString The command string to check for on the list. +**/ +BOOLEAN +EFIAPI +ShellCommandIsCommandOnList ( + IN CONST CHAR16 *CommandString + ) +{ + if (ShellCommandIsCommandOnInternalList (CommandString)) { + return TRUE; + } + + return ShellCommandDynamicCommandExists (CommandString); +} + +/** + Get the help text for a dynamic command. + + @param[in] CommandString The command name. + + @retval NULL No help text was found. + @return String of help text. Caller required to free. +**/ +CHAR16 * +ShellCommandGetDynamicCommandHelp ( + IN CONST CHAR16 *CommandString + ) +{ + EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand; + + DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *)ShellCommandFindDynamicCommand (CommandString); + if (DynamicCommand == NULL) { + return (NULL); + } + + // + // TODO: how to get proper language? + // + return DynamicCommand->GetHelp (DynamicCommand, "en"); +} + +/** + Get the help text for an internal command. + + @param[in] CommandString The command name. + + @retval NULL No help text was found. + @return String of help text. Caller reuiqred to free. +**/ +CHAR16 * +ShellCommandGetInternalCommandHelp ( + IN CONST CHAR16 *CommandString + ) +{ + SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; + + // + // assert for NULL parameter + // + ASSERT (CommandString != NULL); + + // + // check for the command + // + for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link) + ; !IsNull (&mCommandList.Link, &Node->Link) + ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Node->Link) + ) + { + ASSERT (Node->CommandString != NULL); + if (gUnicodeCollation->StriColl ( + gUnicodeCollation, + (CHAR16 *)CommandString, + Node->CommandString + ) == 0 + ) + { + return (HiiGetString (Node->HiiHandle, Node->ManFormatHelp, NULL)); + } + } + + return (NULL); +} + +/** + Get the help text for a command. + + @param[in] CommandString The command name. + + @retval NULL No help text was found. + @return String of help text.Caller reuiqred to free. +**/ +CHAR16 * +EFIAPI +ShellCommandGetCommandHelp ( + IN CONST CHAR16 *CommandString + ) +{ + CHAR16 *HelpStr; + + HelpStr = ShellCommandGetInternalCommandHelp (CommandString); + + if (HelpStr == NULL) { + HelpStr = ShellCommandGetDynamicCommandHelp (CommandString); + } + + return HelpStr; +} + +/** + Registers handlers of type SHELL_RUN_COMMAND and + SHELL_GET_MAN_FILENAME for each shell command. + + If the ShellSupportLevel is greater than the value of the + PcdShellSupportLevel then return RETURN_UNSUPPORTED. + + Registers the handlers specified by GetHelpInfoHandler and CommandHandler + with the command specified by CommandString. If the command named by + CommandString has already been registered, then return + RETURN_ALREADY_STARTED. + + If there are not enough resources available to register the handlers then + RETURN_OUT_OF_RESOURCES is returned. + + If CommandString is NULL, then ASSERT(). + If GetHelpInfoHandler is NULL, then ASSERT(). + If CommandHandler is NULL, then ASSERT(). + If ProfileName is NULL, then ASSERT(). + + @param[in] CommandString Pointer to the command name. This is the + name to look for on the command line in + the shell. + @param[in] CommandHandler Pointer to a function that runs the + specified command. + @param[in] GetManFileName Pointer to a function that provides man + filename. + @param[in] ShellMinSupportLevel minimum Shell Support Level which has this + function. + @param[in] ProfileName profile name to require for support of this + function. + @param[in] CanAffectLE indicates whether this command's return value + can change the LASTERROR environment variable. + @param[in] HiiHandle Handle of this command's HII entry. + @param[in] ManFormatHelp HII locator for the help text. + + @retval RETURN_SUCCESS The handlers were registered. + @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to + register the shell command. + @retval RETURN_UNSUPPORTED the ShellMinSupportLevel was higher than the + currently allowed support level. + @retval RETURN_ALREADY_STARTED The CommandString represents a command that + is already registered. Only 1 handler set for + a given command is allowed. + @sa SHELL_GET_MAN_FILENAME + @sa SHELL_RUN_COMMAND +**/ +RETURN_STATUS +EFIAPI +ShellCommandRegisterCommandName ( + IN CONST CHAR16 *CommandString, + IN SHELL_RUN_COMMAND CommandHandler, + IN SHELL_GET_MAN_FILENAME GetManFileName, + IN UINT32 ShellMinSupportLevel, + IN CONST CHAR16 *ProfileName, + IN CONST BOOLEAN CanAffectLE, + IN CONST EFI_HII_HANDLE HiiHandle, + IN CONST EFI_STRING_ID ManFormatHelp + ) +{ + SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; + SHELL_COMMAND_INTERNAL_LIST_ENTRY *Command; + SHELL_COMMAND_INTERNAL_LIST_ENTRY *PrevCommand; + INTN LexicalMatchValue; + + // + // Initialize local variables. + // + Command = NULL; + PrevCommand = NULL; + LexicalMatchValue = 0; + + // + // ASSERTs for NULL parameters + // + ASSERT (CommandString != NULL); + ASSERT (GetManFileName != NULL); + ASSERT (CommandHandler != NULL); + ASSERT (ProfileName != NULL); + + // + // check for shell support level + // + if (PcdGet8 (PcdShellSupportLevel) < ShellMinSupportLevel) { + return (RETURN_UNSUPPORTED); + } + + // + // check for already on the list + // + if (ShellCommandIsCommandOnList (CommandString)) { + return (RETURN_ALREADY_STARTED); + } + + // + // allocate memory for new struct + // + Node = AllocateZeroPool (sizeof (SHELL_COMMAND_INTERNAL_LIST_ENTRY)); + if (Node == NULL) { + return RETURN_OUT_OF_RESOURCES; + } + + Node->CommandString = AllocateCopyPool (StrSize (CommandString), CommandString); + if (Node->CommandString == NULL) { + FreePool (Node); + return RETURN_OUT_OF_RESOURCES; + } + + Node->GetManFileName = GetManFileName; + Node->CommandHandler = CommandHandler; + Node->LastError = CanAffectLE; + Node->HiiHandle = HiiHandle; + Node->ManFormatHelp = ManFormatHelp; + + if ( (StrLen (ProfileName) > 0) + && (( (mProfileList != NULL) + && (StrStr (mProfileList, ProfileName) == NULL)) || (mProfileList == NULL)) + ) + { + ASSERT ((mProfileList == NULL && mProfileListSize == 0) || (mProfileList != NULL)); + if (mProfileList == NULL) { + // + // If this is the first make a leading ';' + // + StrnCatGrow (&mProfileList, &mProfileListSize, L";", 0); + } + + StrnCatGrow (&mProfileList, &mProfileListSize, ProfileName, 0); + StrnCatGrow (&mProfileList, &mProfileListSize, L";", 0); + } + + // + // Insert a new entry on top of the list + // + InsertHeadList (&mCommandList.Link, &Node->Link); + + // + // Move a new registered command to its sorted ordered location in the list + // + for (Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link), + PrevCommand = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link) + ; !IsNull (&mCommandList.Link, &Command->Link) + ; Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Command->Link)) + { + // + // Get Lexical Comparison Value between PrevCommand and Command list entry + // + LexicalMatchValue = gUnicodeCollation->StriColl ( + gUnicodeCollation, + PrevCommand->CommandString, + Command->CommandString + ); + + // + // Swap PrevCommand and Command list entry if PrevCommand list entry + // is alphabetically greater than Command list entry + // + if (LexicalMatchValue > 0) { + Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)SwapListEntries (&PrevCommand->Link, &Command->Link); + } else if (LexicalMatchValue < 0) { + // + // PrevCommand entry is lexically lower than Command entry + // + break; + } + } + + return (RETURN_SUCCESS); +} + +/** + Function to get the current Profile string. + + @retval NULL There are no installed profiles. + @return A semi-colon delimited list of profiles. +**/ +CONST CHAR16 * +EFIAPI +ShellCommandGetProfileList ( + VOID + ) +{ + return (mProfileList); +} + +/** + Checks if a command string has been registered for CommandString and if so it runs + the previously registered handler for that command with the command line. + + If CommandString is NULL, then ASSERT(). + + If Sections is specified, then each section name listed will be compared in a casesensitive + manner, to the section names described in Appendix B UEFI Shell 2.0 spec. 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[in] CommandString Pointer to the command name. This is the name + found on the command line in the shell. + @param[in, out] RetVal Pointer to the return vaule from the command handler. + + @param[in, out] CanAffectLE indicates whether this command's return value + needs to be placed into LASTERROR environment variable. + + @retval RETURN_SUCCESS The handler was run. + @retval RETURN_NOT_FOUND The CommandString did not match a registered + command name. + @sa SHELL_RUN_COMMAND +**/ +RETURN_STATUS +EFIAPI +ShellCommandRunCommandHandler ( + IN CONST CHAR16 *CommandString, + IN OUT SHELL_STATUS *RetVal, + IN OUT BOOLEAN *CanAffectLE OPTIONAL + ) +{ + SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; + EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand; + + // + // assert for NULL parameters + // + ASSERT (CommandString != NULL); + + // + // check for the command + // + for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link) + ; !IsNull (&mCommandList.Link, &Node->Link) + ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Node->Link) + ) + { + ASSERT (Node->CommandString != NULL); + if (gUnicodeCollation->StriColl ( + gUnicodeCollation, + (CHAR16 *)CommandString, + Node->CommandString + ) == 0 + ) + { + if (CanAffectLE != NULL) { + *CanAffectLE = Node->LastError; + } + + if (RetVal != NULL) { + *RetVal = Node->CommandHandler (NULL, gST); + } else { + Node->CommandHandler (NULL, gST); + } + + return (RETURN_SUCCESS); + } + } + + // + // An internal command was not found, try to find a dynamic command + // + DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *)ShellCommandFindDynamicCommand (CommandString); + if (DynamicCommand != NULL) { + if (RetVal != NULL) { + *RetVal = DynamicCommand->Handler (DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol); + } else { + DynamicCommand->Handler (DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol); + } + + return (RETURN_SUCCESS); + } + + return (RETURN_NOT_FOUND); +} + +/** + Checks if a command string has been registered for CommandString and if so it + returns the MAN filename specified for that command. + + If CommandString is NULL, then ASSERT(). + + @param[in] CommandString Pointer to the command name. This is the name + found on the command line in the shell.\ + + @retval NULL the commandString was not a registered command. + @return other the name of the MAN file. + @sa SHELL_GET_MAN_FILENAME +**/ +CONST CHAR16 * +EFIAPI +ShellCommandGetManFileNameHandler ( + IN CONST CHAR16 *CommandString + ) +{ + SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; + + // + // assert for NULL parameters + // + ASSERT (CommandString != NULL); + + // + // check for the command + // + for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link) + ; !IsNull (&mCommandList.Link, &Node->Link) + ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Node->Link) + ) + { + ASSERT (Node->CommandString != NULL); + if (gUnicodeCollation->StriColl ( + gUnicodeCollation, + (CHAR16 *)CommandString, + Node->CommandString + ) == 0 + ) + { + return (Node->GetManFileName ()); + } + } + + return (NULL); +} + +/** + Get the list of all available shell internal commands. This is a linked list + (via LIST_ENTRY structure). enumerate through it using the BaseLib linked + list functions. do not modify the values. + + @param[in] Sort TRUE to alphabetically sort the values first. FALSE otherwise. + + @return a Linked list of all available shell commands. +**/ +CONST COMMAND_LIST * +EFIAPI +ShellCommandGetCommandList ( + IN CONST BOOLEAN Sort + ) +{ + // if (!Sort) { + // return ((COMMAND_LIST*)(&mCommandList)); + // } + return ((COMMAND_LIST *)(&mCommandList)); +} + +/** + Registers aliases to be set as part of the initialization of the shell application. + + If Command is NULL, then ASSERT(). + If Alias is NULL, then ASSERT(). + + @param[in] Command Pointer to the Command + @param[in] Alias Pointer to Alias + + @retval RETURN_SUCCESS The handlers were registered. + @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to + register the shell command. +**/ +RETURN_STATUS +EFIAPI +ShellCommandRegisterAlias ( + IN CONST CHAR16 *Command, + IN CONST CHAR16 *Alias + ) +{ + ALIAS_LIST *Node; + ALIAS_LIST *CommandAlias; + ALIAS_LIST *PrevCommandAlias; + INTN LexicalMatchValue; + + // + // Asserts for NULL + // + ASSERT (Command != NULL); + ASSERT (Alias != NULL); + + // + // allocate memory for new struct + // + Node = AllocateZeroPool (sizeof (ALIAS_LIST)); + if (Node == NULL) { + return RETURN_OUT_OF_RESOURCES; + } + + Node->CommandString = AllocateCopyPool (StrSize (Command), Command); + if (Node->CommandString == NULL) { + FreePool (Node); + return RETURN_OUT_OF_RESOURCES; + } + + Node->Alias = AllocateCopyPool (StrSize (Alias), Alias); + if (Node->Alias == NULL) { + FreePool (Node->CommandString); + FreePool (Node); + return RETURN_OUT_OF_RESOURCES; + } + + InsertHeadList (&mAliasList.Link, &Node->Link); + + // + // Move a new pre-defined registered alias to its sorted ordered location in the list + // + for ( CommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link), + PrevCommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link) + ; !IsNull (&mAliasList.Link, &CommandAlias->Link) + ; CommandAlias = (ALIAS_LIST *)GetNextNode (&mAliasList.Link, &CommandAlias->Link)) + { + // + // Get Lexical comparison value between PrevCommandAlias and CommandAlias List Entry + // + LexicalMatchValue = gUnicodeCollation->StriColl ( + gUnicodeCollation, + PrevCommandAlias->Alias, + CommandAlias->Alias + ); + + // + // Swap PrevCommandAlias and CommandAlias list entry if PrevCommandAlias list entry + // is alphabetically greater than CommandAlias list entry + // + if (LexicalMatchValue > 0) { + CommandAlias = (ALIAS_LIST *)SwapListEntries (&PrevCommandAlias->Link, &CommandAlias->Link); + } else if (LexicalMatchValue < 0) { + // + // PrevCommandAlias entry is lexically lower than CommandAlias entry + // + break; + } + } + + return (RETURN_SUCCESS); +} + +/** + Get the list of all shell alias commands. This is a linked list + (via LIST_ENTRY structure). enumerate through it using the BaseLib linked + list functions. do not modify the values. + + @return a Linked list of all requested shell alias'. +**/ +CONST ALIAS_LIST * +EFIAPI +ShellCommandGetInitAliasList ( + VOID + ) +{ + return (&mAliasList); +} + +/** + Determine if a given alias is on the list of built in alias'. + + @param[in] Alias The alias to test for + + @retval TRUE The alias is a built in alias + @retval FALSE The alias is not a built in alias +**/ +BOOLEAN +EFIAPI +ShellCommandIsOnAliasList ( + IN CONST CHAR16 *Alias + ) +{ + ALIAS_LIST *Node; + + // + // assert for NULL parameter + // + ASSERT (Alias != NULL); + + // + // check for the Alias + // + for ( Node = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link) + ; !IsNull (&mAliasList.Link, &Node->Link) + ; Node = (ALIAS_LIST *)GetNextNode (&mAliasList.Link, &Node->Link) + ) + { + ASSERT (Node->CommandString != NULL); + ASSERT (Node->Alias != NULL); + if (gUnicodeCollation->StriColl ( + gUnicodeCollation, + (CHAR16 *)Alias, + Node->CommandString + ) == 0 + ) + { + return (TRUE); + } + + if (gUnicodeCollation->StriColl ( + gUnicodeCollation, + (CHAR16 *)Alias, + Node->Alias + ) == 0 + ) + { + return (TRUE); + } + } + + return (FALSE); +} + +/** + Function to determine current state of ECHO. Echo determines if lines from scripts + and ECHO commands are enabled. + + @retval TRUE Echo is currently enabled + @retval FALSE Echo is currently disabled +**/ +BOOLEAN +EFIAPI +ShellCommandGetEchoState ( + VOID + ) +{ + return (mEchoState); +} + +/** + Function to set current state of ECHO. Echo determines if lines from scripts + and ECHO commands are enabled. + + If State is TRUE, Echo will be enabled. + If State is FALSE, Echo will be disabled. + + @param[in] State How to set echo. +**/ +VOID +EFIAPI +ShellCommandSetEchoState ( + IN BOOLEAN State + ) +{ + mEchoState = State; +} + +/** + Indicate that the current shell or script should exit. + + @param[in] ScriptOnly TRUE if exiting a script; FALSE otherwise. + @param[in] ErrorCode The 64 bit error code to return. +**/ +VOID +EFIAPI +ShellCommandRegisterExit ( + IN BOOLEAN ScriptOnly, + IN CONST UINT64 ErrorCode + ) +{ + mExitRequested = (BOOLEAN)(!mExitRequested); + if (mExitRequested) { + mExitScript = ScriptOnly; + } else { + mExitScript = FALSE; + } + + mExitCode = ErrorCode; +} + +/** + Retrieve the Exit indicator. + + @retval TRUE Exit was indicated. + @retval FALSE Exis was not indicated. +**/ +BOOLEAN +EFIAPI +ShellCommandGetExit ( + VOID + ) +{ + return (mExitRequested); +} + +/** + Retrieve the Exit code. + + If ShellCommandGetExit returns FALSE than the return from this is undefined. + + @return the value passed into RegisterExit. +**/ +UINT64 +EFIAPI +ShellCommandGetExitCode ( + VOID + ) +{ + return (mExitCode); +} + +/** + Retrieve the Exit script indicator. + + If ShellCommandGetExit returns FALSE than the return from this is undefined. + + @retval TRUE ScriptOnly was indicated. + @retval FALSE ScriptOnly was not indicated. +**/ +BOOLEAN +EFIAPI +ShellCommandGetScriptExit ( + VOID + ) +{ + return (mExitScript); +} + +/** + Function to cleanup all memory from a SCRIPT_FILE structure. + + @param[in] Script The pointer to the structure to cleanup. +**/ +VOID +EFIAPI +DeleteScriptFileStruct ( + IN SCRIPT_FILE *Script + ) +{ + UINT8 LoopVar; + + if (Script == NULL) { + return; + } + + for (LoopVar = 0; LoopVar < Script->Argc; LoopVar++) { + SHELL_FREE_NON_NULL (Script->Argv[LoopVar]); + } + + if (Script->Argv != NULL) { + SHELL_FREE_NON_NULL (Script->Argv); + } + + Script->CurrentCommand = NULL; + while (!IsListEmpty (&Script->CommandList)) { + Script->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode (&Script->CommandList); + if (Script->CurrentCommand != NULL) { + RemoveEntryList (&Script->CurrentCommand->Link); + if (Script->CurrentCommand->Cl != NULL) { + SHELL_FREE_NON_NULL (Script->CurrentCommand->Cl); + } + + if (Script->CurrentCommand->Data != NULL) { + SHELL_FREE_NON_NULL (Script->CurrentCommand->Data); + } + + SHELL_FREE_NON_NULL (Script->CurrentCommand); + } + } + + SHELL_FREE_NON_NULL (Script->ScriptName); + SHELL_FREE_NON_NULL (Script); +} + +/** + Function to return a pointer to the currently running script file object. + + @retval NULL A script file is not currently running. + @return A pointer to the current script file object. +**/ +SCRIPT_FILE * +EFIAPI +ShellCommandGetCurrentScriptFile ( + VOID + ) +{ + SCRIPT_FILE_LIST *List; + + if (IsListEmpty (&mScriptList.Link)) { + return (NULL); + } + + List = ((SCRIPT_FILE_LIST *)GetFirstNode (&mScriptList.Link)); + return (List->Data); +} + +/** + Function to set a new script as the currently running one. + + This function will correctly stack and unstack nested scripts. + + @param[in] Script Pointer to new script information structure. if NULL + will remove and de-allocate the top-most Script structure. + + @return A pointer to the current running script file after this + change. NULL if removing the final script. +**/ +SCRIPT_FILE * +EFIAPI +ShellCommandSetNewScript ( + IN SCRIPT_FILE *Script OPTIONAL + ) +{ + SCRIPT_FILE_LIST *Node; + + if (Script == NULL) { + if (IsListEmpty (&mScriptList.Link)) { + return (NULL); + } + + Node = (SCRIPT_FILE_LIST *)GetFirstNode (&mScriptList.Link); + RemoveEntryList (&Node->Link); + DeleteScriptFileStruct (Node->Data); + FreePool (Node); + } else { + Node = AllocateZeroPool (sizeof (SCRIPT_FILE_LIST)); + if (Node == NULL) { + return (NULL); + } + + Node->Data = Script; + InsertHeadList (&mScriptList.Link, &Node->Link); + } + + return (ShellCommandGetCurrentScriptFile ()); +} + +/** + Function to generate the next default mapping name. + + If the return value is not NULL then it must be callee freed. + + @param Type What kind of mapping name to make. + + @retval NULL a memory allocation failed. + @return a new map name string +**/ +CHAR16 * +EFIAPI +ShellCommandCreateNewMappingName ( + IN CONST SHELL_MAPPING_TYPE Type + ) +{ + CHAR16 *String; + + ASSERT (Type < MappingTypeMax); + + String = NULL; + + String = AllocateZeroPool (PcdGet8 (PcdShellMapNameLength) * sizeof (String[0])); + UnicodeSPrint ( + String, + PcdGet8 (PcdShellMapNameLength) * sizeof (String[0]), + Type == MappingTypeFileSystem ? L"FS%d:" : L"BLK%d:", + Type == MappingTypeFileSystem ? mFsMaxCount++ : mBlkMaxCount++ + ); + + return (String); +} + +/** + Function to add a map node to the list of map items and update the "path" environment variable (optionally). + + If Path is TRUE (during initialization only), the path environment variable will also be updated to include + default paths on the new map name... + + Path should be FALSE when this function is called from the protocol SetMap function. + + @param[in] Name The human readable mapped name. + @param[in] DevicePath The Device Path for this map. + @param[in] Flags The Flags attribute for this map item. + @param[in] Path TRUE to update path, FALSE to skip this step (should only be TRUE during initialization). + + @retval EFI_SUCCESS The addition was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_INVALID_PARAMETER A parameter was invalid. +**/ +EFI_STATUS +EFIAPI +ShellCommandAddMapItemAndUpdatePath ( + IN CONST CHAR16 *Name, + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN CONST UINT64 Flags, + IN CONST BOOLEAN Path + ) +{ + EFI_STATUS Status; + SHELL_MAP_LIST *MapListNode; + CONST CHAR16 *OriginalPath; + CHAR16 *NewPath; + UINTN NewPathSize; + + NewPathSize = 0; + NewPath = NULL; + OriginalPath = NULL; + Status = EFI_SUCCESS; + + MapListNode = AllocateZeroPool (sizeof (SHELL_MAP_LIST)); + if (MapListNode == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } else { + MapListNode->Flags = Flags; + MapListNode->MapName = AllocateCopyPool (StrSize (Name), Name); + MapListNode->DevicePath = DuplicateDevicePath (DevicePath); + if ((MapListNode->MapName == NULL) || (MapListNode->DevicePath == NULL)) { + Status = EFI_OUT_OF_RESOURCES; + } else { + InsertTailList (&gShellMapList.Link, &MapListNode->Link); + } + } + + if (EFI_ERROR (Status)) { + if (MapListNode != NULL) { + if (MapListNode->DevicePath != NULL) { + FreePool (MapListNode->DevicePath); + } + + if (MapListNode->MapName != NULL) { + FreePool (MapListNode->MapName); + } + + FreePool (MapListNode); + } + } else if (Path) { + // + // Since there was no error and Path was TRUE + // Now add the correct path for that mapping + // + OriginalPath = gEfiShellProtocol->GetEnv (L"path"); + ASSERT ((NewPath == NULL && NewPathSize == 0) || (NewPath != NULL)); + if (OriginalPath != NULL) { + StrnCatGrow (&NewPath, &NewPathSize, OriginalPath, 0); + StrnCatGrow (&NewPath, &NewPathSize, L";", 0); + } + + StrnCatGrow (&NewPath, &NewPathSize, Name, 0); + StrnCatGrow (&NewPath, &NewPathSize, L"\\efi\\tools\\;", 0); + StrnCatGrow (&NewPath, &NewPathSize, Name, 0); + StrnCatGrow (&NewPath, &NewPathSize, L"\\efi\\boot\\;", 0); + StrnCatGrow (&NewPath, &NewPathSize, Name, 0); + StrnCatGrow (&NewPath, &NewPathSize, L"\\", 0); + + Status = gEfiShellProtocol->SetEnv (L"path", NewPath, TRUE); + ASSERT_EFI_ERROR (Status); + FreePool (NewPath); + } + + return (Status); +} + +/** + Creates the default map names for each device path in the system with + a protocol depending on the Type. + + Creates the consistent map names for each device path in the system with + a protocol depending on the Type. + + Note: This will reset all mappings in the system("map -r"). + + Also sets up the default path environment variable if Type is FileSystem. + + @retval EFI_SUCCESS All map names were created successfully. + @retval EFI_NOT_FOUND No protocols were found in the system. + @return Error returned from gBS->LocateHandle(). + + @sa LocateHandle +**/ +EFI_STATUS +EFIAPI +ShellCommandCreateInitialMappingsAndPaths ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleList; + UINTN Count; + EFI_DEVICE_PATH_PROTOCOL **DevicePathList; + CHAR16 *NewDefaultName; + CHAR16 *NewConsistName; + EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable; + SHELL_MAP_LIST *MapListNode; + CONST CHAR16 *CurDir; + CHAR16 *SplitCurDir; + CHAR16 *MapName; + SHELL_MAP_LIST *MapListItem; + + ConsistMappingTable = NULL; + SplitCurDir = NULL; + MapName = NULL; + MapListItem = NULL; + HandleList = NULL; + + // + // Reset the static members back to zero + // + mFsMaxCount = 0; + mBlkMaxCount = 0; + + gEfiShellProtocol->SetEnv (L"path", L"", TRUE); + + // + // First empty out the existing list. + // + if (!IsListEmpty (&gShellMapList.Link)) { + for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link) + ; !IsListEmpty (&gShellMapList.Link) + ; MapListNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link) + ) + { + RemoveEntryList (&MapListNode->Link); + SHELL_FREE_NON_NULL (MapListNode->DevicePath); + SHELL_FREE_NON_NULL (MapListNode->MapName); + SHELL_FREE_NON_NULL (MapListNode->CurrentDirectoryPath); + FreePool (MapListNode); + } // for loop + } + + // + // Find each handle with Simple File System + // + HandleList = GetHandleListByProtocol (&gEfiSimpleFileSystemProtocolGuid); + if (HandleList != NULL) { + // + // Do a count of the handles + // + for (Count = 0; HandleList[Count] != NULL; Count++) { + } + + // + // Get all Device Paths + // + DevicePathList = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL *) * Count); + if (DevicePathList == NULL) { + SHELL_FREE_NON_NULL (HandleList); + return EFI_OUT_OF_RESOURCES; + } + + for (Count = 0; HandleList[Count] != NULL; Count++) { + DevicePathList[Count] = DevicePathFromHandle (HandleList[Count]); + } + + // + // Sort all DevicePaths + // + PerformQuickSort (DevicePathList, Count, sizeof (EFI_DEVICE_PATH_PROTOCOL *), DevicePathCompare); + + if (!EFI_ERROR (ShellCommandConsistMappingInitialize (&ConsistMappingTable))) { + // + // Assign new Mappings to all... + // + for (Count = 0; HandleList[Count] != NULL; Count++) { + // + // Get default name first + // + NewDefaultName = ShellCommandCreateNewMappingName (MappingTypeFileSystem); + ASSERT (NewDefaultName != NULL); + Status = ShellCommandAddMapItemAndUpdatePath (NewDefaultName, DevicePathList[Count], 0, TRUE); + ASSERT_EFI_ERROR (Status); + FreePool (NewDefaultName); + + // + // Now do consistent name + // + NewConsistName = ShellCommandConsistMappingGenMappingName (DevicePathList[Count], ConsistMappingTable); + if (NewConsistName != NULL) { + Status = ShellCommandAddMapItemAndUpdatePath (NewConsistName, DevicePathList[Count], 0, FALSE); + ASSERT_EFI_ERROR (Status); + FreePool (NewConsistName); + } + } + } + + if (ConsistMappingTable != NULL) { + ShellCommandConsistMappingUnInitialize (ConsistMappingTable); + } + + SHELL_FREE_NON_NULL (HandleList); + SHELL_FREE_NON_NULL (DevicePathList); + + HandleList = NULL; + + // + // gShellCurMapping point to node of current file system in the gShellMapList. When reset all mappings, + // all nodes in the gShellMapList will be free. Then gShellCurMapping will be a dangling pointer, So, + // after created new mappings, we should reset the gShellCurMapping pointer back to node of current file system. + // + if (gShellCurMapping != NULL) { + gShellCurMapping = NULL; + CurDir = gEfiShellProtocol->GetEnv (L"cwd"); + if (CurDir != NULL) { + MapName = AllocateCopyPool (StrSize (CurDir), CurDir); + if (MapName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SplitCurDir = StrStr (MapName, L":"); + if (SplitCurDir == NULL) { + SHELL_FREE_NON_NULL (MapName); + return EFI_UNSUPPORTED; + } + + *(SplitCurDir + 1) = CHAR_NULL; + MapListItem = ShellCommandFindMapItem (MapName); + if (MapListItem != NULL) { + gShellCurMapping = MapListItem; + } + + SHELL_FREE_NON_NULL (MapName); + } + } + } else { + Count = (UINTN)-1; + } + + // + // Find each handle with Block Io + // + HandleList = GetHandleListByProtocol (&gEfiBlockIoProtocolGuid); + if (HandleList != NULL) { + for (Count = 0; HandleList[Count] != NULL; Count++) { + } + + // + // Get all Device Paths + // + DevicePathList = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL *) * Count); + if (DevicePathList == NULL) { + SHELL_FREE_NON_NULL (HandleList); + return EFI_OUT_OF_RESOURCES; + } + + for (Count = 0; HandleList[Count] != NULL; Count++) { + DevicePathList[Count] = DevicePathFromHandle (HandleList[Count]); + } + + // + // Sort all DevicePaths + // + PerformQuickSort (DevicePathList, Count, sizeof (EFI_DEVICE_PATH_PROTOCOL *), DevicePathCompare); + + // + // Assign new Mappings to all... + // + for (Count = 0; HandleList[Count] != NULL; Count++) { + // + // Get default name first + // + NewDefaultName = ShellCommandCreateNewMappingName (MappingTypeBlockIo); + ASSERT (NewDefaultName != NULL); + Status = ShellCommandAddMapItemAndUpdatePath (NewDefaultName, DevicePathList[Count], 0, FALSE); + ASSERT_EFI_ERROR (Status); + FreePool (NewDefaultName); + } + + SHELL_FREE_NON_NULL (HandleList); + SHELL_FREE_NON_NULL (DevicePathList); + } else if (Count == (UINTN)-1) { + return (EFI_NOT_FOUND); + } + + return (EFI_SUCCESS); +} + +/** + Add mappings for any devices without one. Do not change any existing maps. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +EFIAPI +ShellCommandUpdateMapping ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleList; + UINTN Count; + EFI_DEVICE_PATH_PROTOCOL **DevicePathList; + CHAR16 *NewDefaultName; + CHAR16 *NewConsistName; + EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable; + + HandleList = NULL; + Status = EFI_SUCCESS; + + // + // remove mappings that represent removed devices. + // + + // + // Find each handle with Simple File System + // + HandleList = GetHandleListByProtocol (&gEfiSimpleFileSystemProtocolGuid); + if (HandleList != NULL) { + // + // Do a count of the handles + // + for (Count = 0; HandleList[Count] != NULL; Count++) { + } + + // + // Get all Device Paths + // + DevicePathList = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL *) * Count); + if (DevicePathList == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + for (Count = 0; HandleList[Count] != NULL; Count++) { + DevicePathList[Count] = DevicePathFromHandle (HandleList[Count]); + } + + // + // Sort all DevicePaths + // + PerformQuickSort (DevicePathList, Count, sizeof (EFI_DEVICE_PATH_PROTOCOL *), DevicePathCompare); + + Status = ShellCommandConsistMappingInitialize (&ConsistMappingTable); + + // + // Assign new Mappings to remainders + // + for (Count = 0; !EFI_ERROR (Status) && HandleList[Count] != NULL; Count++) { + // + // Skip ones that already have + // + if (gEfiShellProtocol->GetMapFromDevicePath (&DevicePathList[Count]) != NULL) { + continue; + } + + // + // Get default name + // + NewDefaultName = ShellCommandCreateNewMappingName (MappingTypeFileSystem); + if (NewDefaultName == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + // + // Call shell protocol SetMap function now... + // + Status = gEfiShellProtocol->SetMap (DevicePathList[Count], NewDefaultName); + + if (!EFI_ERROR (Status)) { + // + // Now do consistent name + // + NewConsistName = ShellCommandConsistMappingGenMappingName (DevicePathList[Count], ConsistMappingTable); + if (NewConsistName != NULL) { + Status = gEfiShellProtocol->SetMap (DevicePathList[Count], NewConsistName); + FreePool (NewConsistName); + } + } + + FreePool (NewDefaultName); + } + + ShellCommandConsistMappingUnInitialize (ConsistMappingTable); + SHELL_FREE_NON_NULL (HandleList); + SHELL_FREE_NON_NULL (DevicePathList); + + HandleList = NULL; + } else { + Count = (UINTN)-1; + } + + // + // Do it all over again for gEfiBlockIoProtocolGuid + // + + return (Status); +} + +/** + Converts a SHELL_FILE_HANDLE to an EFI_FILE_PROTOCOL*. + + @param[in] Handle The SHELL_FILE_HANDLE to convert. + + @return a EFI_FILE_PROTOCOL* representing the same file. +**/ +EFI_FILE_PROTOCOL * +EFIAPI +ConvertShellHandleToEfiFileProtocol ( + IN CONST SHELL_FILE_HANDLE Handle + ) +{ + return ((EFI_FILE_PROTOCOL *)(Handle)); +} + +/** + Converts a EFI_FILE_PROTOCOL* to an SHELL_FILE_HANDLE. + + @param[in] Handle The pointer to EFI_FILE_PROTOCOL to convert. + @param[in] Path The path to the file for verification. + + @return A SHELL_FILE_HANDLE representing the same file. + @retval NULL There was not enough memory. +**/ +SHELL_FILE_HANDLE +EFIAPI +ConvertEfiFileProtocolToShellHandle ( + IN CONST EFI_FILE_PROTOCOL *Handle, + IN CONST CHAR16 *Path + ) +{ + SHELL_COMMAND_FILE_HANDLE *Buffer; + BUFFER_LIST *NewNode; + + if (Path != NULL) { + Buffer = AllocateZeroPool (sizeof (SHELL_COMMAND_FILE_HANDLE)); + if (Buffer == NULL) { + return (NULL); + } + + NewNode = AllocateZeroPool (sizeof (BUFFER_LIST)); + if (NewNode == NULL) { + SHELL_FREE_NON_NULL (Buffer); + return (NULL); + } + + Buffer->FileHandle = (EFI_FILE_PROTOCOL *)Handle; + Buffer->Path = StrnCatGrow (&Buffer->Path, NULL, Path, 0); + if (Buffer->Path == NULL) { + SHELL_FREE_NON_NULL (NewNode); + SHELL_FREE_NON_NULL (Buffer); + return (NULL); + } + + NewNode->Buffer = Buffer; + + InsertHeadList (&mFileHandleList.Link, &NewNode->Link); + } + + return ((SHELL_FILE_HANDLE)(Handle)); +} + +/** + Find the path that was logged with the specified SHELL_FILE_HANDLE. + + @param[in] Handle The SHELL_FILE_HANDLE to query on. + + @return A pointer to the path for the file. +**/ +CONST CHAR16 * +EFIAPI +ShellFileHandleGetPath ( + IN CONST SHELL_FILE_HANDLE Handle + ) +{ + BUFFER_LIST *Node; + + for (Node = (BUFFER_LIST *)GetFirstNode (&mFileHandleList.Link) + ; !IsNull (&mFileHandleList.Link, &Node->Link) + ; Node = (BUFFER_LIST *)GetNextNode (&mFileHandleList.Link, &Node->Link) + ) + { + if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)) { + return (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path); + } + } + + return (NULL); +} + +/** + Remove a SHELL_FILE_HANDLE from the list of SHELL_FILE_HANDLES. + + @param[in] Handle The SHELL_FILE_HANDLE to remove. + + @retval TRUE The item was removed. + @retval FALSE The item was not found. +**/ +BOOLEAN +EFIAPI +ShellFileHandleRemove ( + IN CONST SHELL_FILE_HANDLE Handle + ) +{ + BUFFER_LIST *Node; + + for (Node = (BUFFER_LIST *)GetFirstNode (&mFileHandleList.Link) + ; !IsNull (&mFileHandleList.Link, &Node->Link) + ; Node = (BUFFER_LIST *)GetNextNode (&mFileHandleList.Link, &Node->Link) + ) + { + if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)) { + RemoveEntryList (&Node->Link); + SHELL_FREE_NON_NULL (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path); + SHELL_FREE_NON_NULL (Node->Buffer); + SHELL_FREE_NON_NULL (Node); + return (TRUE); + } + } + + return (FALSE); +} + +/** + Function to determine if a SHELL_FILE_HANDLE is at the end of the file. + + This will NOT work on directories. + + If Handle is NULL, then ASSERT. + + @param[in] Handle the file handle + + @retval TRUE the position is at the end of the file + @retval FALSE the position is not at the end of the file +**/ +BOOLEAN +EFIAPI +ShellFileHandleEof ( + IN SHELL_FILE_HANDLE Handle + ) +{ + EFI_FILE_INFO *Info; + UINT64 Pos; + BOOLEAN RetVal; + + // + // ASSERT if Handle is NULL + // + ASSERT (Handle != NULL); + + gEfiShellProtocol->GetFilePosition (Handle, &Pos); + Info = gEfiShellProtocol->GetFileInfo (Handle); + gEfiShellProtocol->SetFilePosition (Handle, Pos); + + if (Info == NULL) { + return (FALSE); + } + + if (Pos == Info->FileSize) { + RetVal = TRUE; + } else { + RetVal = FALSE; + } + + FreePool (Info); + + return (RetVal); +} + +/** + Frees any BUFFER_LIST defined type. + + @param[in] List The BUFFER_LIST object to free. +**/ +VOID +EFIAPI +FreeBufferList ( + IN BUFFER_LIST *List + ) +{ + BUFFER_LIST *BufferListEntry; + + if (List == NULL) { + return; + } + + // + // enumerate through the buffer list and free all memory + // + for ( BufferListEntry = (BUFFER_LIST *)GetFirstNode (&List->Link) + ; !IsListEmpty (&List->Link) + ; BufferListEntry = (BUFFER_LIST *)GetFirstNode (&List->Link) + ) + { + RemoveEntryList (&BufferListEntry->Link); + if (BufferListEntry->Buffer != NULL) { + FreePool (BufferListEntry->Buffer); + } + + FreePool (BufferListEntry); + } +} + +/** + Dump some hexadecimal data to the screen. + + @param[in] Indent How many spaces to indent the output. + @param[in] Offset The offset of the printing. + @param[in] DataSize The size in bytes of UserData. + @param[in] UserData The data to print out. +**/ +VOID +EFIAPI +DumpHex ( + IN UINTN Indent, + IN UINTN Offset, + IN UINTN DataSize, + IN VOID *UserData + ) +{ + UINT8 *Data; + + CHAR8 Val[50]; + + CHAR8 Str[20]; + + UINT8 TempByte; + UINTN Size; + UINTN Index; + + Data = UserData; + while (DataSize != 0) { + Size = 16; + if (Size > DataSize) { + Size = DataSize; + } + + for (Index = 0; Index < Size; Index += 1) { + TempByte = Data[Index]; + Val[Index * 3 + 0] = Hex[TempByte >> 4]; + Val[Index * 3 + 1] = Hex[TempByte & 0xF]; + Val[Index * 3 + 2] = (CHAR8)((Index == 7) ? '-' : ' '); + Str[Index] = (CHAR8)((TempByte < ' ' || TempByte > '~') ? '.' : TempByte); + } + + Val[Index * 3] = 0; + Str[Index] = 0; + ShellPrintEx (-1, -1, L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str); + + Data += Size; + Offset += Size; + DataSize -= Size; + } +} + +/** + Dump HEX data into buffer. + + @param[in] Buffer HEX data to be dumped in Buffer. + @param[in] Indent How many spaces to indent the output. + @param[in] Offset The offset of the printing. + @param[in] DataSize The size in bytes of UserData. + @param[in] UserData The data to print out. +**/ +CHAR16 * +EFIAPI +CatSDumpHex ( + IN CHAR16 *Buffer, + IN UINTN Indent, + IN UINTN Offset, + IN UINTN DataSize, + IN VOID *UserData + ) +{ + UINT8 *Data; + UINT8 TempByte; + UINTN Size; + UINTN Index; + CHAR8 Val[50]; + CHAR8 Str[20]; + CHAR16 *RetVal; + CHAR16 *TempRetVal; + + Data = UserData; + RetVal = Buffer; + while (DataSize != 0) { + Size = 16; + if (Size > DataSize) { + Size = DataSize; + } + + for (Index = 0; Index < Size; Index += 1) { + TempByte = Data[Index]; + Val[Index * 3 + 0] = Hex[TempByte >> 4]; + Val[Index * 3 + 1] = Hex[TempByte & 0xF]; + Val[Index * 3 + 2] = (CHAR8)((Index == 7) ? '-' : ' '); + Str[Index] = (CHAR8)((TempByte < ' ' || TempByte > 'z') ? '.' : TempByte); + } + + Val[Index * 3] = 0; + Str[Index] = 0; + TempRetVal = CatSPrint (RetVal, L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str); + SHELL_FREE_NON_NULL (RetVal); + RetVal = TempRetVal; + + Data += Size; + Offset += Size; + DataSize -= Size; + } + + return RetVal; +} + +/** + ORDERED_COLLECTION_USER_COMPARE function for SHELL_SORT_UNIQUE_NAME objects. + + @param[in] Unique1AsVoid The first SHELL_SORT_UNIQUE_NAME object (Unique1), + passed in as a pointer-to-VOID. + + @param[in] Unique2AsVoid The second SHELL_SORT_UNIQUE_NAME object (Unique2), + passed in as a pointer-to-VOID. + + @retval <0 If Unique1 compares less than Unique2. + + @retval 0 If Unique1 compares equal to Unique2. + + @retval >0 If Unique1 compares greater than Unique2. +**/ +STATIC +INTN +EFIAPI +UniqueNameCompare ( + IN CONST VOID *Unique1AsVoid, + IN CONST VOID *Unique2AsVoid + ) +{ + CONST SHELL_SORT_UNIQUE_NAME *Unique1; + CONST SHELL_SORT_UNIQUE_NAME *Unique2; + + Unique1 = Unique1AsVoid; + Unique2 = Unique2AsVoid; + + // + // We need to cast away CONST for EFI_UNICODE_COLLATION_STRICOLL. + // + return gUnicodeCollation->StriColl ( + gUnicodeCollation, + (CHAR16 *)Unique1->Alias, + (CHAR16 *)Unique2->Alias + ); +} + +/** + ORDERED_COLLECTION_KEY_COMPARE function for SHELL_SORT_UNIQUE_NAME objects. + + @param[in] UniqueAliasAsVoid The CHAR16 string UniqueAlias, passed in as a + pointer-to-VOID. + + @param[in] UniqueAsVoid The SHELL_SORT_UNIQUE_NAME object (Unique), + passed in as a pointer-to-VOID. + + @retval <0 If UniqueAlias compares less than Unique->Alias. + + @retval 0 If UniqueAlias compares equal to Unique->Alias. + + @retval >0 If UniqueAlias compares greater than Unique->Alias. +**/ +STATIC +INTN +EFIAPI +UniqueNameAliasCompare ( + IN CONST VOID *UniqueAliasAsVoid, + IN CONST VOID *UniqueAsVoid + ) +{ + CONST CHAR16 *UniqueAlias; + CONST SHELL_SORT_UNIQUE_NAME *Unique; + + UniqueAlias = UniqueAliasAsVoid; + Unique = UniqueAsVoid; + + // + // We need to cast away CONST for EFI_UNICODE_COLLATION_STRICOLL. + // + return gUnicodeCollation->StriColl ( + gUnicodeCollation, + (CHAR16 *)UniqueAlias, + (CHAR16 *)Unique->Alias + ); +} + +/** + Sort an EFI_SHELL_FILE_INFO list, optionally moving duplicates to a separate + list. + + @param[in,out] FileList The list of EFI_SHELL_FILE_INFO objects to sort. + + If FileList is NULL on input, then FileList is + considered an empty, hence already sorted, list. + + Otherwise, if (*FileList) is NULL on input, then + EFI_INVALID_PARAMETER is returned. + + Otherwise, the caller is responsible for having + initialized (*FileList)->Link with + InitializeListHead(). No other fields in the + (**FileList) head element are accessed by this + function. + + On output, (*FileList) is sorted according to Order. + If Duplicates is NULL on input, then duplicate + elements are preserved, sorted stably, on + (*FileList). If Duplicates is not NULL on input, + then duplicates are moved (stably sorted) to the + new, dynamically allocated (*Duplicates) list. + + @param[out] Duplicates If Duplicates is NULL on input, (*FileList) will be + a monotonically ordered list on output, with + duplicates stably sorted. + + If Duplicates is not NULL on input, (*FileList) will + be a strictly monotonically oredered list on output, + with duplicates separated (stably sorted) to + (*Duplicates). All fields except Link will be + zero-initialized in the (**Duplicates) head element. + If no duplicates exist, then (*Duplicates) is set to + NULL on output. + + @param[in] Order Determines the comparison operation between + EFI_SHELL_FILE_INFO objects. + + @retval EFI_INVALID_PARAMETER (UINTN)Order is greater than or equal to + (UINTN)ShellSortFileListMax. Neither the + (*FileList) nor the (*Duplicates) list has + been modified. + + @retval EFI_INVALID_PARAMETER (*FileList) was NULL on input. Neither the + (*FileList) nor the (*Duplicates) list has + been modified. + + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. Neither the + (*FileList) nor the (*Duplicates) list has + been modified. + + @retval EFI_SUCCESS Sorting successful, including the case when + FileList is NULL on input. +**/ +EFI_STATUS +EFIAPI +ShellSortFileList ( + IN OUT EFI_SHELL_FILE_INFO **FileList, + OUT EFI_SHELL_FILE_INFO **Duplicates OPTIONAL, + IN SHELL_SORT_FILE_LIST Order + ) +{ + LIST_ENTRY *FilesHead; + ORDERED_COLLECTION *Sort; + LIST_ENTRY *FileEntry; + EFI_SHELL_FILE_INFO *FileInfo; + SHELL_SORT_UNIQUE_NAME *Unique; + EFI_STATUS Status; + EFI_SHELL_FILE_INFO *Dupes; + LIST_ENTRY *NextFileEntry; + CONST CHAR16 *Alias; + ORDERED_COLLECTION_ENTRY *SortEntry; + LIST_ENTRY *TargetFileList; + ORDERED_COLLECTION_ENTRY *NextSortEntry; + VOID *UniqueAsVoid; + + if ((UINTN)Order >= (UINTN)ShellSortFileListMax) { + return EFI_INVALID_PARAMETER; + } + + if (FileList == NULL) { + // + // FileList is considered empty, hence already sorted, with no duplicates. + // + if (Duplicates != NULL) { + *Duplicates = NULL; + } + + return EFI_SUCCESS; + } + + if (*FileList == NULL) { + return EFI_INVALID_PARAMETER; + } + + FilesHead = &(*FileList)->Link; + + // + // Collect all the unique names. + // + Sort = OrderedCollectionInit (UniqueNameCompare, UniqueNameAliasCompare); + if (Sort == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + BASE_LIST_FOR_EACH (FileEntry, FilesHead) { + FileInfo = (EFI_SHELL_FILE_INFO *)FileEntry; + + // + // Try to record the name of this file as a unique name. + // + Unique = AllocatePool (sizeof (*Unique)); + if (Unique == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto UninitSort; + } + + Unique->Alias = ((Order == ShellSortFileListByFileName) ? + FileInfo->FileName : + FileInfo->FullName); + InitializeListHead (&Unique->SameNameList); + + Status = OrderedCollectionInsert (Sort, NULL, Unique); + if (EFI_ERROR (Status)) { + // + // Only two errors are possible: memory allocation failed, or this name + // has been encountered before. In either case, the + // SHELL_SORT_UNIQUE_NAME object being constructed has to be released. + // + FreePool (Unique); + // + // Memory allocation failure is fatal, while having seen the same name + // before is normal. + // + if (Status == EFI_OUT_OF_RESOURCES) { + goto UninitSort; + } + + ASSERT (Status == EFI_ALREADY_STARTED); + } + } + + // + // Set Dupes to suppress incorrect compiler/analyzer warnings. + // + Dupes = NULL; + + // + // If separation of duplicates has been requested, allocate the list for + // them. + // + if (Duplicates != NULL) { + Dupes = AllocateZeroPool (sizeof (*Dupes)); + if (Dupes == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto UninitSort; + } + + InitializeListHead (&Dupes->Link); + } + + // + // No memory allocation beyond this point; thus, no chance to fail. We can + // now migrate the EFI_SHELL_FILE_INFO objects from (*FileList) to Sort. + // + BASE_LIST_FOR_EACH_SAFE (FileEntry, NextFileEntry, FilesHead) { + FileInfo = (EFI_SHELL_FILE_INFO *)FileEntry; + // + // Look up the SHELL_SORT_UNIQUE_NAME that matches FileInfo's name. + // + Alias = ((Order == ShellSortFileListByFileName) ? + FileInfo->FileName : + FileInfo->FullName); + SortEntry = OrderedCollectionFind (Sort, Alias); + ASSERT (SortEntry != NULL); + Unique = OrderedCollectionUserStruct (SortEntry); + // + // Move FileInfo from (*FileList) to the end of the list of files whose + // names all compare identical to FileInfo's name. + // + RemoveEntryList (&FileInfo->Link); + InsertTailList (&Unique->SameNameList, &FileInfo->Link); + } + + // + // All EFI_SHELL_FILE_INFO objects originally in (*FileList) have been + // distributed to Sort. Now migrate them back to (*FileList), advancing in + // unique name order. + // + for (SortEntry = OrderedCollectionMin (Sort); + SortEntry != NULL; + SortEntry = OrderedCollectionNext (SortEntry)) + { + Unique = OrderedCollectionUserStruct (SortEntry); + // + // The first FileInfo encountered for each unique name goes back on + // (*FileList) unconditionally. Further FileInfo instances for the same + // unique name -- that is, duplicates -- are either returned to (*FileList) + // or separated, dependent on the caller's request. + // + TargetFileList = FilesHead; + BASE_LIST_FOR_EACH_SAFE (FileEntry, NextFileEntry, &Unique->SameNameList) { + RemoveEntryList (FileEntry); + InsertTailList (TargetFileList, FileEntry); + if (Duplicates != NULL) { + TargetFileList = &Dupes->Link; + } + } + } + + // + // We're done. If separation of duplicates has been requested, output the + // list of duplicates -- and free that list at once, if it's empty (i.e., if + // no duplicates have been found). + // + if (Duplicates != NULL) { + if (IsListEmpty (&Dupes->Link)) { + FreePool (Dupes); + *Duplicates = NULL; + } else { + *Duplicates = Dupes; + } + } + + Status = EFI_SUCCESS; + + // + // Fall through. + // +UninitSort: + for (SortEntry = OrderedCollectionMin (Sort); + SortEntry != NULL; + SortEntry = NextSortEntry) + { + NextSortEntry = OrderedCollectionNext (SortEntry); + OrderedCollectionDelete (Sort, SortEntry, &UniqueAsVoid); + Unique = UniqueAsVoid; + ASSERT (IsListEmpty (&Unique->SameNameList)); + FreePool (Unique); + } + + OrderedCollectionUninit (Sort); + + return Status; +} |
