REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3737 Apply uncrustify changes to .c/.h files in the ShellPkg package Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Ray Ni <ray.ni@intel.com>
		
			
				
	
	
		
			2325 lines
		
	
	
		
			65 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2325 lines
		
	
	
		
			65 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @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 sucessfully
 | 
						|
**/
 | 
						|
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 sucessful.
 | 
						|
  @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 sucessfully.
 | 
						|
  @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;
 | 
						|
 | 
						|
  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);
 | 
						|
 | 
						|
    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);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    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);
 | 
						|
 | 
						|
    ShellCommandConsistMappingInitialize (&ConsistMappingTable);
 | 
						|
 | 
						|
    //
 | 
						|
    // Assign new Mappings to remainders
 | 
						|
    //
 | 
						|
    for (Count = 0; !EFI_ERROR (Status) && HandleList[Count] != NULL && !EFI_ERROR (Status); 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;
 | 
						|
}
 |