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