Looks good to me.

Shumin, 
If it looks good to you, can you also commit?

Reviewed-by: Jaben Carsey <jaben.car...@intel.com>

> -----Original Message-----
> From: edk2-devel [mailto:edk2-devel-boun...@lists.01.org] On Behalf Of
> jim_dai...@dell.com
> Sent: Monday, December 14, 2015 11:44 AM
> To: edk2-devel@lists.01.org
> Cc: Carsey, Jaben <jaben.car...@intel.com>
> Subject: [edk2] [PATCH] ShellPkg Ease MAN file Title Header syntax
> requirements
> Importance: High
> 
> ShellPkg: Ease the shell's MAN file Title Header syntax requirements.
> 
> Prior to this change, the shell would not use a MAN file if the
> Title Header line was not strictly formatted.  For example, if the
> case of the command name in the file was not exactly the same as the
> case of the command name as typed by the user, the MAN file would
> not be used.  Also, extra whitespace on the line would also cause the
> shell to ignore the MAN file.  This change allows "extra" white space
> and ignores case when looking for the command name.  It also ignores
> any path information for cases where the user enters a relative or
> absolute path to the EFI file.
> 
> Contributed-under: TianoCore Contribution Agreement 1.0
> Signed-off-by: Jim Dailey <jim_dai...@dell.com>
> 
> diff --git a/trunk/edk2/ShellPkg/Application/Shell/ShellManParser.c
> b/trunk/edk2/ShellPkg/Application/Shell/ShellManParser.c
> --- a/trunk/edk2/ShellPkg/Application/Shell/ShellManParser.c  (revision
> 19175)
> +++ b/trunk/edk2/ShellPkg/Application/Shell/ShellManParser.c
>       (working copy)
> @@ -2,6 +2,7 @@
>    Provides interface to shell MAN file parser.
> 
>    Copyright (c) 2009 - 2015, 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
>    which accompanies this distribution.  The full text of the license may be
> found at
> @@ -14,6 +15,8 @@
> 
>  #include "Shell.h"
> 
> +CHAR16 EFIAPI InternalShellCharToUpper (IN CHAR16  Char);
> +
>  /**
>    Verifies that the filename has .MAN on the end.
> 
> @@ -445,14 +448,141 @@
>  }
> 
>  /**
> +  Parses a line from a MAN file to see if it is the Title Header. If it is, 
> then
> +  if the "Brief Description" is desired, allocate a buffer for it and return 
> a
> +  copy. Upon a sucessful return the caller is responsible to free the memory
> in
> +  *BriefDesc
> +
> +  Uses a simple state machine that allows "unlimited" whitespace before and
> after the
> +  ".TH", compares Command and the MAN file commnd name without respect
> to case, and
> +  allows "unlimited" whitespace and '0' and '1' characters before the Short
> Description.
> +  The PCRE regex describing this functionality is:
> ^\s*\.TH\s+(\S)\s[\s01]*(.*)$
> +  where group 1 is the Command Name and group 2 is the Short Description.
> +
> +  @param[in] Command             name of command whose MAN file we think
> Line came from
> +  @param[in] Line                Pointer to a line from the MAN file
> +  @param[out] BriefDesc          pointer to pointer to string where 
> description
> goes.
> +  @param[out] BriefSize          pointer to size of allocated BriefDesc
> +  @param[out] Found              TRUE if the Title Header was found and it
> belongs to Command
> +
> +  @retval TRUE   Line contained the Title Header
> +  @retval FALSE  Line did not contain the Title Header
> +**/
> +BOOLEAN
> +IsTitleHeader(
> +  IN CONST CHAR16       *Command,
> +  IN CHAR16             *Line,
> +  OUT CHAR16            **BriefDesc OPTIONAL,
> +  OUT UINTN             *BriefSize OPTIONAL,
> +  OUT BOOLEAN           *Found
> +  )
> +{
> +  // The states of a simple state machine used to recognize a title header 
> line
> +  // and to extract the Short Description, if desired.
> +  typedef enum {
> +    LookForThMacro, LookForCommandName, CompareCommands,
> GetBriefDescription, Final
> +  } STATEVALUES;
> +
> +  STATEVALUES  State;
> +  UINTN        CommandIndex; // Indexes Command as we compare its chars to
> the MAN file.
> +  BOOLEAN      ReturnValue;  // TRUE if this the Title Header line of *some*
> MAN file.
> +  BOOLEAN      ReturnFound;  // TRUE if this the Title Header line of *the
> desired* MAN file.
> +
> +  ReturnValue = FALSE;
> +  ReturnFound = FALSE;
> +  CommandIndex = 0;
> +  State = LookForThMacro;
> +
> +  do {
> +
> +    if (*Line == L'\0') {
> +      break;
> +    }
> +
> +    switch (State) {
> +
> +      // Handle "^\s*.TH\s"
> +      // Go to state LookForCommandName if the title header macro is
> present; otherwise,
> +      // eat white space. If we see something other than white space, this 
> is not
> a
> +      // title header line.
> +      case LookForThMacro:
> +        if (StrnCmp (L".TH ", Line, 4) == 0 || StrnCmp (L".TH\t", Line, 4) 
> == 0) {
> +          Line += 4;
> +          State = LookForCommandName;
> +        }
> +        else if (*Line == L' ' || *Line == L'\t') {
> +          Line++;
> +        }
> +        else {
> +          State = Final;
> +        }
> +      break;
> +
> +      // Handle "\s*"
> +      // Eat any "extra" whitespace after the title header macro (we have
> already seen
> +      // at least one white space character). Go to state CompareCommands
> when a
> +      // non-white space is seen.
> +      case LookForCommandName:
> +        if (*Line == L' ' || *Line == L'\t') {
> +          Line++;
> +        }
> +        else {
> +          ReturnValue = TRUE;  // This is *some* command's title header line.
> +          State = CompareCommands;
> +          // Do not increment Line; it points to the first character of the
> command
> +          // name on the title header line.
> +        }
> +      break;
> +
> +      // Handle "(\S)\s"
> +      // Compare Command to the title header command name, ignoring case.
> When we
> +      // reach the end of the command (i.e. we see white space), the next 
> state
> +      // depends on whether the caller wants a copy of the Brief Description.
> +      case CompareCommands:
> +        if (*Line == L' ' || *Line == L'\t') {
> +          ReturnFound = TRUE;  // This is the desired command's title header
> line.
> +          State = (BriefDesc == NULL) ? Final : GetBriefDescription;
> +        }
> +        else if (InternalShellCharToUpper (*Line) != InternalShellCharToUpper
> (*(Command + CommandIndex++))) {
> +          State = Final;
> +        }
> +        Line++;
> +      break;
> +
> +      // Handle "[\s01]*(.*)$"
> +      // Skip whitespace, '0', and '1' characters, if any, prior to the brief
> description.
> +      // Return the description to the caller.
> +      case GetBriefDescription:
> +        if (*Line != L' ' && *Line != L'\t' && *Line != L'0' && *Line != 
> L'1') {
> +          *BriefSize = StrSize(Line);
> +          *BriefDesc = AllocateZeroPool(*BriefSize);
> +          if (*BriefDesc != NULL) {
> +            StrCpyS(*BriefDesc, (*BriefSize)/sizeof(CHAR16), Line);
> +          }
> +          State = Final;
> +        }
> +        Line++;
> +      break;
> +
> +    }
> +
> +  } while (State < Final);
> +
> +  *Found = ReturnFound;
> +  return ReturnValue;
> +}
> +
> +/**
>    parses through the MAN file specified by SHELL_FILE_HANDLE and returns
> the
> -  "Brief Description" for the .TH section as specified by Command.  if the
> +  "Brief Description" for the .TH section as specified by Command.  If the
>    command section is not found return EFI_NOT_FOUND.
> 
>    Upon a sucessful return the caller is responsible to free the memory in
> *BriefDesc
> 
>    @param[in] Handle              FileHandle to read from
> -  @param[in] Command             name of command's section to find
> +  @param[in] Command             name of command's section to find as
> entered on the
> +                                 command line (may be a relative or absolute 
> path or
> +                                 be in any case: upper, lower, or mixed in 
> numerous
> ways!).
>    @param[out] BriefDesc          pointer to pointer to string where 
> description
> goes.
>    @param[out] BriefSize          pointer to size of allocated BriefDesc
>    @param[in, out] Ascii          TRUE if the file is ASCII, FALSE otherwise, 
> will be
> @@ -459,8 +589,8 @@
>                                   set if the file handle is at the 0 position.
> 
>    @retval EFI_OUT_OF_RESOURCES  a memory allocation failed.
> -  @retval EFI_SUCCESS           the section was found and its description 
> sotred
> in
> -                                an alloceted buffer.
> +  @retval EFI_SUCCESS           the section was found and its description 
> stored
> in
> +                                an allocated buffer if requested.
>  **/
>  EFI_STATUS
>  EFIAPI
> @@ -473,13 +603,10 @@
>    )
>  {
>    EFI_STATUS  Status;
> -  CHAR16      *TitleString;
>    CHAR16      *ReadLine;
>    UINTN       Size;
> -  CHAR16      *TitleEnd;
> -  UINTN       TitleLen;
>    BOOLEAN     Found;
> -  UINTN       TitleSize;
> +  UINTN       Start;
> 
>    if ( Handle     == NULL
>      || Command    == NULL
> @@ -497,59 +624,34 @@
>      return (EFI_OUT_OF_RESOURCES);
>    }
> 
> -  TitleSize = (4*sizeof(CHAR16)) + StrSize(Command);
> -  TitleString = AllocateZeroPool(TitleSize);
> -  if (TitleString == NULL) {
> -    FreePool(ReadLine);
> -    return (EFI_OUT_OF_RESOURCES);
> +  //
> +  // Do not pass any leading path information that may be present to
> IsTitleHeader().
> +  //
> +  Start = StrLen(Command);
> +  while (Start
> +         && (*(Command + Start - 1) != L'\\')
> +         && (*(Command + Start - 1) != L'/')
> +         && (*(Command + Start - 1) != L':')) {
> +    --Start;
>    }
> -  StrCpyS(TitleString, TitleSize/sizeof(CHAR16), L".TH ");
> -  StrCatS(TitleString, TitleSize/sizeof(CHAR16), Command);
> 
> -  TitleLen = StrLen(TitleString);
>    for (;!ShellFileHandleEof(Handle);Size = 1024) {
>     Status = ShellFileHandleReadLine(Handle, ReadLine, &Size, TRUE, Ascii);
> -    if (ReadLine[0] == L'#') {
> -      //
> -      // Skip comment lines
> -      //
> -      continue;
> -    }
>      //
>      // ignore too small of buffer...
>      //
> -    if (Status == EFI_BUFFER_TOO_SMALL) {
> -      Status = EFI_SUCCESS;
> -    }
> -    if (EFI_ERROR(Status)) {
> +    if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) {
>        break;
>      }
> -    if (StrnCmp(ReadLine, TitleString, TitleLen) == 0) {
> -      Found = TRUE;
> -      //
> -      // we found it so copy out the rest of the line into BriefDesc
> -      // After skipping any spaces or zeroes
> -      //
> -      for ( TitleEnd = ReadLine+TitleLen
> -          ; *TitleEnd == L' ' || *TitleEnd == L'0' || *TitleEnd == L'1'
> -          ; TitleEnd++);
> -      if (BriefDesc != NULL) {
> -        *BriefSize = StrSize(TitleEnd);
> -        *BriefDesc = AllocateZeroPool(*BriefSize);
> -        if (*BriefDesc == NULL) {
> -          Status = EFI_OUT_OF_RESOURCES;
> -          break;
> -        }
> -        StrCpyS(*BriefDesc, (*BriefSize)/sizeof(CHAR16), TitleEnd);
> -      }
> +
> +    Status = EFI_NOT_FOUND;
> +    if (IsTitleHeader (Command+Start, ReadLine, BriefDesc, BriefSize, 
> &Found))
> {
> +      Status = Found ? EFI_SUCCESS : EFI_NOT_FOUND;
>        break;
>      }
>    }
> +
>    FreePool(ReadLine);
> -  FreePool(TitleString);
> -  if (!Found && !EFI_ERROR(Status)) {
> -    return (EFI_NOT_FOUND);
> -  }
>    return (Status);
>  }
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to