mirror of
				https://git.proxmox.com/git/mirror_edk2
				synced 2025-10-31 15:06:49 +00:00 
			
		
		
		
	 eec44cdd00
			
		
	
	
		eec44cdd00
		
	
	
	
	
		
			
			`Dupes` is used only if `Duplicates != NULL` and function is left if allocation of memory for `Dupes` fails, so it can't be used uninitialized. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=3228 Cc: Ray Ni <ray.ni@intel.com> Cc: Zhichao Gao <zhichao.gao@intel.com> Signed-off-by: Sergei Dmitrouk <sergei@posteo.net> Message-Id: <20210511225616.5942-2-sergei@posteo.net> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
		
			
				
	
	
		
			2229 lines
		
	
	
		
			64 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2229 lines
		
	
	
		
			64 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;
 | |
| }
 |