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