Home | History | Annotate | Download | only in UefiShellLevel3CommandsLib
      1 /** @file
      2   Main file for Help shell level 3 function.
      3 
      4   Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved. <BR>
      5   Copyright (c) 2014, ARM Limited. All rights reserved. <BR>
      6   (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
      7 
      8   This program and the accompanying materials
      9   are licensed and made available under the terms and conditions of the BSD License
     10   which accompanies this distribution.  The full text of the license may be found at
     11   http://opensource.org/licenses/bsd-license.php
     12 
     13   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     14   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     15 
     16 **/
     17 
     18 #include "UefiShellLevel3CommandsLib.h"
     19 
     20 #include <Library/ShellLib.h>
     21 #include <Library/HandleParsingLib.h>
     22 
     23 #include <Protocol/ShellDynamicCommand.h>
     24 
     25 /**
     26    function to insert string items into a list in the correct alphabetical place
     27 
     28    the resultant list is a double NULL terminated list of NULL terminated strings.
     29 
     30    upon successful return the memory must be caller freed (unless passed back in
     31    via a loop where it will get reallocated).
     32 
     33    @param[in,out] DestList    double pointer to the list. may be NULL.
     34    @param[in,out] DestSize    pointer to the size of list. may be 0, if DestList is NULL.
     35    @param[in]     Item        the item to insert.
     36 
     37    @retval EFI_SUCCESS        the operation was successful.
     38 **/
     39 EFI_STATUS
     40 LexicalInsertIntoList(
     41   IN OUT   CHAR16 **DestList,
     42   IN OUT   UINTN  *DestSize,
     43   IN CONST CHAR16 *Item
     44   )
     45 {
     46   CHAR16                              *NewList;
     47   INTN                                LexicalMatchValue;
     48   CHAR16                              *LexicalSpot;
     49   UINTN                               SizeOfAddedNameInBytes;
     50 
     51   //
     52   // If there are none, then just return with success
     53   //
     54   if (Item == NULL || *Item == CHAR_NULL || StrLen(Item)==0) {
     55     return (EFI_SUCCESS);
     56   }
     57 
     58   NewList = *DestList;
     59 
     60   SizeOfAddedNameInBytes = StrSize(Item);
     61   NewList = ReallocatePool(*DestSize, (*DestSize) + SizeOfAddedNameInBytes, NewList);
     62   (*DestSize) = (*DestSize) + SizeOfAddedNameInBytes;
     63 
     64   //
     65   // Find the correct spot in the list
     66   //
     67   for (LexicalSpot = NewList
     68     ; LexicalSpot != NULL && LexicalSpot < NewList + (*DestSize)
     69     ; LexicalSpot += StrLen(LexicalSpot) + 1
     70     ) {
     71     //
     72     // Get Lexical Comparison Value between PrevCommand and Command list entry
     73     //
     74     LexicalMatchValue = gUnicodeCollation->StriColl (
     75                                               gUnicodeCollation,
     76                                               (CHAR16 *)LexicalSpot,
     77                                               (CHAR16 *)Item
     78                                               );
     79     //
     80     // The new item goes before this one.
     81     //
     82     if (LexicalMatchValue > 0 || StrLen(LexicalSpot) == 0) {
     83       if (StrLen(LexicalSpot) != 0) {
     84         //
     85         // Move this and all other items out of the way
     86         //
     87         CopyMem(
     88           LexicalSpot + (SizeOfAddedNameInBytes/sizeof(CHAR16)),
     89           LexicalSpot,
     90           (*DestSize) - SizeOfAddedNameInBytes - ((LexicalSpot - NewList) * sizeof(CHAR16))
     91           );
     92       }
     93 
     94       //
     95       // Stick this one in place
     96       //
     97       StrCpyS(LexicalSpot, SizeOfAddedNameInBytes/sizeof(CHAR16), Item);
     98       break;
     99     }
    100   }
    101 
    102   *DestList = NewList;
    103   return (EFI_SUCCESS);
    104 }
    105 
    106 /**
    107    function to add each command name from the linked list to the string list.
    108 
    109    the resultant list is a double NULL terminated list of NULL terminated strings.
    110 
    111    @param[in,out] DestList    double pointer to the list. may be NULL.
    112    @param[in,out] DestSize    pointer to the size of list. may be 0, if DestList is NULL.
    113    @param[in]     SourceList  the double linked list of commands.
    114 
    115    @retval EFI_SUCCESS        the operation was successful.
    116 **/
    117 EFI_STATUS
    118 CopyListOfCommandNames(
    119   IN OUT   CHAR16       **DestList,
    120   IN OUT   UINTN        *DestSize,
    121   IN CONST COMMAND_LIST *SourceList
    122   )
    123 {
    124   CONST COMMAND_LIST  *Node;
    125 
    126   for ( Node = (COMMAND_LIST*)GetFirstNode(&SourceList->Link)
    127       ; SourceList != NULL && !IsListEmpty(&SourceList->Link) && !IsNull(&SourceList->Link, &Node->Link)
    128       ; Node = (COMMAND_LIST*)GetNextNode(&SourceList->Link, &Node->Link)
    129     ) {
    130     LexicalInsertIntoList(DestList, DestSize, Node->CommandString);
    131   }
    132   return (EFI_SUCCESS);
    133 }
    134 
    135 /**
    136    function to add each dynamic command name to the string list.
    137 
    138    the resultant list is a double NULL terminated list of NULL terminated strings.
    139 
    140    @param[in,out] DestList    double pointer to the list. may be NULL.
    141    @param[in,out] DestSize    pointer to the size of list. may be 0, if DestList is NULL.
    142 
    143    @retval EFI_SUCCESS        the operation was successful.
    144    @return an error from HandleProtocol
    145 **/
    146 STATIC
    147 EFI_STATUS
    148 CopyListOfCommandNamesWithDynamic(
    149   IN OUT  CHAR16** DestList,
    150   IN OUT  UINTN    *DestSize
    151   )
    152 {
    153   EFI_HANDLE                          *CommandHandleList;
    154   CONST EFI_HANDLE                    *NextCommand;
    155   EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;
    156   EFI_STATUS                          Status;
    157 
    158   CommandHandleList = GetHandleListByProtocol(&gEfiShellDynamicCommandProtocolGuid);
    159 
    160   //
    161   // If there are none, then just return with success
    162   //
    163   if (CommandHandleList == NULL) {
    164     return (EFI_SUCCESS);
    165   }
    166 
    167   Status = EFI_SUCCESS;
    168 
    169   //
    170   // Append those to the list.
    171   //
    172   for (NextCommand = CommandHandleList ; *NextCommand != NULL && !EFI_ERROR(Status) ; NextCommand++) {
    173     Status = gBS->HandleProtocol(
    174       *NextCommand,
    175       &gEfiShellDynamicCommandProtocolGuid,
    176       (VOID **)&DynamicCommand
    177       );
    178 
    179     if (EFI_ERROR(Status)) {
    180       continue;
    181     }
    182 
    183     Status = LexicalInsertIntoList(DestList, DestSize, DynamicCommand->CommandName);
    184   }
    185 
    186   SHELL_FREE_NON_NULL(CommandHandleList);
    187   return (Status);
    188 }
    189 
    190 
    191 /**
    192   Attempt to print help from a dynamically added command.
    193 
    194   @param[in]  CommandToGetHelpOn  The unicode name of the command that help is
    195                                   requested on.
    196   @param[in]  SectionToGetHelpOn  Pointer to the section specifier(s).
    197   @param[in]  PrintCommandText    Print the command followed by the help content
    198                                   or just help.
    199 
    200   @retval EFI_SUCCESS             The help was displayed
    201   @retval EFI_NOT_FOUND           The command name could not be found
    202   @retval EFI_DEVICE_ERROR        The help data format was incorrect.
    203 **/
    204 EFI_STATUS
    205 PrintDynamicCommandHelp(
    206   IN CONST CHAR16  *CommandToGetHelpOn,
    207   IN CONST CHAR16  *SectionToGetHelpOn,
    208   IN BOOLEAN       PrintCommandText
    209  )
    210 {
    211   EFI_STATUS                          Status;
    212   BOOLEAN                             Found;
    213   EFI_HANDLE                          *CommandHandleList;
    214   EFI_HANDLE                          *NextCommand;
    215   EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;
    216 
    217   Status = EFI_NOT_FOUND;
    218   Found = FALSE;
    219   CommandHandleList = NULL;
    220 
    221   CommandHandleList = GetHandleListByProtocol(&gEfiShellDynamicCommandProtocolGuid);
    222 
    223   if (CommandHandleList == NULL) {
    224     //
    225     // not found or out of resources
    226     //
    227     return Status;
    228   }
    229 
    230   for (NextCommand = CommandHandleList; *NextCommand != NULL; NextCommand++) {
    231     Status = gBS->HandleProtocol(
    232       *NextCommand,
    233       &gEfiShellDynamicCommandProtocolGuid,
    234       (VOID **)&DynamicCommand
    235       );
    236 
    237     if (EFI_ERROR(Status)) {
    238       continue;
    239     }
    240 
    241     //
    242     // Check execution break flag when printing multiple command help information.
    243     //
    244     if (ShellGetExecutionBreakFlag ()) {
    245       break;
    246     }
    247 
    248     if ((gUnicodeCollation->MetaiMatch (gUnicodeCollation, (CHAR16 *)DynamicCommand->CommandName, (CHAR16*)CommandToGetHelpOn)) ||
    249       (gEfiShellProtocol->GetAlias (CommandToGetHelpOn, NULL) != NULL && (gUnicodeCollation->MetaiMatch (gUnicodeCollation, (CHAR16 *)DynamicCommand->CommandName, (CHAR16*)(gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL)))))) {
    250       // Print as Shell Help if in ManPage format.
    251       Status = ShellPrintHelp (DynamicCommand->CommandName, SectionToGetHelpOn,
    252                               PrintCommandText);
    253       if (Status == EFI_DEVICE_ERROR) {
    254         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_HELP_INV),
    255                         gShellLevel3HiiHandle, DynamicCommand->CommandName);
    256       } else if (EFI_ERROR(Status)) {
    257         ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_HELP_NF),
    258                         gShellLevel3HiiHandle, DynamicCommand->CommandName);
    259       } else {
    260         Found = TRUE;
    261       }
    262     }
    263   }
    264 
    265   SHELL_FREE_NON_NULL(CommandHandleList);
    266 
    267   return (Found ? EFI_SUCCESS : Status);
    268 
    269 }
    270 
    271 STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
    272   {L"-usage", TypeFlag},
    273   {L"-section", TypeMaxValue},
    274   {L"-verbose", TypeFlag},
    275   {L"-v", TypeFlag},
    276   {NULL, TypeMax}
    277   };
    278 
    279 /**
    280   Function for 'help' command.
    281 
    282   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
    283   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
    284 **/
    285 SHELL_STATUS
    286 EFIAPI
    287 ShellCommandRunHelp (
    288   IN EFI_HANDLE        ImageHandle,
    289   IN EFI_SYSTEM_TABLE  *SystemTable
    290   )
    291 {
    292   EFI_STATUS          Status;
    293   LIST_ENTRY          *Package;
    294   CHAR16              *ProblemParam;
    295   SHELL_STATUS        ShellStatus;
    296   CHAR16              *SortedCommandList;
    297   CONST CHAR16        *CurrentCommand;
    298   CHAR16              *CommandToGetHelpOn;
    299   CHAR16              *SectionToGetHelpOn;
    300   CHAR16              *HiiString;
    301   BOOLEAN             Found;
    302   BOOLEAN             PrintCommandText;
    303   UINTN               SortedCommandListSize;
    304 
    305   PrintCommandText    = TRUE;
    306   ProblemParam        = NULL;
    307   ShellStatus         = SHELL_SUCCESS;
    308   CommandToGetHelpOn  = NULL;
    309   SectionToGetHelpOn  = NULL;
    310   SortedCommandList   = NULL;
    311   Found               = FALSE;
    312 
    313   //
    314   // initialize the shell lib (we must be in non-auto-init...)
    315   //
    316   Status = ShellInitialize();
    317   ASSERT_EFI_ERROR(Status);
    318 
    319   Status = CommandInit();
    320   ASSERT_EFI_ERROR(Status);
    321 
    322   //
    323   // parse the command line
    324   //
    325   Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
    326   if (EFI_ERROR(Status)) {
    327     if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
    328       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel3HiiHandle, L"help", ProblemParam);
    329       FreePool(ProblemParam);
    330       ShellStatus = SHELL_INVALID_PARAMETER;
    331     } else {
    332       ASSERT(FALSE);
    333     }
    334   } else {
    335     //
    336     // Check for conflicting parameters.
    337     //
    338     if (ShellCommandLineGetFlag(Package, L"-usage")
    339       &&ShellCommandLineGetFlag(Package, L"-section")
    340       &&(ShellCommandLineGetFlag(Package, L"-verbose") || ShellCommandLineGetFlag(Package, L"-v"))
    341      ){
    342       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_CON), gShellLevel3HiiHandle, L"help");
    343       ShellStatus = SHELL_INVALID_PARAMETER;
    344     } else if (ShellCommandLineGetRawValue(Package, 2) != NULL) {
    345       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel3HiiHandle, L"help");
    346       ShellStatus = SHELL_INVALID_PARAMETER;
    347     } else {
    348       //
    349       // Get the command name we are getting help on
    350       //
    351       ASSERT(CommandToGetHelpOn == NULL);
    352       StrnCatGrow(&CommandToGetHelpOn, NULL, ShellCommandLineGetRawValue(Package, 1), 0);
    353       if (CommandToGetHelpOn == NULL && ShellCommandLineGetFlag(Package, L"-?")) {
    354         //
    355         // If we dont have a command and we got a simple -?
    356         // we are looking for help on help command.
    357         //
    358         StrnCatGrow(&CommandToGetHelpOn, NULL, L"help", 0);
    359       }
    360 
    361       if (CommandToGetHelpOn == NULL) {
    362         StrnCatGrow(&CommandToGetHelpOn, NULL, L"*", 0);
    363         ASSERT(SectionToGetHelpOn == NULL);
    364         StrnCatGrow(&SectionToGetHelpOn, NULL, L"NAME", 0);
    365       } else {
    366         PrintCommandText = FALSE;
    367         ASSERT(SectionToGetHelpOn == NULL);
    368         //
    369         // Get the section name for the given command name
    370         //
    371         if (ShellCommandLineGetFlag(Package, L"-section")) {
    372           StrnCatGrow(&SectionToGetHelpOn, NULL, ShellCommandLineGetValue(Package, L"-section"), 0);
    373         } else if (ShellCommandLineGetFlag(Package, L"-usage")) {
    374           StrnCatGrow(&SectionToGetHelpOn, NULL, L"NAME,SYNOPSIS", 0);
    375         } else if (ShellCommandLineGetFlag(Package, L"-verbose") || ShellCommandLineGetFlag(Package, L"-v")) {
    376         } else {
    377           //
    378           // The output of help <command> will display NAME, SYNOPSIS, OPTIONS, DESCRIPTION, and EXAMPLES sections.
    379           //
    380           StrnCatGrow (&SectionToGetHelpOn, NULL, L"NAME,SYNOPSIS,OPTIONS,DESCRIPTION,EXAMPLES", 0);
    381         }
    382       }
    383 
    384       if (gUnicodeCollation->StriColl(gUnicodeCollation, CommandToGetHelpOn, L"special") == 0) {
    385         //
    386         // we need info on the special characters
    387         //
    388         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_SC_HEADER), gShellLevel3HiiHandle);
    389         HiiString = HiiGetString(gShellLevel3HiiHandle, STRING_TOKEN(STR_HELP_SC_DATA), NULL);
    390         ShellPrintEx(-1, -1, L"%s", HiiString);
    391         FreePool(HiiString);
    392         Found = TRUE;
    393       } else {
    394         SortedCommandList = NULL;
    395         SortedCommandListSize = 0;
    396         CopyListOfCommandNames(&SortedCommandList, &SortedCommandListSize, ShellCommandGetCommandList(TRUE));
    397         CopyListOfCommandNamesWithDynamic(&SortedCommandList, &SortedCommandListSize);
    398 
    399         for (CurrentCommand = SortedCommandList
    400           ; CurrentCommand != NULL && *CurrentCommand != CHAR_NULL && CurrentCommand < SortedCommandList + SortedCommandListSize/sizeof(CHAR16)
    401           ; CurrentCommand += StrLen(CurrentCommand) + 1
    402           ) {
    403           //
    404           // Checking execution break flag when print multiple command help information.
    405           //
    406           if (ShellGetExecutionBreakFlag ()) {
    407             break;
    408           }
    409 
    410           if ((gUnicodeCollation->MetaiMatch(gUnicodeCollation, (CHAR16*)CurrentCommand, CommandToGetHelpOn)) ||
    411              (gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL) != NULL && (gUnicodeCollation->MetaiMatch(gUnicodeCollation, (CHAR16*)CurrentCommand, (CHAR16*)(gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL)))))) {
    412             //
    413             // We have a command to look for help on.
    414             //
    415             Status = ShellPrintHelp(CurrentCommand, SectionToGetHelpOn, PrintCommandText);
    416             if (EFI_ERROR(Status)) {
    417               //
    418               // now try to match against the dynamic command list and print help
    419               //
    420               Status = PrintDynamicCommandHelp (CurrentCommand, SectionToGetHelpOn, PrintCommandText);
    421             }
    422             if (Status == EFI_DEVICE_ERROR) {
    423                 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_INV), gShellLevel3HiiHandle, CurrentCommand);
    424             } else if (EFI_ERROR(Status)) {
    425                 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_NF), gShellLevel3HiiHandle, CurrentCommand);
    426             } else {
    427                 Found = TRUE;
    428             }
    429           }
    430         }
    431 
    432         //
    433         // Search the .man file for Shell applications (Shell external commands).
    434         //
    435         if (!Found) {
    436           Status = ShellPrintHelp(CommandToGetHelpOn, SectionToGetHelpOn, FALSE);
    437           if (Status == EFI_DEVICE_ERROR) {
    438               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_INV), gShellLevel3HiiHandle, CommandToGetHelpOn);
    439           } else if (EFI_ERROR(Status)) {
    440               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_NF), gShellLevel3HiiHandle, CommandToGetHelpOn);
    441           } else {
    442             Found = TRUE;
    443           }
    444         }
    445       }
    446 
    447       if (!Found) {
    448         ShellStatus = SHELL_NOT_FOUND;
    449       }
    450 
    451       //
    452       // free the command line package
    453       //
    454       ShellCommandLineFreeVarList (Package);
    455     }
    456   }
    457 
    458   if (CommandToGetHelpOn != NULL && StrCmp(CommandToGetHelpOn, L"*") == 0){
    459     //
    460     // If '*' then the command entered was 'Help' without qualifiers, This footer
    461     // provides additional info on help switches
    462     //
    463     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_FOOTER), gShellLevel3HiiHandle);
    464   }
    465   if (CommandToGetHelpOn != NULL) {
    466     FreePool(CommandToGetHelpOn);
    467   }
    468   if (SectionToGetHelpOn != NULL) {
    469     FreePool(SectionToGetHelpOn);
    470   }
    471   SHELL_FREE_NON_NULL(SortedCommandList);
    472 
    473   return (ShellStatus);
    474 }
    475