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