/** @file Main file for Help shell level 3 function. Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
Copyright (c) 2014, ARM Limited. All rights reserved.
(C) Copyright 2015 Hewlett-Packard Development Company, L.P.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "UefiShellLevel3CommandsLib.h" #include #include #include /** function to insert string items into a list in the correct alphabetical place the resultant list is a double NULL terminated list of NULL terminated strings. upon successful return the memory must be caller freed (unless passed back in via a loop where it will get reallocated). @param[in,out] DestList double pointer to the list. may be NULL. @param[in,out] DestSize pointer to the size of list. may be 0, if DestList is NULL. @param[in] Item the item to insert. @retval EFI_SUCCESS the operation was successful. **/ EFI_STATUS LexicalInsertIntoList( IN OUT CHAR16 **DestList, IN OUT UINTN *DestSize, IN CONST CHAR16 *Item ) { CHAR16 *NewList; INTN LexicalMatchValue; CHAR16 *LexicalSpot; UINTN SizeOfAddedNameInBytes; // // If there are none, then just return with success // if (Item == NULL || *Item == CHAR_NULL || StrLen(Item)==0) { return (EFI_SUCCESS); } NewList = *DestList; SizeOfAddedNameInBytes = StrSize(Item); NewList = ReallocatePool(*DestSize, (*DestSize) + SizeOfAddedNameInBytes, NewList); (*DestSize) = (*DestSize) + SizeOfAddedNameInBytes; // // Find the correct spot in the list // for (LexicalSpot = NewList ; LexicalSpot != NULL && LexicalSpot < NewList + (*DestSize) ; LexicalSpot += StrLen(LexicalSpot) + 1 ) { // // Get Lexical Comparison Value between PrevCommand and Command list entry // LexicalMatchValue = gUnicodeCollation->StriColl ( gUnicodeCollation, (CHAR16 *)LexicalSpot, (CHAR16 *)Item ); // // The new item goes before this one. // if (LexicalMatchValue > 0 || StrLen(LexicalSpot) == 0) { if (StrLen(LexicalSpot) != 0) { // // Move this and all other items out of the way // CopyMem( LexicalSpot + (SizeOfAddedNameInBytes/sizeof(CHAR16)), LexicalSpot, (*DestSize) - SizeOfAddedNameInBytes - ((LexicalSpot - NewList) * sizeof(CHAR16)) ); } // // Stick this one in place // StrCpyS(LexicalSpot, SizeOfAddedNameInBytes/sizeof(CHAR16), Item); break; } } *DestList = NewList; return (EFI_SUCCESS); } /** function to add each command name from the linked list to the string list. the resultant list is a double NULL terminated list of NULL terminated strings. @param[in,out] DestList double pointer to the list. may be NULL. @param[in,out] DestSize pointer to the size of list. may be 0, if DestList is NULL. @param[in] SourceList the double linked list of commands. @retval EFI_SUCCESS the operation was successful. **/ EFI_STATUS CopyListOfCommandNames( IN OUT CHAR16 **DestList, IN OUT UINTN *DestSize, IN CONST COMMAND_LIST *SourceList ) { CONST COMMAND_LIST *Node; for ( Node = (COMMAND_LIST*)GetFirstNode(&SourceList->Link) ; SourceList != NULL && !IsListEmpty(&SourceList->Link) && !IsNull(&SourceList->Link, &Node->Link) ; Node = (COMMAND_LIST*)GetNextNode(&SourceList->Link, &Node->Link) ) { LexicalInsertIntoList(DestList, DestSize, Node->CommandString); } return (EFI_SUCCESS); } /** function to add each dynamic command name to the string list. the resultant list is a double NULL terminated list of NULL terminated strings. @param[in,out] DestList double pointer to the list. may be NULL. @param[in,out] DestSize pointer to the size of list. may be 0, if DestList is NULL. @retval EFI_SUCCESS the operation was successful. @return an error from HandleProtocol **/ STATIC EFI_STATUS CopyListOfCommandNamesWithDynamic( IN OUT CHAR16** DestList, IN OUT UINTN *DestSize ) { EFI_HANDLE *CommandHandleList; CONST EFI_HANDLE *NextCommand; EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand; EFI_STATUS Status; CommandHandleList = GetHandleListByProtocol(&gEfiShellDynamicCommandProtocolGuid); // // If there are none, then just return with success // if (CommandHandleList == NULL) { return (EFI_SUCCESS); } Status = EFI_SUCCESS; // // Append those to the list. // for (NextCommand = CommandHandleList ; *NextCommand != NULL && !EFI_ERROR(Status) ; NextCommand++) { Status = gBS->HandleProtocol( *NextCommand, &gEfiShellDynamicCommandProtocolGuid, (VOID **)&DynamicCommand ); if (EFI_ERROR(Status)) { continue; } Status = LexicalInsertIntoList(DestList, DestSize, DynamicCommand->CommandName); } SHELL_FREE_NON_NULL(CommandHandleList); return (Status); } /** Attempt to print help from a dynamically added command. @param[in] CommandToGetHelpOn The unicode name of the command that help is requested on. @param[in] SectionToGetHelpOn Pointer to the section specifier(s). @param[in] PrintCommandText Print the command followed by the help content or just help. @retval EFI_SUCCESS The help was displayed @retval EFI_NOT_FOUND The command name could not be found @retval EFI_DEVICE_ERROR The help data format was incorrect. **/ EFI_STATUS PrintDynamicCommandHelp( IN CONST CHAR16 *CommandToGetHelpOn, IN CONST CHAR16 *SectionToGetHelpOn, IN BOOLEAN PrintCommandText ) { EFI_STATUS Status; BOOLEAN Found; EFI_HANDLE *CommandHandleList; EFI_HANDLE *NextCommand; EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand; Status = EFI_NOT_FOUND; Found = FALSE; CommandHandleList = NULL; CommandHandleList = GetHandleListByProtocol(&gEfiShellDynamicCommandProtocolGuid); if (CommandHandleList == NULL) { // // not found or out of resources // return Status; } for (NextCommand = CommandHandleList; *NextCommand != NULL; NextCommand++) { Status = gBS->HandleProtocol( *NextCommand, &gEfiShellDynamicCommandProtocolGuid, (VOID **)&DynamicCommand ); if (EFI_ERROR(Status)) { continue; } // // Check execution break flag when printing multiple command help information. // if (ShellGetExecutionBreakFlag ()) { break; } if ((gUnicodeCollation->MetaiMatch (gUnicodeCollation, (CHAR16 *)DynamicCommand->CommandName, (CHAR16*)CommandToGetHelpOn)) || (gEfiShellProtocol->GetAlias (CommandToGetHelpOn, NULL) != NULL && (gUnicodeCollation->MetaiMatch (gUnicodeCollation, (CHAR16 *)DynamicCommand->CommandName, (CHAR16*)(gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL)))))) { // Print as Shell Help if in ManPage format. Status = ShellPrintHelp (DynamicCommand->CommandName, SectionToGetHelpOn, PrintCommandText); if (Status == EFI_DEVICE_ERROR) { ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_HELP_INV), gShellLevel3HiiHandle, DynamicCommand->CommandName); } else if (EFI_ERROR(Status)) { ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_HELP_NF), gShellLevel3HiiHandle, DynamicCommand->CommandName); } else { Found = TRUE; } } } SHELL_FREE_NON_NULL(CommandHandleList); return (Found ? EFI_SUCCESS : Status); } STATIC CONST SHELL_PARAM_ITEM ParamList[] = { {L"-usage", TypeFlag}, {L"-section", TypeMaxValue}, {L"-verbose", TypeFlag}, {L"-v", TypeFlag}, {NULL, TypeMax} }; /** Function for 'help' command. @param[in] ImageHandle Handle to the Image (NULL if Internal). @param[in] SystemTable Pointer to the System Table (NULL if Internal). **/ SHELL_STATUS EFIAPI ShellCommandRunHelp ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; LIST_ENTRY *Package; CHAR16 *ProblemParam; SHELL_STATUS ShellStatus; CHAR16 *SortedCommandList; CONST CHAR16 *CurrentCommand; CHAR16 *CommandToGetHelpOn; CHAR16 *SectionToGetHelpOn; CHAR16 *HiiString; BOOLEAN Found; BOOLEAN PrintCommandText; UINTN SortedCommandListSize; PrintCommandText = TRUE; ProblemParam = NULL; ShellStatus = SHELL_SUCCESS; CommandToGetHelpOn = NULL; SectionToGetHelpOn = NULL; SortedCommandList = NULL; Found = FALSE; // // initialize the shell lib (we must be in non-auto-init...) // Status = ShellInitialize(); ASSERT_EFI_ERROR(Status); Status = CommandInit(); ASSERT_EFI_ERROR(Status); // // parse the command line // Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE); if (EFI_ERROR(Status)) { if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) { ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel3HiiHandle, L"help", ProblemParam); FreePool(ProblemParam); ShellStatus = SHELL_INVALID_PARAMETER; } else { ASSERT(FALSE); } } else { // // Check for conflicting parameters. // if (ShellCommandLineGetFlag(Package, L"-usage") &&ShellCommandLineGetFlag(Package, L"-section") &&(ShellCommandLineGetFlag(Package, L"-verbose") || ShellCommandLineGetFlag(Package, L"-v")) ){ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_CON), gShellLevel3HiiHandle, L"help"); ShellStatus = SHELL_INVALID_PARAMETER; } else if (ShellCommandLineGetRawValue(Package, 2) != NULL) { ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel3HiiHandle, L"help"); ShellStatus = SHELL_INVALID_PARAMETER; } else { // // Get the command name we are getting help on // ASSERT(CommandToGetHelpOn == NULL); StrnCatGrow(&CommandToGetHelpOn, NULL, ShellCommandLineGetRawValue(Package, 1), 0); if (CommandToGetHelpOn == NULL && ShellCommandLineGetFlag(Package, L"-?")) { // // If we dont have a command and we got a simple -? // we are looking for help on help command. // StrnCatGrow(&CommandToGetHelpOn, NULL, L"help", 0); } if (CommandToGetHelpOn == NULL) { StrnCatGrow(&CommandToGetHelpOn, NULL, L"*", 0); ASSERT(SectionToGetHelpOn == NULL); StrnCatGrow(&SectionToGetHelpOn, NULL, L"NAME", 0); } else { PrintCommandText = FALSE; ASSERT(SectionToGetHelpOn == NULL); // // Get the section name for the given command name // if (ShellCommandLineGetFlag(Package, L"-section")) { StrnCatGrow(&SectionToGetHelpOn, NULL, ShellCommandLineGetValue(Package, L"-section"), 0); } else if (ShellCommandLineGetFlag(Package, L"-usage")) { StrnCatGrow(&SectionToGetHelpOn, NULL, L"NAME,SYNOPSIS", 0); } else if (ShellCommandLineGetFlag(Package, L"-verbose") || ShellCommandLineGetFlag(Package, L"-v")) { } else { // // The output of help will display NAME, SYNOPSIS, OPTIONS, DESCRIPTION, and EXAMPLES sections. // StrnCatGrow (&SectionToGetHelpOn, NULL, L"NAME,SYNOPSIS,OPTIONS,DESCRIPTION,EXAMPLES", 0); } } if (gUnicodeCollation->StriColl(gUnicodeCollation, CommandToGetHelpOn, L"special") == 0) { // // we need info on the special characters // ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_SC_HEADER), gShellLevel3HiiHandle); HiiString = HiiGetString(gShellLevel3HiiHandle, STRING_TOKEN(STR_HELP_SC_DATA), NULL); ShellPrintEx(-1, -1, L"%s", HiiString); FreePool(HiiString); Found = TRUE; } else { SortedCommandList = NULL; SortedCommandListSize = 0; CopyListOfCommandNames(&SortedCommandList, &SortedCommandListSize, ShellCommandGetCommandList(TRUE)); CopyListOfCommandNamesWithDynamic(&SortedCommandList, &SortedCommandListSize); for (CurrentCommand = SortedCommandList ; CurrentCommand != NULL && CurrentCommand < SortedCommandList + SortedCommandListSize/sizeof(CHAR16) && *CurrentCommand != CHAR_NULL ; CurrentCommand += StrLen(CurrentCommand) + 1 ) { // // Checking execution break flag when print multiple command help information. // if (ShellGetExecutionBreakFlag ()) { break; } if ((gUnicodeCollation->MetaiMatch(gUnicodeCollation, (CHAR16*)CurrentCommand, CommandToGetHelpOn)) || (gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL) != NULL && (gUnicodeCollation->MetaiMatch(gUnicodeCollation, (CHAR16*)CurrentCommand, (CHAR16*)(gEfiShellProtocol->GetAlias(CommandToGetHelpOn, NULL)))))) { // // We have a command to look for help on. // Status = ShellPrintHelp(CurrentCommand, SectionToGetHelpOn, PrintCommandText); if (EFI_ERROR(Status)) { // // now try to match against the dynamic command list and print help // Status = PrintDynamicCommandHelp (CurrentCommand, SectionToGetHelpOn, PrintCommandText); } if (Status == EFI_DEVICE_ERROR) { ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_INV), gShellLevel3HiiHandle, CurrentCommand); } else if (EFI_ERROR(Status)) { ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_NF), gShellLevel3HiiHandle, CurrentCommand); } else { Found = TRUE; } } } // // Search the .man file for Shell applications (Shell external commands). // if (!Found) { Status = ShellPrintHelp(CommandToGetHelpOn, SectionToGetHelpOn, FALSE); if (Status == EFI_DEVICE_ERROR) { ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_INV), gShellLevel3HiiHandle, CommandToGetHelpOn); } else if (EFI_ERROR(Status)) { ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_NF), gShellLevel3HiiHandle, CommandToGetHelpOn); } else { Found = TRUE; } } } if (!Found) { ShellStatus = SHELL_NOT_FOUND; } // // free the command line package // ShellCommandLineFreeVarList (Package); } } if (CommandToGetHelpOn != NULL && StrCmp(CommandToGetHelpOn, L"*") == 0){ // // If '*' then the command entered was 'Help' without qualifiers, This footer // provides additional info on help switches // ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_HELP_FOOTER), gShellLevel3HiiHandle); } if (CommandToGetHelpOn != NULL) { FreePool(CommandToGetHelpOn); } if (SectionToGetHelpOn != NULL) { FreePool(SectionToGetHelpOn); } SHELL_FREE_NON_NULL(SortedCommandList); return (ShellStatus); }