UefiShellCommandLib.c revision d51088b764304f920a41a186bef45ca789e080dc
1/** @file 2 Provides interface to shell internal functions for shell commands. 3 4 Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR> 5 This program and the accompanying materials 6 are licensed and made available under the terms and conditions of the BSD License 7 which accompanies this distribution. The full text of the license may be found at 8 http://opensource.org/licenses/bsd-license.php 9 10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 13**/ 14 15#include "UefiShellCommandLib.h" 16 17/// The tag for use in identifying UNICODE files. 18/// If the file is UNICODE, the first 16 bits of the file will equal this value. 19enum { 20 gUnicodeFileTag = 0xFEFF 21}; 22 23// STATIC local variables 24STATIC SHELL_COMMAND_INTERNAL_LIST_ENTRY mCommandList; 25STATIC SCRIPT_FILE_LIST mScriptList; 26STATIC ALIAS_LIST mAliasList; 27STATIC BOOLEAN mEchoState; 28STATIC BOOLEAN mExitRequested; 29STATIC UINT64 mExitCode; 30STATIC BOOLEAN mExitScript; 31STATIC CHAR16 *mProfileList; 32STATIC UINTN mProfileListSize; 33STATIC UINTN mFsMaxCount = 0; 34STATIC UINTN mBlkMaxCount = 0; 35STATIC BUFFER_LIST mFileHandleList; 36 37// global variables required by library class. 38EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollation = NULL; 39EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *gDevPathToText = NULL; 40SHELL_MAP_LIST gShellMapList; 41SHELL_MAP_LIST *gShellCurDir = NULL; 42 43CONST CHAR16* SupportLevel[] = { 44 L"Minimal", 45 L"Scripting", 46 L"Basic", 47 L"Interactive" 48}; 49 50/** 51 Function to make sure that the global protocol pointers are valid. 52 must be called after constructor before accessing the pointers. 53**/ 54EFI_STATUS 55EFIAPI 56CommandInit( 57 VOID 58 ) 59{ 60 EFI_STATUS Status; 61 if (gUnicodeCollation == NULL) { 62 Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation); 63 if (EFI_ERROR(Status)) { 64 return (EFI_DEVICE_ERROR); 65 } 66 } 67 if (gDevPathToText == NULL) { 68 Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID**)&gDevPathToText); 69 if (EFI_ERROR(Status)) { 70 return (EFI_DEVICE_ERROR); 71 } 72 } 73 return (EFI_SUCCESS); 74} 75 76/** 77 Constructor for the Shell Command library. 78 79 Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell. 80 81 @param ImageHandle the image handle of the process 82 @param SystemTable the EFI System Table pointer 83 84 @retval EFI_SUCCESS the initialization was complete sucessfully 85**/ 86RETURN_STATUS 87EFIAPI 88ShellCommandLibConstructor ( 89 IN EFI_HANDLE ImageHandle, 90 IN EFI_SYSTEM_TABLE *SystemTable 91 ) 92{ 93 EFI_STATUS Status; 94 InitializeListHead(&gShellMapList.Link); 95 InitializeListHead(&mCommandList.Link); 96 InitializeListHead(&mAliasList.Link); 97 InitializeListHead(&mScriptList.Link); 98 InitializeListHead(&mFileHandleList.Link); 99 mEchoState = TRUE; 100 101 mExitRequested = FALSE; 102 mExitScript = FALSE; 103 mProfileListSize = 0; 104 mProfileList = NULL; 105 106 if (gUnicodeCollation == NULL) { 107 Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation); 108 if (EFI_ERROR(Status)) { 109 return (EFI_DEVICE_ERROR); 110 } 111 } 112 113 return (RETURN_SUCCESS); 114} 115 116/** 117 Destructor for the library. free any resources. 118 119 @param ImageHandle the image handle of the process 120 @param SystemTable the EFI System Table pointer 121 122 @retval RETURN_SUCCESS this function always returns success 123**/ 124RETURN_STATUS 125EFIAPI 126ShellCommandLibDestructor ( 127 IN EFI_HANDLE ImageHandle, 128 IN EFI_SYSTEM_TABLE *SystemTable 129 ) 130{ 131 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; 132 ALIAS_LIST *Node2; 133 SCRIPT_FILE_LIST *Node3; 134 SHELL_MAP_LIST *MapNode; 135 // 136 // enumerate throught the list and free all the memory 137 // 138 while (!IsListEmpty (&mCommandList.Link)) { 139 Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link); 140 RemoveEntryList(&Node->Link); 141 SHELL_FREE_NON_NULL(Node->CommandString); 142 FreePool(Node); 143 DEBUG_CODE(Node = NULL;); 144 } 145 146 // 147 // enumerate through the alias list and free all memory 148 // 149 while (!IsListEmpty (&mAliasList.Link)) { 150 Node2 = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link); 151 RemoveEntryList(&Node2->Link); 152 SHELL_FREE_NON_NULL(Node2->CommandString); 153 SHELL_FREE_NON_NULL(Node2->Alias); 154 SHELL_FREE_NON_NULL(Node2); 155 DEBUG_CODE(Node2 = NULL;); 156 } 157 158 // 159 // enumerate throught the list and free all the memory 160 // 161 while (!IsListEmpty (&mScriptList.Link)) { 162 Node3 = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link); 163 RemoveEntryList(&Node3->Link); 164 DeleteScriptFileStruct(Node3->Data); 165 FreePool(Node3); 166 } 167 168 // 169 // enumerate throught the mappings list and free all the memory 170 // 171 if (!IsListEmpty(&gShellMapList.Link)) { 172 for (MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) 173 ; !IsListEmpty (&gShellMapList.Link) 174 ; MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) 175 ){ 176 ASSERT(MapNode != NULL); 177 RemoveEntryList(&MapNode->Link); 178 SHELL_FREE_NON_NULL(MapNode->DevicePath); 179 SHELL_FREE_NON_NULL(MapNode->MapName); 180 SHELL_FREE_NON_NULL(MapNode->CurrentDirectoryPath); 181 FreePool(MapNode); 182 } 183 } 184 if (!IsListEmpty(&mFileHandleList.Link)){ 185 FreeBufferList(&mFileHandleList); 186 } 187 188 if (mProfileList != NULL) { 189 FreePool(mProfileList); 190 } 191 192 gUnicodeCollation = NULL; 193 gDevPathToText = NULL; 194 gShellCurDir = NULL; 195 196 return (RETURN_SUCCESS); 197} 198 199/** 200 Checks if a command is already on the list. 201 202 @param[in] CommandString The command string to check for on the list. 203**/ 204BOOLEAN 205EFIAPI 206ShellCommandIsCommandOnList ( 207 IN CONST CHAR16 *CommandString 208 ) 209{ 210 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; 211 212 // 213 // assert for NULL parameter 214 // 215 ASSERT(CommandString != NULL); 216 217 // 218 // check for the command 219 // 220 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link) 221 ; !IsNull(&mCommandList.Link, &Node->Link) 222 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link) 223 ){ 224 ASSERT(Node->CommandString != NULL); 225 if (gUnicodeCollation->StriColl( 226 gUnicodeCollation, 227 (CHAR16*)CommandString, 228 Node->CommandString) == 0 229 ){ 230 return (TRUE); 231 } 232 } 233 return (FALSE); 234} 235 236/** 237 Get the help text for a command. 238 239 @param[in] CommandString The command name. 240 241 @retval NULL No help text was found. 242 @return String of help text. Caller reuiqred to free. 243**/ 244CHAR16* 245EFIAPI 246ShellCommandGetCommandHelp ( 247 IN CONST CHAR16 *CommandString 248 ) 249{ 250 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; 251 252 // 253 // assert for NULL parameter 254 // 255 ASSERT(CommandString != NULL); 256 257 // 258 // check for the command 259 // 260 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link) 261 ; !IsNull(&mCommandList.Link, &Node->Link) 262 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link) 263 ){ 264 ASSERT(Node->CommandString != NULL); 265 if (gUnicodeCollation->StriColl( 266 gUnicodeCollation, 267 (CHAR16*)CommandString, 268 Node->CommandString) == 0 269 ){ 270 return (HiiGetString(Node->HiiHandle, Node->ManFormatHelp, NULL)); 271 } 272 } 273 return (NULL); 274} 275 276/** 277 Registers handlers of type SHELL_RUN_COMMAND and 278 SHELL_GET_MAN_FILENAME for each shell command. 279 280 If the ShellSupportLevel is greater than the value of the 281 PcdShellSupportLevel then return RETURN_UNSUPPORTED. 282 283 Registers the handlers specified by GetHelpInfoHandler and CommandHandler 284 with the command specified by CommandString. If the command named by 285 CommandString has already been registered, then return 286 RETURN_ALREADY_STARTED. 287 288 If there are not enough resources available to register the handlers then 289 RETURN_OUT_OF_RESOURCES is returned. 290 291 If CommandString is NULL, then ASSERT(). 292 If GetHelpInfoHandler is NULL, then ASSERT(). 293 If CommandHandler is NULL, then ASSERT(). 294 If ProfileName is NULL, then ASSERT(). 295 296 @param[in] CommandString Pointer to the command name. This is the 297 name to look for on the command line in 298 the shell. 299 @param[in] CommandHandler Pointer to a function that runs the 300 specified command. 301 @param[in] GetManFileName Pointer to a function that provides man 302 filename. 303 @param[in] ShellMinSupportLevel minimum Shell Support Level which has this 304 function. 305 @param[in] ProfileName profile name to require for support of this 306 function. 307 @param[in] CanAffectLE indicates whether this command's return value 308 can change the LASTERROR environment variable. 309 @param[in] HiiHandle Handle of this command's HII entry. 310 @param[in] ManFormatHelp HII locator for the help text. 311 312 @retval RETURN_SUCCESS The handlers were registered. 313 @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to 314 register the shell command. 315 @retval RETURN_UNSUPPORTED the ShellMinSupportLevel was higher than the 316 currently allowed support level. 317 @retval RETURN_ALREADY_STARTED The CommandString represents a command that 318 is already registered. Only 1 handler set for 319 a given command is allowed. 320 @sa SHELL_GET_MAN_FILENAME 321 @sa SHELL_RUN_COMMAND 322**/ 323RETURN_STATUS 324EFIAPI 325ShellCommandRegisterCommandName ( 326 IN CONST CHAR16 *CommandString, 327 IN SHELL_RUN_COMMAND CommandHandler, 328 IN SHELL_GET_MAN_FILENAME GetManFileName, 329 IN UINT32 ShellMinSupportLevel, 330 IN CONST CHAR16 *ProfileName, 331 IN CONST BOOLEAN CanAffectLE, 332 IN CONST EFI_HANDLE HiiHandle, 333 IN CONST EFI_STRING_ID ManFormatHelp 334 ) 335{ 336 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; 337 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Command; 338 SHELL_COMMAND_INTERNAL_LIST_ENTRY *PrevCommand; 339 INTN LexicalMatchValue; 340 341 // 342 // Initialize local variables. 343 // 344 Command = NULL; 345 PrevCommand = NULL; 346 LexicalMatchValue = 0; 347 348 // 349 // ASSERTs for NULL parameters 350 // 351 ASSERT(CommandString != NULL); 352 ASSERT(GetManFileName != NULL); 353 ASSERT(CommandHandler != NULL); 354 ASSERT(ProfileName != NULL); 355 356 // 357 // check for shell support level 358 // 359 if (PcdGet8(PcdShellSupportLevel) < ShellMinSupportLevel) { 360 return (RETURN_UNSUPPORTED); 361 } 362 363 // 364 // check for already on the list 365 // 366 if (ShellCommandIsCommandOnList(CommandString)) { 367 return (RETURN_ALREADY_STARTED); 368 } 369 370 // 371 // allocate memory for new struct 372 // 373 Node = AllocateZeroPool(sizeof(SHELL_COMMAND_INTERNAL_LIST_ENTRY)); 374 ASSERT(Node != NULL); 375 Node->CommandString = AllocateZeroPool(StrSize(CommandString)); 376 ASSERT(Node->CommandString != NULL); 377 378 // 379 // populate the new struct 380 // 381 StrCpy(Node->CommandString, CommandString); 382 383 Node->GetManFileName = GetManFileName; 384 Node->CommandHandler = CommandHandler; 385 Node->LastError = CanAffectLE; 386 Node->HiiHandle = HiiHandle; 387 Node->ManFormatHelp = ManFormatHelp; 388 389 if ( StrLen(ProfileName)>0 390 && ((mProfileList != NULL 391 && StrStr(mProfileList, ProfileName) == NULL) || mProfileList == NULL) 392 ){ 393 ASSERT((mProfileList == NULL && mProfileListSize == 0) || (mProfileList != NULL)); 394 if (mProfileList == NULL) { 395 // 396 // If this is the first make a leading ';' 397 // 398 StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0); 399 } 400 StrnCatGrow(&mProfileList, &mProfileListSize, ProfileName, 0); 401 StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0); 402 } 403 404 // 405 // Insert a new entry on top of the list 406 // 407 InsertHeadList (&mCommandList.Link, &Node->Link); 408 409 // 410 // Move a new registered command to its sorted ordered location in the list 411 // 412 for (Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link), 413 PrevCommand = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link) 414 ; !IsNull (&mCommandList.Link, &Command->Link) 415 ; Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Command->Link)) { 416 417 // 418 // Get Lexical Comparison Value between PrevCommand and Command list entry 419 // 420 LexicalMatchValue = gUnicodeCollation->StriColl ( 421 gUnicodeCollation, 422 PrevCommand->CommandString, 423 Command->CommandString 424 ); 425 426 // 427 // Swap PrevCommand and Command list entry if PrevCommand list entry 428 // is alphabetically greater than Command list entry 429 // 430 if (LexicalMatchValue > 0){ 431 Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *) SwapListEntries (&PrevCommand->Link, &Command->Link); 432 } else if (LexicalMatchValue < 0) { 433 // 434 // PrevCommand entry is lexically lower than Command entry 435 // 436 break; 437 } 438 } 439 440 return (RETURN_SUCCESS); 441} 442 443/** 444 Function to get the current Profile string. 445 446 @retval NULL There are no installed profiles. 447 @return A semi-colon delimited list of profiles. 448**/ 449CONST CHAR16 * 450EFIAPI 451ShellCommandGetProfileList ( 452 VOID 453 ) 454{ 455 return (mProfileList); 456} 457 458/** 459 Checks if a command string has been registered for CommandString and if so it runs 460 the previously registered handler for that command with the command line. 461 462 If CommandString is NULL, then ASSERT(). 463 464 If Sections is specified, then each section name listed will be compared in a casesensitive 465 manner, to the section names described in Appendix B UEFI Shell 2.0 spec. If the section exists, 466 it will be appended to the returned help text. If the section does not exist, no 467 information will be returned. If Sections is NULL, then all help text information 468 available will be returned. 469 470 @param[in] CommandString Pointer to the command name. This is the name 471 found on the command line in the shell. 472 @param[in, out] RetVal Pointer to the return vaule from the command handler. 473 474 @param[in, out] CanAffectLE indicates whether this command's return value 475 needs to be placed into LASTERROR environment variable. 476 477 @retval RETURN_SUCCESS The handler was run. 478 @retval RETURN_NOT_FOUND The CommandString did not match a registered 479 command name. 480 @sa SHELL_RUN_COMMAND 481**/ 482RETURN_STATUS 483EFIAPI 484ShellCommandRunCommandHandler ( 485 IN CONST CHAR16 *CommandString, 486 IN OUT SHELL_STATUS *RetVal, 487 IN OUT BOOLEAN *CanAffectLE OPTIONAL 488 ) 489{ 490 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; 491 492 // 493 // assert for NULL parameters 494 // 495 ASSERT(CommandString != NULL); 496 497 // 498 // check for the command 499 // 500 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link) 501 ; !IsNull(&mCommandList.Link, &Node->Link) 502 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link) 503 ){ 504 ASSERT(Node->CommandString != NULL); 505 if (gUnicodeCollation->StriColl( 506 gUnicodeCollation, 507 (CHAR16*)CommandString, 508 Node->CommandString) == 0 509 ){ 510 if (CanAffectLE != NULL) { 511 *CanAffectLE = Node->LastError; 512 } 513 if (RetVal != NULL) { 514 *RetVal = Node->CommandHandler(NULL, gST); 515 } else { 516 Node->CommandHandler(NULL, gST); 517 } 518 return (RETURN_SUCCESS); 519 } 520 } 521 return (RETURN_NOT_FOUND); 522} 523 524/** 525 Checks if a command string has been registered for CommandString and if so it 526 returns the MAN filename specified for that command. 527 528 If CommandString is NULL, then ASSERT(). 529 530 @param[in] CommandString Pointer to the command name. This is the name 531 found on the command line in the shell.\ 532 533 @retval NULL the commandString was not a registered command. 534 @return other the name of the MAN file. 535 @sa SHELL_GET_MAN_FILENAME 536**/ 537CONST CHAR16* 538EFIAPI 539ShellCommandGetManFileNameHandler ( 540 IN CONST CHAR16 *CommandString 541 ) 542{ 543 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node; 544 545 // 546 // assert for NULL parameters 547 // 548 ASSERT(CommandString != NULL); 549 550 // 551 // check for the command 552 // 553 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link) 554 ; !IsNull(&mCommandList.Link, &Node->Link) 555 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link) 556 ){ 557 ASSERT(Node->CommandString != NULL); 558 if (gUnicodeCollation->StriColl( 559 gUnicodeCollation, 560 (CHAR16*)CommandString, 561 Node->CommandString) == 0 562 ){ 563 return (Node->GetManFileName()); 564 } 565 } 566 return (NULL); 567} 568 569/** 570 Get the list of all available shell internal commands. This is a linked list 571 (via LIST_ENTRY structure). enumerate through it using the BaseLib linked 572 list functions. do not modify the values. 573 574 @param[in] Sort TRUE to alphabetically sort the values first. FALSE otherwise. 575 576 @return a Linked list of all available shell commands. 577**/ 578CONST COMMAND_LIST* 579EFIAPI 580ShellCommandGetCommandList ( 581 IN CONST BOOLEAN Sort 582 ) 583{ 584// if (!Sort) { 585// return ((COMMAND_LIST*)(&mCommandList)); 586// } 587 return ((COMMAND_LIST*)(&mCommandList)); 588} 589 590/** 591 Registers aliases to be set as part of the initialization of the shell application. 592 593 If Command is NULL, then ASSERT(). 594 If Alias is NULL, then ASSERT(). 595 596 @param[in] Command Pointer to the Command 597 @param[in] Alias Pointer to Alias 598 599 @retval RETURN_SUCCESS The handlers were registered. 600 @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to 601 register the shell command. 602**/ 603RETURN_STATUS 604EFIAPI 605ShellCommandRegisterAlias ( 606 IN CONST CHAR16 *Command, 607 IN CONST CHAR16 *Alias 608 ) 609{ 610 ALIAS_LIST *Node; 611 612 // 613 // Asserts for NULL 614 // 615 ASSERT(Command != NULL); 616 ASSERT(Alias != NULL); 617 618 // 619 // allocate memory for new struct 620 // 621 Node = AllocateZeroPool(sizeof(ALIAS_LIST)); 622 ASSERT(Node != NULL); 623 Node->CommandString = AllocateZeroPool(StrSize(Command)); 624 Node->Alias = AllocateZeroPool(StrSize(Alias)); 625 ASSERT(Node->CommandString != NULL); 626 ASSERT(Node->Alias != NULL); 627 628 // 629 // populate the new struct 630 // 631 StrCpy(Node->CommandString, Command); 632 StrCpy(Node->Alias , Alias ); 633 634 // 635 // add the new struct to the list 636 // 637 InsertTailList (&mAliasList.Link, &Node->Link); 638 639 return (RETURN_SUCCESS); 640} 641 642/** 643 Get the list of all shell alias commands. This is a linked list 644 (via LIST_ENTRY structure). enumerate through it using the BaseLib linked 645 list functions. do not modify the values. 646 647 @return a Linked list of all requested shell alias'. 648**/ 649CONST ALIAS_LIST* 650EFIAPI 651ShellCommandGetInitAliasList ( 652 VOID 653 ) 654{ 655 return (&mAliasList); 656} 657 658/** 659 Determine if a given alias is on the list of built in alias'. 660 661 @param[in] Alias The alias to test for 662 663 @retval TRUE The alias is a built in alias 664 @retval FALSE The alias is not a built in alias 665**/ 666BOOLEAN 667EFIAPI 668ShellCommandIsOnAliasList( 669 IN CONST CHAR16 *Alias 670 ) 671{ 672 ALIAS_LIST *Node; 673 674 // 675 // assert for NULL parameter 676 // 677 ASSERT(Alias != NULL); 678 679 // 680 // check for the Alias 681 // 682 for ( Node = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link) 683 ; !IsNull(&mAliasList.Link, &Node->Link) 684 ; Node = (ALIAS_LIST *)GetNextNode(&mAliasList.Link, &Node->Link) 685 ){ 686 ASSERT(Node->CommandString != NULL); 687 ASSERT(Node->Alias != NULL); 688 if (gUnicodeCollation->StriColl( 689 gUnicodeCollation, 690 (CHAR16*)Alias, 691 Node->CommandString) == 0 692 ){ 693 return (TRUE); 694 } 695 if (gUnicodeCollation->StriColl( 696 gUnicodeCollation, 697 (CHAR16*)Alias, 698 Node->Alias) == 0 699 ){ 700 return (TRUE); 701 } 702 } 703 return (FALSE); 704} 705 706/** 707 Function to determine current state of ECHO. Echo determins if lines from scripts 708 and ECHO commands are enabled. 709 710 @retval TRUE Echo is currently enabled 711 @retval FALSE Echo is currently disabled 712**/ 713BOOLEAN 714EFIAPI 715ShellCommandGetEchoState( 716 VOID 717 ) 718{ 719 return (mEchoState); 720} 721 722/** 723 Function to set current state of ECHO. Echo determins if lines from scripts 724 and ECHO commands are enabled. 725 726 If State is TRUE, Echo will be enabled. 727 If State is FALSE, Echo will be disabled. 728 729 @param[in] State How to set echo. 730**/ 731VOID 732EFIAPI 733ShellCommandSetEchoState( 734 IN BOOLEAN State 735 ) 736{ 737 mEchoState = State; 738} 739 740/** 741 Indicate that the current shell or script should exit. 742 743 @param[in] ScriptOnly TRUE if exiting a script; FALSE otherwise. 744 @param[in] ErrorCode The 64 bit error code to return. 745**/ 746VOID 747EFIAPI 748ShellCommandRegisterExit ( 749 IN BOOLEAN ScriptOnly, 750 IN CONST UINT64 ErrorCode 751 ) 752{ 753 mExitRequested = (BOOLEAN)(!mExitRequested); 754 if (mExitRequested) { 755 mExitScript = ScriptOnly; 756 } else { 757 mExitScript = FALSE; 758 } 759 mExitCode = ErrorCode; 760} 761 762/** 763 Retrieve the Exit indicator. 764 765 @retval TRUE Exit was indicated. 766 @retval FALSE Exis was not indicated. 767**/ 768BOOLEAN 769EFIAPI 770ShellCommandGetExit ( 771 VOID 772 ) 773{ 774 return (mExitRequested); 775} 776 777/** 778 Retrieve the Exit code. 779 780 If ShellCommandGetExit returns FALSE than the return from this is undefined. 781 782 @return the value passed into RegisterExit. 783**/ 784UINT64 785EFIAPI 786ShellCommandGetExitCode ( 787 VOID 788 ) 789{ 790 return (mExitCode); 791} 792/** 793 Retrieve the Exit script indicator. 794 795 If ShellCommandGetExit returns FALSE than the return from this is undefined. 796 797 @retval TRUE ScriptOnly was indicated. 798 @retval FALSE ScriptOnly was not indicated. 799**/ 800BOOLEAN 801EFIAPI 802ShellCommandGetScriptExit ( 803 VOID 804 ) 805{ 806 return (mExitScript); 807} 808 809/** 810 Function to cleanup all memory from a SCRIPT_FILE structure. 811 812 @param[in] Script The pointer to the structure to cleanup. 813**/ 814VOID 815EFIAPI 816DeleteScriptFileStruct ( 817 IN SCRIPT_FILE *Script 818 ) 819{ 820 UINT8 LoopVar; 821 822 if (Script == NULL) { 823 return; 824 } 825 826 for (LoopVar = 0 ; LoopVar < Script->Argc ; LoopVar++) { 827 SHELL_FREE_NON_NULL(Script->Argv[LoopVar]); 828 } 829 if (Script->Argv != NULL) { 830 SHELL_FREE_NON_NULL(Script->Argv); 831 } 832 Script->CurrentCommand = NULL; 833 while (!IsListEmpty (&Script->CommandList)) { 834 Script->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&Script->CommandList); 835 if (Script->CurrentCommand != NULL) { 836 RemoveEntryList(&Script->CurrentCommand->Link); 837 if (Script->CurrentCommand->Cl != NULL) { 838 SHELL_FREE_NON_NULL(Script->CurrentCommand->Cl); 839 } 840 if (Script->CurrentCommand->Data != NULL) { 841 SHELL_FREE_NON_NULL(Script->CurrentCommand->Data); 842 } 843 SHELL_FREE_NON_NULL(Script->CurrentCommand); 844 } 845 } 846 SHELL_FREE_NON_NULL(Script->ScriptName); 847 SHELL_FREE_NON_NULL(Script); 848} 849 850/** 851 Function to return a pointer to the currently running script file object. 852 853 @retval NULL A script file is not currently running. 854 @return A pointer to the current script file object. 855**/ 856SCRIPT_FILE* 857EFIAPI 858ShellCommandGetCurrentScriptFile ( 859 VOID 860 ) 861{ 862 SCRIPT_FILE_LIST *List; 863 if (IsListEmpty (&mScriptList.Link)) { 864 return (NULL); 865 } 866 List = ((SCRIPT_FILE_LIST*)GetFirstNode(&mScriptList.Link)); 867 return (List->Data); 868} 869 870/** 871 Function to set a new script as the currently running one. 872 873 This function will correctly stack and unstack nested scripts. 874 875 @param[in] Script Pointer to new script information structure. if NULL 876 will remove and de-allocate the top-most Script structure. 877 878 @return A pointer to the current running script file after this 879 change. NULL if removing the final script. 880**/ 881SCRIPT_FILE* 882EFIAPI 883ShellCommandSetNewScript ( 884 IN SCRIPT_FILE *Script OPTIONAL 885 ) 886{ 887 SCRIPT_FILE_LIST *Node; 888 if (Script == NULL) { 889 if (IsListEmpty (&mScriptList.Link)) { 890 return (NULL); 891 } 892 Node = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link); 893 RemoveEntryList(&Node->Link); 894 DeleteScriptFileStruct(Node->Data); 895 FreePool(Node); 896 } else { 897 Node = AllocateZeroPool(sizeof(SCRIPT_FILE_LIST)); 898 if (Node == NULL) { 899 return (NULL); 900 } 901 Node->Data = Script; 902 InsertHeadList(&mScriptList.Link, &Node->Link); 903 } 904 return (ShellCommandGetCurrentScriptFile()); 905} 906 907/** 908 Function to generate the next default mapping name. 909 910 If the return value is not NULL then it must be callee freed. 911 912 @param Type What kind of mapping name to make. 913 914 @retval NULL a memory allocation failed. 915 @return a new map name string 916**/ 917CHAR16* 918EFIAPI 919ShellCommandCreateNewMappingName( 920 IN CONST SHELL_MAPPING_TYPE Type 921 ) 922{ 923 CHAR16 *String; 924 ASSERT(Type < MappingTypeMax); 925 926 String = NULL; 927 928 String = AllocateZeroPool(PcdGet8(PcdShellMapNameLength) * sizeof(String[0])); 929 UnicodeSPrint( 930 String, 931 PcdGet8(PcdShellMapNameLength) * sizeof(String[0]), 932 Type == MappingTypeFileSystem?L"FS%d:":L"BLK%d:", 933 Type == MappingTypeFileSystem?mFsMaxCount++:mBlkMaxCount++); 934 935 return (String); 936} 937 938/** 939 Function to add a map node to the list of map items and update the "path" environment variable (optionally). 940 941 If Path is TRUE (during initialization only), the path environment variable will also be updated to include 942 default paths on the new map name... 943 944 Path should be FALSE when this function is called from the protocol SetMap function. 945 946 @param[in] Name The human readable mapped name. 947 @param[in] DevicePath The Device Path for this map. 948 @param[in] Flags The Flags attribute for this map item. 949 @param[in] Path TRUE to update path, FALSE to skip this step (should only be TRUE during initialization). 950 951 @retval EFI_SUCCESS The addition was sucessful. 952 @retval EFI_OUT_OF_RESOURCES A memory allocation failed. 953 @retval EFI_INVALID_PARAMETER A parameter was invalid. 954**/ 955EFI_STATUS 956EFIAPI 957ShellCommandAddMapItemAndUpdatePath( 958 IN CONST CHAR16 *Name, 959 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, 960 IN CONST UINT64 Flags, 961 IN CONST BOOLEAN Path 962 ) 963{ 964 EFI_STATUS Status; 965 SHELL_MAP_LIST *MapListNode; 966 CONST CHAR16 *OriginalPath; 967 CHAR16 *NewPath; 968 UINTN NewPathSize; 969 970 NewPathSize = 0; 971 NewPath = NULL; 972 OriginalPath = NULL; 973 Status = EFI_SUCCESS; 974 975 MapListNode = AllocateZeroPool(sizeof(SHELL_MAP_LIST)); 976 if (MapListNode == NULL) { 977 Status = EFI_OUT_OF_RESOURCES; 978 } else { 979 MapListNode->Flags = Flags; 980 MapListNode->MapName = AllocateZeroPool(StrSize(Name)); 981 MapListNode->DevicePath = DuplicateDevicePath(DevicePath); 982 if ((MapListNode->MapName == NULL) || (MapListNode->DevicePath == NULL)){ 983 Status = EFI_OUT_OF_RESOURCES; 984 } else { 985 StrCpy(MapListNode->MapName, Name); 986 InsertTailList(&gShellMapList.Link, &MapListNode->Link); 987 } 988 } 989 if (EFI_ERROR(Status)) { 990 if (MapListNode != NULL) { 991 if (MapListNode->DevicePath != NULL) { 992 FreePool(MapListNode->DevicePath); 993 } 994 if (MapListNode->MapName != NULL) { 995 FreePool(MapListNode->MapName); 996 } 997 FreePool(MapListNode); 998 } 999 } else if (Path) { 1000 // 1001 // Since there was no error and Path was TRUE 1002 // Now add the correct path for that mapping 1003 // 1004 OriginalPath = gEfiShellProtocol->GetEnv(L"path"); 1005 ASSERT((NewPath == NULL && NewPathSize == 0) || (NewPath != NULL)); 1006 if (OriginalPath != NULL) { 1007 StrnCatGrow(&NewPath, &NewPathSize, OriginalPath, 0); 1008 } else { 1009 StrnCatGrow(&NewPath, &NewPathSize, L".\\", 0); 1010 } 1011 StrnCatGrow(&NewPath, &NewPathSize, L";", 0); 1012 StrnCatGrow(&NewPath, &NewPathSize, Name, 0); 1013 StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\tools\\;", 0); 1014 StrnCatGrow(&NewPath, &NewPathSize, Name, 0); 1015 StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\boot\\;", 0); 1016 StrnCatGrow(&NewPath, &NewPathSize, Name, 0); 1017 StrnCatGrow(&NewPath, &NewPathSize, L"\\", 0); 1018 1019 Status = gEfiShellProtocol->SetEnv(L"path", NewPath, TRUE); 1020 ASSERT_EFI_ERROR(Status); 1021 FreePool(NewPath); 1022 } 1023 return (Status); 1024} 1025 1026/** 1027 Creates the default map names for each device path in the system with 1028 a protocol depending on the Type. 1029 1030 Creates the consistent map names for each device path in the system with 1031 a protocol depending on the Type. 1032 1033 Note: This will reset all mappings in the system("map -r"). 1034 1035 Also sets up the default path environment variable if Type is FileSystem. 1036 1037 @retval EFI_SUCCESS All map names were created sucessfully. 1038 @retval EFI_NOT_FOUND No protocols were found in the system. 1039 @return Error returned from gBS->LocateHandle(). 1040 1041 @sa LocateHandle 1042**/ 1043EFI_STATUS 1044EFIAPI 1045ShellCommandCreateInitialMappingsAndPaths( 1046 VOID 1047 ) 1048{ 1049 EFI_STATUS Status; 1050 EFI_HANDLE *HandleList; 1051 UINTN Count; 1052 EFI_DEVICE_PATH_PROTOCOL **DevicePathList; 1053 CHAR16 *NewDefaultName; 1054 CHAR16 *NewConsistName; 1055 EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable; 1056 SHELL_MAP_LIST *MapListNode; 1057 1058 HandleList = NULL; 1059 1060 // 1061 // Reset the static members back to zero 1062 // 1063 mFsMaxCount = 0; 1064 mBlkMaxCount = 0; 1065 1066 gEfiShellProtocol->SetEnv(L"path", L"", TRUE); 1067 1068 // 1069 // First empty out the existing list. 1070 // 1071 if (!IsListEmpty(&gShellMapList.Link)) { 1072 for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) 1073 ; !IsListEmpty(&gShellMapList.Link) 1074 ; MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) 1075 ){ 1076 RemoveEntryList(&MapListNode->Link); 1077 FreePool(MapListNode); 1078 } // for loop 1079 } 1080 1081 // 1082 // Find each handle with Simple File System 1083 // 1084 HandleList = GetHandleListByProtocol(&gEfiSimpleFileSystemProtocolGuid); 1085 if (HandleList != NULL) { 1086 // 1087 // Do a count of the handles 1088 // 1089 for (Count = 0 ; HandleList[Count] != NULL ; Count++); 1090 1091 // 1092 // Get all Device Paths 1093 // 1094 DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count); 1095 ASSERT(DevicePathList != NULL); 1096 1097 for (Count = 0 ; HandleList[Count] != NULL ; Count++) { 1098 DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]); 1099 } 1100 1101 // 1102 // Sort all DevicePaths 1103 // 1104 PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare); 1105 1106 ShellCommandConsistMappingInitialize(&ConsistMappingTable); 1107 // 1108 // Assign new Mappings to all... 1109 // 1110 for (Count = 0 ; HandleList[Count] != NULL ; Count++) { 1111 // 1112 // Get default name first 1113 // 1114 NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem); 1115 ASSERT(NewDefaultName != NULL); 1116 Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, TRUE); 1117 ASSERT_EFI_ERROR(Status); 1118 FreePool(NewDefaultName); 1119 1120 // 1121 // Now do consistent name 1122 // 1123 NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable); 1124 if (NewConsistName != NULL) { 1125 Status = ShellCommandAddMapItemAndUpdatePath(NewConsistName, DevicePathList[Count], 0, FALSE); 1126 ASSERT_EFI_ERROR(Status); 1127 FreePool(NewConsistName); 1128 } 1129 } 1130 1131 ShellCommandConsistMappingUnInitialize(ConsistMappingTable); 1132 1133 SHELL_FREE_NON_NULL(HandleList); 1134 SHELL_FREE_NON_NULL(DevicePathList); 1135 1136 HandleList = NULL; 1137 } else { 1138 Count = (UINTN)-1; 1139 } 1140 1141 // 1142 // Find each handle with Block Io 1143 // 1144 HandleList = GetHandleListByProtocol(&gEfiBlockIoProtocolGuid); 1145 if (HandleList != NULL) { 1146 for (Count = 0 ; HandleList[Count] != NULL ; Count++); 1147 1148 // 1149 // Get all Device Paths 1150 // 1151 DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count); 1152 ASSERT(DevicePathList != NULL); 1153 1154 for (Count = 0 ; HandleList[Count] != NULL ; Count++) { 1155 DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]); 1156 } 1157 1158 // 1159 // Sort all DevicePaths 1160 // 1161 PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare); 1162 1163 // 1164 // Assign new Mappings to all... 1165 // 1166 for (Count = 0 ; HandleList[Count] != NULL ; Count++) { 1167 // 1168 // Get default name first 1169 // 1170 NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeBlockIo); 1171 ASSERT(NewDefaultName != NULL); 1172 Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, FALSE); 1173 ASSERT_EFI_ERROR(Status); 1174 FreePool(NewDefaultName); 1175 } 1176 1177 SHELL_FREE_NON_NULL(HandleList); 1178 SHELL_FREE_NON_NULL(DevicePathList); 1179 } else if (Count == (UINTN)-1) { 1180 return (EFI_NOT_FOUND); 1181 } 1182 1183 return (EFI_SUCCESS); 1184} 1185 1186/** 1187 Converts a SHELL_FILE_HANDLE to an EFI_FILE_PROTOCOL*. 1188 1189 @param[in] Handle The SHELL_FILE_HANDLE to convert. 1190 1191 @return a EFI_FILE_PROTOCOL* representing the same file. 1192**/ 1193EFI_FILE_PROTOCOL* 1194EFIAPI 1195ConvertShellHandleToEfiFileProtocol( 1196 IN CONST SHELL_FILE_HANDLE Handle 1197 ) 1198{ 1199 return ((EFI_FILE_PROTOCOL*)(Handle)); 1200} 1201 1202/** 1203 Converts a EFI_FILE_PROTOCOL* to an SHELL_FILE_HANDLE. 1204 1205 @param[in] Handle The pointer to EFI_FILE_PROTOCOL to convert. 1206 @param[in] Path The path to the file for verification. 1207 1208 @return A SHELL_FILE_HANDLE representing the same file. 1209 @retval NULL There was not enough memory. 1210**/ 1211SHELL_FILE_HANDLE 1212EFIAPI 1213ConvertEfiFileProtocolToShellHandle( 1214 IN CONST EFI_FILE_PROTOCOL *Handle, 1215 IN CONST CHAR16 *Path 1216 ) 1217{ 1218 SHELL_COMMAND_FILE_HANDLE *Buffer; 1219 BUFFER_LIST *NewNode; 1220 1221 if (Path != NULL) { 1222 Buffer = AllocateZeroPool(sizeof(SHELL_COMMAND_FILE_HANDLE)); 1223 if (Buffer == NULL) { 1224 return (NULL); 1225 } 1226 NewNode = AllocateZeroPool(sizeof(BUFFER_LIST)); 1227 if (NewNode == NULL) { 1228 return (NULL); 1229 } 1230 Buffer->FileHandle = (EFI_FILE_PROTOCOL*)Handle; 1231 Buffer->Path = StrnCatGrow(&Buffer->Path, NULL, Path, 0); 1232 if (Buffer->Path == NULL) { 1233 return (NULL); 1234 } 1235 NewNode->Buffer = Buffer; 1236 1237 InsertHeadList(&mFileHandleList.Link, &NewNode->Link); 1238 } 1239 return ((SHELL_FILE_HANDLE)(Handle)); 1240} 1241 1242/** 1243 Find the path that was logged with the specified SHELL_FILE_HANDLE. 1244 1245 @param[in] Handle The SHELL_FILE_HANDLE to query on. 1246 1247 @return A pointer to the path for the file. 1248**/ 1249CONST CHAR16* 1250EFIAPI 1251ShellFileHandleGetPath( 1252 IN CONST SHELL_FILE_HANDLE Handle 1253 ) 1254{ 1255 BUFFER_LIST *Node; 1256 1257 for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link) 1258 ; !IsNull(&mFileHandleList.Link, &Node->Link) 1259 ; Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link) 1260 ){ 1261 if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){ 1262 return (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path); 1263 } 1264 } 1265 return (NULL); 1266} 1267 1268/** 1269 Remove a SHELL_FILE_HANDLE from the list of SHELL_FILE_HANDLES. 1270 1271 @param[in] Handle The SHELL_FILE_HANDLE to remove. 1272 1273 @retval TRUE The item was removed. 1274 @retval FALSE The item was not found. 1275**/ 1276BOOLEAN 1277EFIAPI 1278ShellFileHandleRemove( 1279 IN CONST SHELL_FILE_HANDLE Handle 1280 ) 1281{ 1282 BUFFER_LIST *Node; 1283 1284 for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link) 1285 ; !IsNull(&mFileHandleList.Link, &Node->Link) 1286 ; Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link) 1287 ){ 1288 if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){ 1289 RemoveEntryList(&Node->Link); 1290 SHELL_FREE_NON_NULL(((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path); 1291 SHELL_FREE_NON_NULL(Node->Buffer); 1292 SHELL_FREE_NON_NULL(Node); 1293 return (TRUE); 1294 } 1295 } 1296 return (FALSE); 1297} 1298 1299/** 1300 Function to determine if a SHELL_FILE_HANDLE is at the end of the file. 1301 1302 This will NOT work on directories. 1303 1304 If Handle is NULL, then ASSERT. 1305 1306 @param[in] Handle the file handle 1307 1308 @retval TRUE the position is at the end of the file 1309 @retval FALSE the position is not at the end of the file 1310**/ 1311BOOLEAN 1312EFIAPI 1313ShellFileHandleEof( 1314 IN SHELL_FILE_HANDLE Handle 1315 ) 1316{ 1317 EFI_FILE_INFO *Info; 1318 UINT64 Pos; 1319 BOOLEAN RetVal; 1320 1321 // 1322 // ASSERT if Handle is NULL 1323 // 1324 ASSERT(Handle != NULL); 1325 1326 gEfiShellProtocol->GetFilePosition(Handle, &Pos); 1327 Info = gEfiShellProtocol->GetFileInfo (Handle); 1328 ASSERT(Info != NULL); 1329 gEfiShellProtocol->SetFilePosition(Handle, Pos); 1330 1331 if (Info == NULL) { 1332 return (FALSE); 1333 } 1334 1335 if (Pos == Info->FileSize) { 1336 RetVal = TRUE; 1337 } else { 1338 RetVal = FALSE; 1339 } 1340 1341 FreePool (Info); 1342 1343 return (RetVal); 1344} 1345 1346/** 1347 Frees any BUFFER_LIST defined type. 1348 1349 @param[in] List The BUFFER_LIST object to free. 1350**/ 1351VOID 1352EFIAPI 1353FreeBufferList ( 1354 IN BUFFER_LIST *List 1355 ) 1356{ 1357 BUFFER_LIST *BufferListEntry; 1358 1359 if (List == NULL){ 1360 return; 1361 } 1362 // 1363 // enumerate through the buffer list and free all memory 1364 // 1365 for ( BufferListEntry = ( BUFFER_LIST *)GetFirstNode(&List->Link) 1366 ; !IsListEmpty (&List->Link) 1367 ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link) 1368 ){ 1369 RemoveEntryList(&BufferListEntry->Link); 1370 ASSERT(BufferListEntry->Buffer != NULL); 1371 if (BufferListEntry->Buffer != NULL) { 1372 FreePool(BufferListEntry->Buffer); 1373 } 1374 FreePool(BufferListEntry); 1375 } 1376} 1377 1378