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