]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ShellPkg/Library/UefiShellLevel3CommandsLib/Help.c
ShellPkg/for: Fix potential null pointer deference
[mirror_edk2.git] / ShellPkg / Library / UefiShellLevel3CommandsLib / Help.c
index e816e0b2e4fba3a57c058ff920a2860f388fef23..f6159c133585b67c5ab208c53e3e5364a1a414e6 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   Main file for Help shell level 3 function.\r
 \r
-  Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved. <BR>\r
+  Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved. <BR>\r
   Copyright (c) 2014, ARM Limited. All rights reserved. <BR>\r
   (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>\r
 \r
 #include <Library/ShellLib.h>\r
 #include <Library/HandleParsingLib.h>\r
 \r
-#include <Protocol/EfiShellDynamicCommand.h>\r
+#include <Protocol/ShellDynamicCommand.h>\r
+\r
+/**\r
+   function to insert string items into a list in the correct alphabetical place\r
+\r
+   the resultant list is a double NULL terminated list of NULL terminated strings.\r
+\r
+   upon successful return the memory must be caller freed (unless passed back in \r
+   via a loop where it will get reallocated).\r
+\r
+   @param[in,out] DestList    double pointer to the list. may be NULL.\r
+   @param[in,out] DestSize    pointer to the size of list. may be 0, if DestList is NULL.\r
+   @param[in]     Item        the item to insert.\r
+\r
+   @retval EFI_SUCCESS        the operation was successful.\r
+**/\r
+EFI_STATUS\r
+LexicalInsertIntoList(\r
+  IN OUT   CHAR16 **DestList, \r
+  IN OUT   UINTN  *DestSize,\r
+  IN CONST CHAR16 *Item\r
+  )\r
+{\r
+  CHAR16                              *NewList;\r
+  INTN                                LexicalMatchValue;\r
+  CHAR16                              *LexicalSpot;\r
+  UINTN                               SizeOfAddedNameInBytes;\r
+\r
+  //\r
+  // If there are none, then just return with success\r
+  //\r
+  if (Item == NULL || *Item == CHAR_NULL || StrLen(Item)==0) {\r
+    return (EFI_SUCCESS);\r
+  }\r
+\r
+  NewList = *DestList;\r
+\r
+  SizeOfAddedNameInBytes = StrSize(Item);\r
+  NewList = ReallocatePool(*DestSize, (*DestSize) + SizeOfAddedNameInBytes, NewList);\r
+  (*DestSize) = (*DestSize) + SizeOfAddedNameInBytes;\r
+\r
+  //\r
+  // Find the correct spot in the list\r
+  //\r
+  for (LexicalSpot = NewList\r
+    ; LexicalSpot != NULL && LexicalSpot < NewList + (*DestSize)\r
+    ; LexicalSpot += StrLen(LexicalSpot) + 1\r
+    ) {\r
+    //\r
+    // Get Lexical Comparison Value between PrevCommand and Command list entry\r
+    //\r
+    LexicalMatchValue = gUnicodeCollation->StriColl (\r
+                                              gUnicodeCollation,\r
+                                              (CHAR16 *)LexicalSpot,\r
+                                              (CHAR16 *)Item\r
+                                              );\r
+    //\r
+    // The new item goes before this one.\r
+    //\r
+    if (LexicalMatchValue > 0 || StrLen(LexicalSpot) == 0) {\r
+      if (StrLen(LexicalSpot) != 0) {\r
+        //\r
+        // Move this and all other items out of the way\r
+        //\r
+        CopyMem(\r
+          LexicalSpot + (SizeOfAddedNameInBytes/sizeof(CHAR16)),\r
+          LexicalSpot,\r
+          (*DestSize) - SizeOfAddedNameInBytes - ((LexicalSpot - NewList) * sizeof(CHAR16))\r
+          );\r
+      }\r
+\r
+      //\r
+      // Stick this one in place\r
+      //\r
+      StrCpyS(LexicalSpot, SizeOfAddedNameInBytes/sizeof(CHAR16), Item);\r
+      break;\r
+    }\r
+  }\r
+\r
+  *DestList = NewList;\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+   function to add each command name from the linked list to the string list.\r
+\r
+   the resultant list is a double NULL terminated list of NULL terminated strings.\r
+\r
+   @param[in,out] DestList    double pointer to the list. may be NULL.\r
+   @param[in,out] DestSize    pointer to the size of list. may be 0, if DestList is NULL.\r
+   @param[in]     SourceList  the double linked list of commands.\r
+\r
+   @retval EFI_SUCCESS        the operation was successful.\r
+**/\r
+EFI_STATUS\r
+CopyListOfCommandNames(\r
+  IN OUT   CHAR16       **DestList, \r
+  IN OUT   UINTN        *DestSize,\r
+  IN CONST COMMAND_LIST *SourceList\r
+  )\r
+{\r
+  CONST COMMAND_LIST  *Node;\r
+\r
+  for ( Node = (COMMAND_LIST*)GetFirstNode(&SourceList->Link)\r
+      ; SourceList != NULL && !IsListEmpty(&SourceList->Link) && !IsNull(&SourceList->Link, &Node->Link)\r
+      ; Node = (COMMAND_LIST*)GetNextNode(&SourceList->Link, &Node->Link)\r
+    ) {\r
+    LexicalInsertIntoList(DestList, DestSize, Node->CommandString);\r
+  }\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+   function to add each dynamic command name to the string list.\r
+\r
+   the resultant list is a double NULL terminated list of NULL terminated strings.\r
+\r
+   @param[in,out] DestList    double pointer to the list. may be NULL.\r
+   @param[in,out] DestSize    pointer to the size of list. may be 0, if DestList is NULL.\r
+\r
+   @retval EFI_SUCCESS        the operation was successful.\r
+   @return an error from HandleProtocol\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+CopyListOfCommandNamesWithDynamic(\r
+  IN OUT  CHAR16** DestList, \r
+  IN OUT  UINTN    *DestSize\r
+  )\r
+{\r
+  EFI_HANDLE                          *CommandHandleList;\r
+  CONST EFI_HANDLE                    *NextCommand;\r
+  EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;\r
+  EFI_STATUS                          Status;\r
+\r
+  CommandHandleList = GetHandleListByProtocol(&gEfiShellDynamicCommandProtocolGuid);\r
+\r
+  //\r
+  // If there are none, then just return with success\r
+  //\r
+  if (CommandHandleList == NULL) {\r
+    return (EFI_SUCCESS);\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  //\r
+  // Append those to the list.\r
+  //\r
+  for (NextCommand = CommandHandleList ; *NextCommand != NULL && !EFI_ERROR(Status) ; NextCommand++) {\r
+    Status = gBS->HandleProtocol(\r
+      *NextCommand,\r
+      &gEfiShellDynamicCommandProtocolGuid,\r
+      (VOID **)&DynamicCommand\r
+      );\r
+\r
+    if (EFI_ERROR(Status)) {\r
+      continue;\r
+    }\r
+\r
+    Status = LexicalInsertIntoList(DestList, DestSize, DynamicCommand->CommandName);\r
+  }\r
+\r
+  SHELL_FREE_NON_NULL(CommandHandleList);\r
+  return (Status);\r
+}\r
+\r
 \r
 /**\r
   Attempt to print help from a dynamically added command.\r
   @retval EFI_DEVICE_ERROR        The help data format was incorrect.\r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
 PrintDynamicCommandHelp(\r
-  IN CHAR16  *CommandToGetHelpOn,\r
-  IN CHAR16  *SectionToGetHelpOn,\r
-  IN BOOLEAN  PrintCommandText\r
+  IN CONST CHAR16  *CommandToGetHelpOn,\r
+  IN CONST CHAR16  *SectionToGetHelpOn,\r
+  IN BOOLEAN       PrintCommandText\r
  )\r
 {\r
   EFI_STATUS                          Status;\r
@@ -80,7 +245,7 @@ PrintDynamicCommandHelp(
       break;\r
     }\r
 \r
-    if ((gUnicodeCollation->MetaiMatch (gUnicodeCollation, (CHAR16 *)DynamicCommand->CommandName, CommandToGetHelpOn)) ||\r
+    if ((gUnicodeCollation->MetaiMatch (gUnicodeCollation, (CHAR16 *)DynamicCommand->CommandName, (CHAR16*)CommandToGetHelpOn)) ||\r
       (gEfiShellProtocol->GetAlias (CommandToGetHelpOn, NULL) != NULL && (gUnicodeCollation->MetaiMatch (gUnicodeCollation, (CHAR16 *)DynamicCommand->CommandName, (CHAR16*)(gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL)))))) {\r
       // Print as Shell Help if in ManPage format.\r
       Status = ShellPrintHelp (DynamicCommand->CommandName, SectionToGetHelpOn,\r
@@ -97,6 +262,8 @@ PrintDynamicCommandHelp(
     }\r
   }\r
 \r
+  SHELL_FREE_NON_NULL(CommandHandleList);\r
+\r
   return (Found ? EFI_SUCCESS : Status);\r
 \r
 }\r
@@ -126,19 +293,21 @@ ShellCommandRunHelp (
   LIST_ENTRY          *Package;\r
   CHAR16              *ProblemParam;\r
   SHELL_STATUS        ShellStatus;\r
-  CONST COMMAND_LIST  *CommandList;\r
-  CONST COMMAND_LIST  *Node;\r
+  CHAR16              *SortedCommandList;\r
+  CONST CHAR16        *CurrentCommand;\r
   CHAR16              *CommandToGetHelpOn;\r
   CHAR16              *SectionToGetHelpOn;\r
   CHAR16              *HiiString;\r
   BOOLEAN             Found;\r
   BOOLEAN             PrintCommandText;\r
+  UINTN               SortedCommandListSize;\r
 \r
   PrintCommandText    = TRUE;\r
   ProblemParam        = NULL;\r
   ShellStatus         = SHELL_SUCCESS;\r
   CommandToGetHelpOn  = NULL;\r
   SectionToGetHelpOn  = NULL;\r
+  SortedCommandList   = NULL;\r
   Found               = FALSE;\r
 \r
   //\r
@@ -222,43 +391,44 @@ ShellCommandRunHelp (
         FreePool(HiiString);\r
         Found = TRUE;\r
       } else {\r
-        CommandList = ShellCommandGetCommandList(TRUE);\r
-        ASSERT(CommandList != NULL);\r
-        for ( Node = (COMMAND_LIST*)GetFirstNode(&CommandList->Link)\r
-            ; CommandList != NULL && !IsListEmpty(&CommandList->Link) && !IsNull(&CommandList->Link, &Node->Link)\r
-            ; Node = (COMMAND_LIST*)GetNextNode(&CommandList->Link, &Node->Link)\r
-           ){\r
+        SortedCommandList = NULL;\r
+        SortedCommandListSize = 0;\r
+        CopyListOfCommandNames(&SortedCommandList, &SortedCommandListSize, ShellCommandGetCommandList(TRUE));\r
+        CopyListOfCommandNamesWithDynamic(&SortedCommandList, &SortedCommandListSize);\r
+\r
+        for (CurrentCommand = SortedCommandList \r
+          ; CurrentCommand != NULL && CurrentCommand < SortedCommandList + SortedCommandListSize/sizeof(CHAR16) && *CurrentCommand != CHAR_NULL\r
+          ; CurrentCommand += StrLen(CurrentCommand) + 1\r
+          ) {\r
           //\r
           // Checking execution break flag when print multiple command help information.\r
           //\r
           if (ShellGetExecutionBreakFlag ()) {\r
             break;\r
           } \r
-          if ((gUnicodeCollation->MetaiMatch(gUnicodeCollation, Node->CommandString, CommandToGetHelpOn)) ||\r
-             (gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL) != NULL && (gUnicodeCollation->MetaiMatch(gUnicodeCollation, Node->CommandString, (CHAR16*)(gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL)))))) {\r
+\r
+          if ((gUnicodeCollation->MetaiMatch(gUnicodeCollation, (CHAR16*)CurrentCommand, CommandToGetHelpOn)) ||\r
+             (gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL) != NULL && (gUnicodeCollation->MetaiMatch(gUnicodeCollation, (CHAR16*)CurrentCommand, (CHAR16*)(gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL)))))) {\r
             //\r
             // We have a command to look for help on.\r
             //\r
-            Status = ShellPrintHelp(Node->CommandString, SectionToGetHelpOn, PrintCommandText);\r
+            Status = ShellPrintHelp(CurrentCommand, SectionToGetHelpOn, PrintCommandText);\r
+            if (EFI_ERROR(Status)) {\r
+              //\r
+              // now try to match against the dynamic command list and print help\r
+              //\r
+              Status = PrintDynamicCommandHelp (CurrentCommand, SectionToGetHelpOn, PrintCommandText);\r
+            }\r
             if (Status == EFI_DEVICE_ERROR) {\r
-                ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_INV), gShellLevel3HiiHandle, Node->CommandString);\r
+                ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_INV), gShellLevel3HiiHandle, CurrentCommand);\r
             } else if (EFI_ERROR(Status)) {\r
-                ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_NF), gShellLevel3HiiHandle, Node->CommandString);\r
+                ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_NF), gShellLevel3HiiHandle, CurrentCommand);\r
             } else {\r
                 Found = TRUE;\r
             }\r
           }\r
         }\r
 \r
-        //\r
-        // now try to match against the dynamic command list and print help\r
-        //\r
-        Status = PrintDynamicCommandHelp (CommandToGetHelpOn, SectionToGetHelpOn,\r
-                                          PrintCommandText);\r
-        if (!EFI_ERROR(Status)) {\r
-          Found = TRUE;\r
-        }\r
-\r
         //\r
         // Search the .man file for Shell applications (Shell external commands).\r
         //\r
@@ -298,6 +468,7 @@ ShellCommandRunHelp (
   if (SectionToGetHelpOn != NULL) {\r
     FreePool(SectionToGetHelpOn);\r
   }\r
+  SHELL_FREE_NON_NULL(SortedCommandList);\r
 \r
   return (ShellStatus);\r
 }\r