UEFI Shell scandalizes the help message in spec level so that a standalone
UEFI shell application can never get "-?" switch, instead the Shell core
(interpreter) detects the "-?" and finds .MAN file for that shell
application in certain spec defined paths, then show the help extracted
from that .MAN file.

But it means distributing a UEFI shell application not only means
distributing a .EFI file but also distributing a .MAN file. If the text
formatted .MAN file is corrupted (edited by user by mistake), or is
missing (deleted by user by mistake), no help will be shown to user.

So this patch enhance the Shell to make it support finding help message
imbedded in resource section of application image.

Cc: Jaben Carsey <[email protected]>
Cc: Ruiyu Ni <[email protected]>
Cc: Liming Gao <[email protected]>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Qiu Shumin <[email protected]>
---
 ShellPkg/Application/Shell/Shell.h          |   3 +-
 ShellPkg/Application/Shell/ShellManParser.c | 739 +++++++++++++++++++++++++++-
 2 files changed, 726 insertions(+), 16 deletions(-)

diff --git a/ShellPkg/Application/Shell/Shell.h 
b/ShellPkg/Application/Shell/Shell.h
index 351b941..df7199b 100644
--- a/ShellPkg/Application/Shell/Shell.h
+++ b/ShellPkg/Application/Shell/Shell.h
@@ -2,7 +2,7 @@
   function definitions for internal to shell functions.
 
   (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
-  Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+  Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
   This program and the accompanying materials
   are licensed and made available under the terms and conditions of the BSD 
License
   which accompanies this distribution.  The full text of the license may be 
found at
@@ -29,6 +29,7 @@
 #include <Protocol/EfiShellEnvironment2.h>
 #include <Protocol/EfiShellParameters.h>
 #include <Protocol/BlockIo.h>
+#include <Protocol/HiiPackageList.h>
 
 #include <Library/BaseLib.h>
 #include <Library/UefiApplicationEntryPoint.h>
diff --git a/ShellPkg/Application/Shell/ShellManParser.c 
b/ShellPkg/Application/Shell/ShellManParser.c
index 222cdaf..49703ac 100644
--- a/ShellPkg/Application/Shell/ShellManParser.c
+++ b/ShellPkg/Application/Shell/ShellManParser.c
@@ -1,7 +1,7 @@
 /** @file
   Provides interface to shell MAN file parser.
 
-  Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+  Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
   Copyright 2015 Dell Inc.
   This program and the accompanying materials
   are licensed and made available under the terms and conditions of the BSD 
License
@@ -37,6 +37,58 @@ InternalShellCharToUpper (
   );
 
 /**
+  Verifies that the filename has .EFI on the end.
+
+  allocates a new buffer and copies the name (appending .EFI if necessary).
+  Caller to free the buffer.
+
+  @param[in] NameString            original name string
+
+  @return                          the new filename with .efi as the extension.
+**/
+CHAR16 *
+EFIAPI
+GetExecuatableFileName (
+  IN CONST CHAR16    *NameString
+  )
+{
+  CHAR16  *Buffer;
+  CHAR16  *SuffixStr;
+  if (NameString == NULL) {
+    return (NULL);
+  }
+
+  //
+  // Fix the file name
+  //
+  if (StrnCmp(NameString+StrLen(NameString)-StrLen(L".efi"), L".efi", 
StrLen(L".efi"))==0) {
+    Buffer = AllocateCopyPool(StrSize(NameString), NameString);
+  } else if (StrnCmp(NameString+StrLen(NameString)-StrLen(L".man"), L".man", 
StrLen(L".man"))==0) {
+    Buffer = AllocateCopyPool(StrSize(NameString), NameString);
+    if (Buffer != NULL) {
+      SuffixStr = Buffer+StrLen(Buffer)-StrLen(L".man");
+      StrnCpyS (SuffixStr, StrSize(L".man")/sizeof(CHAR16), L".efi", 
StrLen(L".efi"));
+    }
+  } else {
+    Buffer = AllocateZeroPool(StrSize(NameString) + 
StrLen(L".efi")*sizeof(CHAR16));
+    if (Buffer != NULL) {
+      StrnCpyS( Buffer,
+                (StrSize(NameString) + 
StrLen(L".efi")*sizeof(CHAR16))/sizeof(CHAR16),
+                NameString,
+                StrLen(NameString)
+                );
+      StrnCatS( Buffer,
+                (StrSize(NameString) + 
StrLen(L".efi")*sizeof(CHAR16))/sizeof(CHAR16),
+                L".efi",
+                StrLen(L".efi")
+                );
+    }
+  }
+  return (Buffer);
+
+}
+
+/**
   Verifies that the filename has .MAN on the end.
 
   allocates a new buffer and copies the name (appending .MAN if necessary)
@@ -381,7 +433,7 @@ ManFileFindSections(
 
   Upon a sucessful return the caller is responsible to free the memory in 
*BriefDesc
 
-  @param[in] Handle             Buffer to read from
+  @param[in] Buffer             Buffer to read from
   @param[in] Command            name of command's section to find
   @param[in] BriefDesc          pointer to pointer to string where description 
goes.
   @param[in] BriefSize          pointer to size of allocated BriefDesc
@@ -404,6 +456,7 @@ ManBufferFindTitleSection(
   CHAR16        *TitleEnd;
   CHAR16        *CurrentLocation;
   UINTN         TitleLength;
+  UINTN         Start;
   CONST CHAR16  StartString[] = L".TH ";
   CONST CHAR16  EndString[]   = L" 0 ";
 
@@ -417,15 +470,26 @@ ManBufferFindTitleSection(
   Status    = EFI_SUCCESS;
 
   //
+  // Do not pass any leading path information that may be present to 
IsTitleHeader().
+  //
+  Start = StrLen(Command);
+  while ((Start != 0)
+         && (*(Command + Start - 1) != L'\\')
+         && (*(Command + Start - 1) != L'/')
+         && (*(Command + Start - 1) != L':')) {
+    --Start;
+  }
+
+  //
   // more characters for StartString and EndString
   //
-  TitleLength = StrSize(Command) + (StrLen(StartString) + StrLen(EndString)) * 
sizeof(CHAR16);
+  TitleLength = StrSize(Command + Start) + (StrLen(StartString) + 
StrLen(EndString)) * sizeof(CHAR16);
   TitleString = AllocateZeroPool(TitleLength);
   if (TitleString == NULL) {
     return (EFI_OUT_OF_RESOURCES);
   }
   StrCpyS(TitleString, TitleLength/sizeof(CHAR16), StartString);
-  StrCatS(TitleString, TitleLength/sizeof(CHAR16), Command);
+  StrCatS(TitleString, TitleLength/sizeof(CHAR16), Command + Start);
   StrCatS(TitleString, TitleLength/sizeof(CHAR16), EndString);
 
   CurrentLocation = StrStr(*Buffer, TitleString);
@@ -679,6 +743,560 @@ ManFileFindTitleSection(
 }
 
 /**
+  Calculate the size of StringSrc and output it. If StringDest is not NULL,
+  copy string text from src to dest.
+
+  This is a internal function.
+
+  @param  StringDest             Buffer to store the string text. If it is 
NULL,
+                                 only the size will be returned.
+  @param  StringSrc              Points to current null-terminated string.
+  @param  BufferSize             Length of the buffer.
+
+  @retval EFI_SUCCESS            The string text was outputed successfully.
+  @retval EFI_BUFFER_TOO_SMALL   Buffer is insufficient to store the found 
string
+                                 text. BufferSize is updated to the required 
buffer
+                                 size.
+**/
+EFI_STATUS
+ShellManGetUnicodeStringTextOrSize (
+  OUT EFI_STRING       StringDest, OPTIONAL
+  IN  UINT8            *StringSrc,
+  IN  OUT UINTN        *BufferSize
+  )
+{
+  UINTN  StringSize;
+  UINT8  *StringPtr;
+
+  ASSERT (StringSrc != NULL && BufferSize != NULL);
+
+  StringSize = sizeof (CHAR16);
+  StringPtr  = StringSrc;
+  while (ReadUnaligned16 ((UINT16 *) StringPtr) != 0) {
+    StringSize += sizeof (CHAR16);
+    StringPtr += sizeof (CHAR16);
+  }
+
+  if (*BufferSize < StringSize) {
+    *BufferSize = StringSize;
+    return EFI_BUFFER_TOO_SMALL;
+  }
+  if (StringDest != NULL) {
+    CopyMem (StringDest, StringSrc, StringSize);
+  }
+
+  *BufferSize = StringSize;
+  return EFI_SUCCESS;
+}
+
+/**
+  Convert Ascii string text to unicode string test.
+
+  This is a internal function.
+
+
+  @param  StringDest             Buffer to store the string text. If it is 
NULL,
+                                 only the size will be returned.
+  @param  StringSrc              Points to current null-terminated string.
+  @param  BufferSize             Length of the buffer.
+
+  @retval EFI_SUCCESS            The string text was outputed successfully.
+  @retval EFI_BUFFER_TOO_SMALL   Buffer is insufficient to store the found 
string
+                                 text. BufferSize is updated to the required 
buffer
+                                 size.
+**/
+EFI_STATUS
+ShellManConvertToUnicodeText (
+  OUT EFI_STRING       StringDest,
+  IN  CHAR8            *StringSrc,
+  IN  OUT UINTN        *BufferSize
+  )
+{
+  UINTN  StringSize;
+  UINTN  Index;
+
+  ASSERT (StringSrc != NULL && BufferSize != NULL);
+
+  StringSize = AsciiStrSize (StringSrc) * 2;
+  if (*BufferSize < StringSize || StringDest == NULL) {
+    *BufferSize = StringSize;
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  for (Index = 0; Index < AsciiStrLen (StringSrc); Index++) {
+    StringDest[Index] = (CHAR16) StringSrc[Index];
+  }
+
+  StringDest[Index] = 0;
+  return EFI_SUCCESS;
+}
+
+/**
+  Parse all string blocks to find a String block specified by StringId.
+  If StringId = (EFI_STRING_ID) (-1), find out all string blocks
+  within this string package and backup its information. If LastStringId is
+  specified, the string id of last string block will also be output.
+  If StringId = 0, output the string id of last string block 
(EFI_HII_SIBT_STRING).
+
+  @param  StringBlockArray        Hii string block to parse.
+  @param  StringId                The string's id, which is unique within
+                                  PackageList.
+  @param  BlockType               Output the block type of found string block.
+  @param  StringBlockAddr         Output the block address of found string 
block.
+  @param  StringTextOffset        Offset, relative to the found block address, 
of
+                                  the  string text information.
+  @param  LastStringId            Output the last string id when StringId = 0 
or StringId = -1.
+  @param  StartStringId           The first id in the skip block which 
StringId in the block.
+
+  @retval EFI_SUCCESS             The string text and font is retrieved
+                                  successfully.
+  @retval EFI_NOT_FOUND           The specified text or font info can not be 
found
+                                  out.
+  @retval EFI_OUT_OF_RESOURCES    The system is out of resources to accomplish 
the
+                                  task.
+  @retval EFI_INVALID_PARAMETER   The specified text or font info can not be 
found
+                                  out.
+
+**/
+EFI_STATUS
+ParseStrBlkArrWorker (
+  IN UINT8                  *StringBlockArray,
+  IN EFI_STRING_ID                StringId,
+  OUT UINT8                       *BlockType,
+  OUT UINT8                       **StringBlockAddr,
+  OUT UINTN                       *StringTextOffset,
+  OUT EFI_STRING_ID               *LastStringId,
+  OUT EFI_STRING_ID               *StartStringId
+  )
+{
+  EFI_STRING_ID             CurrentStringId;
+  UINT8                     *BlockHdr;
+  UINTN                     BlockSize;
+  UINTN                     Offset;
+  UINTN                     StringSize;
+  UINT8                     *StringTextPtr;
+  UINT8                     Length8;
+  UINT16                    StringCount;
+  UINT16                    Index;
+  UINT16                    SkipCount;
+  UINT16                    FontSize;
+  UINT32                    Length32;
+  EFI_HII_SIBT_EXT2_BLOCK   Ext2;
+  UINT8                     FontId;
+  EFI_HII_FONT_STYLE        FontStyle;
+
+
+  CurrentStringId = 1;
+  BlockHdr        = NULL;
+  StringTextPtr   = NULL;
+  StringSize      = 0;
+
+  if (StringBlockArray == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (StringId != (EFI_STRING_ID)(-1) && StringId != 0) {
+    if (BlockType == NULL || StringBlockAddr == NULL || StringTextOffset == 
NULL){
+      return EFI_INVALID_PARAMETER;
+    }
+  } else {
+    if (StringId == 0 && LastStringId != NULL) {
+      return EFI_SUCCESS;
+    }
+  }
+
+  //
+  // Parse the string blocks to get the string only.
+  //
+  BlockHdr    = StringBlockArray;
+  BlockSize   = 0;
+  Offset      = 0;
+  while (*BlockHdr != EFI_HII_SIBT_END) {
+    switch (*BlockHdr){
+      case EFI_HII_SIBT_STRING_SCSU:
+        Offset = sizeof (EFI_HII_STRING_BLOCK);
+        StringTextPtr = BlockHdr + Offset;
+        BlockSize += Offset + AsciiStrSize ((CHAR8 *) StringTextPtr);
+        CurrentStringId++;
+        break;
+
+      case EFI_HII_SIBT_STRING_SCSU_FONT:
+        Offset = sizeof (EFI_HII_SIBT_STRING_SCSU_FONT_BLOCK) - sizeof (UINT8);
+        StringTextPtr = BlockHdr + Offset;
+        BlockSize += Offset + AsciiStrSize ((CHAR8 *) StringTextPtr);
+        CurrentStringId++;
+        break;
+
+      case EFI_HII_SIBT_STRINGS_SCSU:
+        CopyMem (&StringCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), 
sizeof (UINT16));
+        StringTextPtr = (UINT8*)((UINTN)BlockHdr + sizeof 
(EFI_HII_SIBT_STRINGS_SCSU_BLOCK) - sizeof (UINT8));
+        BlockSize += StringTextPtr - BlockHdr;
+
+        for (Index = 0; Index < StringCount; Index++) {
+          BlockSize += AsciiStrSize ((CHAR8 *) StringTextPtr);
+          if (CurrentStringId == StringId) {
+            ASSERT (BlockType != NULL && StringBlockAddr != NULL && 
StringTextOffset != NULL);
+            *BlockType        = *BlockHdr;
+            *StringBlockAddr  = BlockHdr;
+            *StringTextOffset = StringTextPtr - BlockHdr;
+            return EFI_SUCCESS;
+          }
+          StringTextPtr = StringTextPtr + AsciiStrSize ((CHAR8 *) 
StringTextPtr);
+          CurrentStringId++;
+        }
+        break;
+
+      case EFI_HII_SIBT_STRINGS_SCSU_FONT:
+        CopyMem (
+          &StringCount,
+          (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof 
(UINT8)),
+          sizeof (UINT16)
+        );
+        StringTextPtr = (UINT8*)((UINTN)BlockHdr + sizeof 
(EFI_HII_SIBT_STRINGS_SCSU_FONT_BLOCK) - sizeof (UINT8));
+        BlockSize += StringTextPtr - BlockHdr;
+
+        for (Index = 0; Index < StringCount; Index++) {
+          BlockSize += AsciiStrSize ((CHAR8 *) StringTextPtr);
+          if (CurrentStringId == StringId) {
+            ASSERT (BlockType != NULL && StringBlockAddr != NULL && 
StringTextOffset != NULL);
+            *BlockType        = *BlockHdr;
+            *StringBlockAddr  = BlockHdr;
+            *StringTextOffset = StringTextPtr - BlockHdr;
+            return EFI_SUCCESS;
+          }
+          StringTextPtr = StringTextPtr + AsciiStrSize ((CHAR8 *) 
StringTextPtr);
+          CurrentStringId++;
+        }
+        break;
+
+      case EFI_HII_SIBT_STRING_UCS2:
+        Offset        = sizeof (EFI_HII_STRING_BLOCK);
+        StringTextPtr = BlockHdr + Offset;
+        ShellManGetUnicodeStringTextOrSize (NULL, StringTextPtr, &StringSize);
+        BlockSize += Offset + StringSize;
+        CurrentStringId++;
+        break;
+
+      case EFI_HII_SIBT_STRING_UCS2_FONT:
+        Offset = sizeof (EFI_HII_SIBT_STRING_UCS2_FONT_BLOCK)  - sizeof 
(CHAR16);
+        StringTextPtr = BlockHdr + Offset;
+        ShellManGetUnicodeStringTextOrSize (NULL, StringTextPtr, &StringSize);
+        BlockSize += Offset + StringSize;
+        CurrentStringId++;
+        break;
+
+      case EFI_HII_SIBT_STRINGS_UCS2:
+        Offset = sizeof (EFI_HII_SIBT_STRINGS_UCS2_BLOCK) - sizeof (CHAR16);
+        StringTextPtr = BlockHdr + Offset;
+        BlockSize += Offset;
+        CopyMem (&StringCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), 
sizeof (UINT16));
+        for (Index = 0; Index < StringCount; Index++) {
+          ShellManGetUnicodeStringTextOrSize (NULL, StringTextPtr, 
&StringSize);
+          BlockSize += StringSize;
+          if (CurrentStringId == StringId) {
+            ASSERT (BlockType != NULL && StringBlockAddr != NULL && 
StringTextOffset != NULL);
+            *BlockType        = *BlockHdr;
+            *StringBlockAddr  = BlockHdr;
+            *StringTextOffset = StringTextPtr - BlockHdr;
+            return EFI_SUCCESS;
+          }
+          StringTextPtr = StringTextPtr + StringSize;
+          CurrentStringId++;
+        }
+        break;
+
+      case EFI_HII_SIBT_STRINGS_UCS2_FONT:
+        Offset = sizeof (EFI_HII_SIBT_STRINGS_UCS2_FONT_BLOCK) - sizeof 
(CHAR16);
+        StringTextPtr = BlockHdr + Offset;
+        BlockSize += Offset;
+        CopyMem (
+          &StringCount,
+          (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof 
(UINT8)),
+          sizeof (UINT16)
+        );
+        for (Index = 0; Index < StringCount; Index++) {
+          ShellManGetUnicodeStringTextOrSize (NULL, StringTextPtr, 
&StringSize);
+          BlockSize += StringSize;
+          if (CurrentStringId == StringId) {
+            ASSERT (BlockType != NULL && StringBlockAddr != NULL && 
StringTextOffset != NULL);
+            *BlockType        = *BlockHdr;
+            *StringBlockAddr  = BlockHdr;
+            *StringTextOffset = StringTextPtr - BlockHdr;
+            return EFI_SUCCESS;
+          }
+          StringTextPtr = StringTextPtr + StringSize;
+          CurrentStringId++;
+        }
+        break;
+
+      case EFI_HII_SIBT_DUPLICATE:
+        if (CurrentStringId == StringId) {
+          CopyMem (
+            &StringId,
+            BlockHdr + sizeof (EFI_HII_STRING_BLOCK),
+            sizeof (EFI_STRING_ID)
+            );
+          ASSERT (StringId != CurrentStringId);
+          CurrentStringId = 1;
+          BlockSize       = 0;
+        } else {
+          BlockSize       += sizeof (EFI_HII_SIBT_DUPLICATE_BLOCK);
+          CurrentStringId++;
+        }
+        break;
+
+      case EFI_HII_SIBT_SKIP1:
+        SkipCount = (UINT16) (*(UINT8*)((UINTN)BlockHdr + sizeof 
(EFI_HII_STRING_BLOCK)));
+        CurrentStringId = (UINT16) (CurrentStringId + SkipCount);
+        BlockSize       +=  sizeof (EFI_HII_SIBT_SKIP1_BLOCK);
+        break;
+
+      case EFI_HII_SIBT_SKIP2:
+        CopyMem (&SkipCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof 
(UINT16));
+        CurrentStringId = (UINT16) (CurrentStringId + SkipCount);
+        BlockSize       +=  sizeof (EFI_HII_SIBT_SKIP2_BLOCK);
+        break;
+
+      case EFI_HII_SIBT_EXT1:
+        CopyMem (
+          &Length8,
+          (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof 
(UINT8)),
+          sizeof (UINT8)
+          );
+        BlockSize += Length8;
+        break;
+
+      case EFI_HII_SIBT_EXT2:
+        CopyMem (&Ext2, BlockHdr, sizeof (EFI_HII_SIBT_EXT2_BLOCK));
+        if (Ext2.BlockType2 == EFI_HII_SIBT_FONT && StringId == 
(EFI_STRING_ID) (-1)) {
+          BlockHdr += sizeof (EFI_HII_SIBT_EXT2_BLOCK);
+          CopyMem (&FontId, BlockHdr, sizeof (UINT8));
+          BlockHdr ++;
+          CopyMem (&FontSize, BlockHdr, sizeof (UINT16));
+          BlockHdr += sizeof (UINT16);
+          CopyMem (&FontStyle, BlockHdr, sizeof (EFI_HII_FONT_STYLE));
+          BlockHdr += sizeof (EFI_HII_FONT_STYLE);
+        }
+        BlockSize += Ext2.Length;
+        break;
+
+      case EFI_HII_SIBT_EXT4:
+        CopyMem (
+          &Length32,
+          (UINT8*)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof 
(UINT8)),
+          sizeof (UINT32)
+        );
+        BlockSize += Length32;
+        break;
+
+      default:
+        break;
+    }
+
+    //
+    // If we want to get the specified StringId string.
+    //
+    if (StringId > 0 && StringId != (EFI_STRING_ID)(-1)){
+      *BlockType        = *BlockHdr;
+      *StringBlockAddr  = BlockHdr;
+      *StringTextOffset = Offset;
+
+      //
+      // If found the specified StringId
+      //
+      if (StringId == CurrentStringId - 1) {
+        //
+        // if only one skip item, return EFI_NOT_FOUND.
+        //
+        if(*BlockType == EFI_HII_SIBT_SKIP2 || *BlockType == 
EFI_HII_SIBT_SKIP1) {
+          return EFI_NOT_FOUND;
+        } else {
+          return EFI_SUCCESS;
+        }
+      }
+
+      if (StringId < CurrentStringId - 1) {
+        return EFI_NOT_FOUND;
+      }
+    }
+
+    BlockHdr  = StringBlockArray + BlockSize;
+    if (StartStringId != NULL) {
+        *StartStringId  = CurrentStringId;
+    }
+  }
+
+  //
+  // Get last string ID
+  //
+  if (StringId == (EFI_STRING_ID) (-1) && LastStringId != NULL) {
+    *LastStringId = (EFI_STRING_ID) (CurrentStringId - 1);
+    return EFI_SUCCESS;
+  }
+
+  return EFI_NOT_FOUND;
+
+}
+
+/**
+  Get all the strings text from the PacakgeList. The strings arrays are 
delimited by
+  CHAR_NULL.
+
+  @param  PackageListHdr            Hii package list header.
+  @param  StringBuffer              The strings text buffer. Free by caller
+  @param  BufferSize                The StringBuffer size in bytes
+
+  @retval EFI_SUCCESS               Get strings success.
+  @retval EFI_NOT_FOUND             None strings are found.
+  @retval EFI_OUT_OF_RESOURCES      The system is out of resources to 
accomplish the
+                                    task.
+**/
+EFI_STATUS
+ParseStringPackageList(
+  IN EFI_HII_PACKAGE_LIST_HEADER  *PackageListHdr,
+  OUT CHAR16                      **StringBuffer,
+  OUT UINTN                       *BufferSize
+  )
+{
+  EFI_HII_PACKAGE_HEADER                *PackageHdrPtr;
+  EFI_HII_PACKAGE_HEADER                PackageHeader;
+  EFI_HII_STRING_PACKAGE_HDR            *StrPkgHdrPtr;
+  EFI_STRING_ID                         LastStringId;
+  EFI_STRING_ID                         StrIdIndex;
+  EFI_STATUS                            Status;
+  BOOLEAN                               AtLeastOneFound;
+  UINT32                                StrPkgHdrSize;
+  UINT8                                 *StringBlockArray;
+  UINT8                                 *StringTextPtr;
+  UINT8                                 BlockType;
+  UINT8                                 *StringBlockAddr;
+  UINTN                                 StringTextOffset;
+  UINTN                                 TempStringSize;
+  CHAR16                                *TempString;
+  CHAR16                                *StrBufferWalker;
+
+
+  PackageHdrPtr     = NULL;
+  StrPkgHdrPtr      = NULL;
+  AtLeastOneFound   = FALSE;
+  StringBlockArray  = NULL;
+  StringTextPtr     = NULL;
+  TempStringSize    = 0;
+  TempString        = NULL;
+  *BufferSize       = 0;
+  *StringBuffer     = NULL;
+  StrBufferWalker   = NULL;
+
+  PackageHdrPtr = (EFI_HII_PACKAGE_HEADER *)((UINT8 *)PackageListHdr + 
sizeof(EFI_HII_PACKAGE_LIST_HEADER));
+  CopyMem (&PackageHeader, PackageHdrPtr, sizeof (EFI_HII_PACKAGE_HEADER));
+
+  while (PackageHeader.Type != EFI_HII_PACKAGE_END) {
+    if (PackageHeader.Type == EFI_HII_PACKAGE_STRINGS) {
+      //
+      // Parse every string packages
+      //
+      StrPkgHdrPtr = (EFI_HII_STRING_PACKAGE_HDR *)PackageHdrPtr;
+      CopyMem (&StrPkgHdrSize, (UINT8 *) StrPkgHdrPtr + sizeof 
(EFI_HII_PACKAGE_HEADER), sizeof (UINT32));
+      StringBlockArray = (UINT8 *)StrPkgHdrPtr + StrPkgHdrSize;
+
+      //
+      // Get the max StringID in this package
+      //
+      Status = ParseStrBlkArrWorker(StringBlockArray, (EFI_STRING_ID) (-1), 
NULL, NULL, NULL, &LastStringId, NULL);
+      if (EFI_ERROR(Status)) {
+        PackageHdrPtr = (EFI_HII_PACKAGE_HEADER *) ((UINT8 *) PackageHdrPtr + 
PackageHeader.Length);
+        CopyMem (&PackageHeader, PackageHdrPtr, sizeof 
(EFI_HII_PACKAGE_HEADER));
+        continue;
+      }
+      //
+      // Parse every string in string package
+      //
+      for (StrIdIndex = 0; StrIdIndex <= LastStringId; StrIdIndex++) {
+        Status = ParseStrBlkArrWorker (
+                                        StringBlockArray,
+                                        StrIdIndex,
+                                        &BlockType,
+                                        &StringBlockAddr,
+                                        &StringTextOffset,
+                                        NULL,
+                                        NULL
+                                      );
+        if EFI_ERROR(Status) {
+          continue;
+        } else {
+          //
+          // Get string successfully & draw content.
+          //
+          StringTextPtr = StringBlockAddr + StringTextOffset;
+          switch (BlockType) {
+            case EFI_HII_SIBT_STRING_SCSU:
+            case EFI_HII_SIBT_STRING_SCSU_FONT:
+            case EFI_HII_SIBT_STRINGS_SCSU:
+            case EFI_HII_SIBT_STRINGS_SCSU_FONT:
+              TempStringSize = 0;
+              Status = ShellManConvertToUnicodeText (TempString, (CHAR8 *) 
StringTextPtr, &TempStringSize);
+              if (Status == EFI_BUFFER_TOO_SMALL) {
+                TempString = (CHAR16 *)AllocateZeroPool(TempStringSize);
+                if (TempString == NULL) {
+                  return EFI_OUT_OF_RESOURCES;
+                }
+                Status = ShellManConvertToUnicodeText (TempString, (CHAR8 *) 
StringTextPtr, &TempStringSize);
+                ASSERT (Status == EFI_SUCCESS);
+              }
+              break;
+
+            case EFI_HII_SIBT_STRING_UCS2:
+            case EFI_HII_SIBT_STRING_UCS2_FONT:
+            case EFI_HII_SIBT_STRINGS_UCS2:
+            case EFI_HII_SIBT_STRINGS_UCS2_FONT:
+              TempStringSize = 0;
+              Status = ShellManGetUnicodeStringTextOrSize (TempString, 
StringTextPtr, &TempStringSize);
+              if (Status == EFI_BUFFER_TOO_SMALL) {
+                TempString = (CHAR16 *)AllocateZeroPool(TempStringSize);
+                if (TempString == NULL) {
+                  return EFI_OUT_OF_RESOURCES;
+                }
+                Status = ShellManGetUnicodeStringTextOrSize (TempString, 
StringTextPtr, &TempStringSize);
+                ASSERT (Status == EFI_SUCCESS);
+              }
+              break;
+          }
+
+          //
+          // Append the string into StringBuffer
+          //
+          *StringBuffer = (CHAR16 *)ReallocatePool(*BufferSize, *BufferSize + 
TempStringSize, *StringBuffer);
+          *BufferSize   += TempStringSize;
+          if (*StringBuffer == NULL) {
+            SHELL_FREE_NON_NULL(TempString);
+            return EFI_OUT_OF_RESOURCES;
+          }
+          StrBufferWalker = *StringBuffer;
+          StrBufferWalker += (*BufferSize - TempStringSize) / sizeof(CHAR16);
+          CopyMem((VOID*)StrBufferWalker, (VOID*)TempString, TempStringSize);
+
+          SHELL_FREE_NON_NULL(TempString);
+          AtLeastOneFound = TRUE;
+        }
+      }
+    }
+
+    //
+    // goto header of next package
+    //
+    PackageHdrPtr = (EFI_HII_PACKAGE_HEADER *) ((UINT8 *) PackageHdrPtr + 
PackageHeader.Length);
+    CopyMem (&PackageHeader, PackageHdrPtr, sizeof (EFI_HII_PACKAGE_HEADER));
+  }
+
+  if (AtLeastOneFound) {
+    return EFI_SUCCESS;
+  } else {
+    return EFI_NOT_FOUND;
+  }
+
+}
+
+/**
   This function returns the help information for the specified command. The 
help text
   will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B)
 
@@ -720,13 +1338,20 @@ ProcessManFile(
 {
   CHAR16            *TempString;
   SHELL_FILE_HANDLE FileHandle;
+  EFI_HANDLE        CmdFileImgHandle;
   EFI_STATUS        Status;
   UINTN             HelpSize;
   UINTN             BriefSize;
   BOOLEAN           Ascii;
   CHAR16            *TempString2;
-  EFI_DEVICE_PATH_PROTOCOL  *FileDevPath;
-  EFI_DEVICE_PATH_PROTOCOL  *DevPath;
+  CHAR16            *CmdFileName;
+  CHAR16            *CmdFilePathName;
+  CHAR16            *StrArrBuff;
+  CHAR16            *StrArrWalker;
+  UINTN             StrArrBuffSize;
+  EFI_DEVICE_PATH_PROTOCOL      *FileDevPath;
+  EFI_DEVICE_PATH_PROTOCOL      *DevPath;
+  EFI_HII_PACKAGE_LIST_HEADER   *PackageListHeader;
 
   if ( ManFileName == NULL
     || Command     == NULL
@@ -735,10 +1360,19 @@ ProcessManFile(
     return (EFI_INVALID_PARAMETER);
   }
 
-  HelpSize    = 0;
-  BriefSize   = 0;
-  TempString  = NULL;
-  Ascii       = FALSE;
+  HelpSize          = 0;
+  BriefSize         = 0;
+  StrArrBuffSize    = 0;
+  TempString        = NULL;
+  Ascii             = FALSE;
+  CmdFileName       = NULL;
+  CmdFilePathName   = NULL;
+  CmdFileImgHandle  = NULL;
+  StrArrBuff        = NULL;
+  PackageListHeader = NULL;
+  FileDevPath       = NULL;
+  DevPath           = NULL;
+
   //
   // See if it's in HII first
   //
@@ -750,6 +1384,9 @@ ProcessManFile(
       Status = ManBufferFindSections(TempString2, Sections, HelpText, 
&HelpSize);
     }
   } else {
+    //
+    // If the image is a external app, check .MAN file first.
+    //
     FileHandle    = NULL;
     TempString  = GetManFileName(ManFileName);
     if (TempString == NULL) {
@@ -761,8 +1398,8 @@ ProcessManFile(
       FileDevPath = FileDevicePath(NULL, TempString);
       DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, FileDevPath);
       Status = InternalOpenFileDevicePath(DevPath, &FileHandle, 
EFI_FILE_MODE_READ, 0);
-      FreePool(FileDevPath);
-      FreePool(DevPath);
+      SHELL_FREE_NON_NULL(FileDevPath);
+      SHELL_FREE_NON_NULL(DevPath);
     }
 
     if (!EFI_ERROR(Status)) {
@@ -773,13 +1410,85 @@ ProcessManFile(
         Status = ManFileFindSections(FileHandle, Sections, HelpText, 
&HelpSize, Ascii);
       }
       ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
-    } else {
+      if (!EFI_ERROR(Status)) {
+        //
+        // Get help text from .MAN file success.
+        //
+        goto Done;
+      }
+    }
+
+    //
+    // Load the app image to check  EFI_HII_PACKAGE_LIST_PROTOCOL.
+    //
+    CmdFileName     = GetExecuatableFileName(TempString);
+    //
+    // If the file in CWD then use the file name, else use the full
+    // path name.
+    //
+    CmdFilePathName = ShellFindFilePath(CmdFileName);
+    if (CmdFilePathName == NULL) {
+      Status = EFI_NOT_FOUND;
+      goto Done;
+    }
+    DevPath = 
ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CmdFilePathName);
+    Status      = gBS->LoadImage(FALSE, gImageHandle, DevPath, NULL, 0, 
&CmdFileImgHandle);
+    if(EFI_ERROR(Status)) {
+      *HelpText = NULL;
+      goto Done;
+    }
+    Status = gBS->OpenProtocol( CmdFileImgHandle,
+                                &gEfiHiiPackageListProtocolGuid,
+                                (VOID**)&PackageListHeader,
+                                gImageHandle,
+                                NULL,
+                                EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                               );
+    if(EFI_ERROR(Status)) {
       *HelpText = NULL;
+      goto Done;
+    }
+
+    //
+    // If get package list on image handle, find help info in it.
+    //
+    Status = ParseStringPackageList(PackageListHeader, &StrArrBuff, 
&StrArrBuffSize);
+    if (EFI_ERROR(Status)) {
+      goto Done;
+    } else {
+      for ( StrArrWalker = StrArrBuff;
+            (UINTN)(StrArrWalker - StrArrBuff) < 
(StrArrBuffSize)/sizeof(CHAR16);
+            StrArrWalker = StrArrWalker + StrLen(StrArrWalker) + 1
+          ) {
+        //
+        // StrArrWalker is a string to be checked
+        //
+        Status = ManBufferFindTitleSection(&StrArrWalker, Command, BriefDesc, 
&BriefSize);
+        if (!EFI_ERROR(Status) && HelpText != NULL){
+          Status = ManBufferFindSections(StrArrWalker, Sections, HelpText, 
&HelpSize);
+        }
+        if (!EFI_ERROR(Status)){
+          //
+          // Found what we need and return
+          //
+          goto Done;
+        }
+      }
     }
+    *HelpText = NULL;
   }
-  if (TempString != NULL) {
-    FreePool(TempString);
+
+Done:
+  if (CmdFileImgHandle != NULL) {
+    Status = gBS->UnloadImage (CmdFileImgHandle);
   }
+  SHELL_FREE_NON_NULL(StrArrBuff);
+  SHELL_FREE_NON_NULL(TempString);
+  SHELL_FREE_NON_NULL(CmdFileName);
+  SHELL_FREE_NON_NULL(CmdFilePathName);
+  SHELL_FREE_NON_NULL(FileDevPath);
+  SHELL_FREE_NON_NULL(DevPath);
 
   return (Status);
 }
+
-- 
1.9.5.msysgit.1

_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to