Home | History | Annotate | Download | only in Shell
      1 /** @file
      2   Member functions of EFI_SHELL_PROTOCOL and functions for creation,
      3   manipulation, and initialization of EFI_SHELL_PROTOCOL.
      4 
      5   (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
      6   (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
      7   Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
      8   This program and the accompanying materials
      9   are licensed and made available under the terms and conditions of the BSD License
     10   which accompanies this distribution.  The full text of the license may be found at
     11   http://opensource.org/licenses/bsd-license.php
     12 
     13   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     14   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     15 
     16 **/
     17 
     18 #include "Shell.h"
     19 
     20 #define INIT_NAME_BUFFER_SIZE  128
     21 
     22 /**
     23   Close an open file handle.
     24 
     25   This function closes a specified file handle. All "dirty" cached file data is
     26   flushed to the device, and the file is closed. In all cases the handle is
     27   closed.
     28 
     29   @param[in] FileHandle           The file handle to close.
     30 
     31   @retval EFI_SUCCESS             The file handle was closed successfully.
     32 **/
     33 EFI_STATUS
     34 EFIAPI
     35 EfiShellClose (
     36   IN SHELL_FILE_HANDLE            FileHandle
     37   )
     38 {
     39   ShellFileHandleRemove(FileHandle);
     40   return (FileHandleClose(ConvertShellHandleToEfiFileProtocol(FileHandle)));
     41 }
     42 
     43 /**
     44   Internal worker to determine whether there is a BlockIo somewhere
     45   upon the device path specified.
     46 
     47   @param[in] DevicePath    The device path to test.
     48 
     49   @retval TRUE      gEfiBlockIoProtocolGuid was installed on a handle with this device path
     50   @retval FALSE     gEfiBlockIoProtocolGuid was not found.
     51 **/
     52 BOOLEAN
     53 InternalShellProtocolIsBlockIoPresent(
     54   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
     55   )
     56 {
     57   EFI_DEVICE_PATH_PROTOCOL  *DevicePathCopy;
     58   EFI_STATUS                Status;
     59   EFI_HANDLE                Handle;
     60 
     61   Handle = NULL;
     62 
     63   DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)DevicePath;
     64   Status = gBS->LocateDevicePath(&gEfiBlockIoProtocolGuid, &DevicePathCopy, &Handle);
     65 
     66   if ((Handle != NULL) && (!EFI_ERROR(Status))) {
     67     return (TRUE);
     68   }
     69   return (FALSE);
     70 }
     71 
     72 /**
     73   Internal worker to determine whether there is a file system somewhere
     74   upon the device path specified.
     75 
     76   @param[in] DevicePath    The device path to test.
     77 
     78   @retval TRUE      gEfiSimpleFileSystemProtocolGuid was installed on a handle with this device path
     79   @retval FALSE     gEfiSimpleFileSystemProtocolGuid was not found.
     80 **/
     81 BOOLEAN
     82 InternalShellProtocolIsSimpleFileSystemPresent(
     83   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
     84   )
     85 {
     86   EFI_DEVICE_PATH_PROTOCOL  *DevicePathCopy;
     87   EFI_STATUS                Status;
     88   EFI_HANDLE                Handle;
     89 
     90   Handle = NULL;
     91 
     92   DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)DevicePath;
     93   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);
     94 
     95   if ((Handle != NULL) && (!EFI_ERROR(Status))) {
     96     return (TRUE);
     97   }
     98   return (FALSE);
     99 }
    100 
    101 /**
    102   Internal worker debug helper function to print out maps as they are added.
    103 
    104   @param[in] Mapping        string mapping that has been added
    105   @param[in] DevicePath     pointer to device path that has been mapped.
    106 
    107   @retval EFI_SUCCESS   the operation was successful.
    108   @return other         an error ocurred
    109 
    110   @sa LocateHandle
    111   @sa OpenProtocol
    112 **/
    113 EFI_STATUS
    114 InternalShellProtocolDebugPrintMessage (
    115   IN CONST CHAR16                   *Mapping,
    116   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
    117   )
    118 {
    119   EFI_STATUS                        Status;
    120   CHAR16                            *Temp;
    121 
    122   Status = EFI_SUCCESS;
    123   DEBUG_CODE_BEGIN();
    124 
    125   if (Mapping != NULL) {
    126     DEBUG((EFI_D_INFO, "Added new map item:\"%S\"\r\n", Mapping));
    127   }
    128   Temp = ConvertDevicePathToText(DevicePath, TRUE, TRUE);
    129   DEBUG((EFI_D_INFO, "DevicePath: %S\r\n", Temp));
    130   FreePool(Temp);
    131 
    132   DEBUG_CODE_END();
    133   return (Status);
    134 }
    135 
    136 /**
    137   This function creates a mapping for a device path.
    138 
    139   If both DeviecPath and Mapping are NULL, this will reset the mapping to default values.
    140 
    141   @param DevicePath             Points to the device path. If this is NULL and Mapping points to a valid mapping,
    142                                 then the mapping will be deleted.
    143   @param Mapping                Points to the NULL-terminated mapping for the device path.  Must end with a ':'
    144 
    145   @retval EFI_SUCCESS           Mapping created or deleted successfully.
    146   @retval EFI_NO_MAPPING        There is no handle that corresponds exactly to DevicePath. See the
    147                                 boot service function LocateDevicePath().
    148   @retval EFI_ACCESS_DENIED     The mapping is a built-in alias.
    149   @retval EFI_INVALID_PARAMETER Mapping was NULL
    150   @retval EFI_INVALID_PARAMETER Mapping did not end with a ':'
    151   @retval EFI_INVALID_PARAMETER DevicePath was not pointing at a device that had a SIMPLE_FILE_SYSTEM_PROTOCOL installed.
    152   @retval EFI_NOT_FOUND         There was no mapping found to delete
    153   @retval EFI_OUT_OF_RESOURCES  Memory allocation failed
    154 **/
    155 EFI_STATUS
    156 EFIAPI
    157 EfiShellSetMap(
    158   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL,
    159   IN CONST CHAR16 *Mapping
    160   )
    161 {
    162   EFI_STATUS      Status;
    163   SHELL_MAP_LIST  *MapListNode;
    164 
    165   if (Mapping == NULL){
    166     return (EFI_INVALID_PARAMETER);
    167   }
    168 
    169   if (Mapping[StrLen(Mapping)-1] != ':') {
    170     return (EFI_INVALID_PARAMETER);
    171   }
    172 
    173   //
    174   // Delete the mapping
    175   //
    176   if (DevicePath == NULL) {
    177     if (IsListEmpty(&gShellMapList.Link)) {
    178       return (EFI_NOT_FOUND);
    179     }
    180     for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
    181         ; !IsNull(&gShellMapList.Link, &MapListNode->Link)
    182         ; MapListNode = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListNode->Link)
    183        ){
    184           if (StringNoCaseCompare(&MapListNode->MapName, &Mapping) == 0) {
    185             RemoveEntryList(&MapListNode->Link);
    186             SHELL_FREE_NON_NULL(MapListNode->DevicePath);
    187             SHELL_FREE_NON_NULL(MapListNode->MapName);
    188             SHELL_FREE_NON_NULL(MapListNode->CurrentDirectoryPath);
    189             FreePool(MapListNode);
    190             return (EFI_SUCCESS);
    191           }
    192     } // for loop
    193 
    194     //
    195     // We didnt find one to delete
    196     //
    197     return (EFI_NOT_FOUND);
    198   }
    199 
    200   //
    201   // make sure this is a valid to add device path
    202   //
    203   ///@todo add BlockIo to this test...
    204   if (!InternalShellProtocolIsSimpleFileSystemPresent(DevicePath)
    205     && !InternalShellProtocolIsBlockIoPresent(DevicePath)) {
    206     return (EFI_INVALID_PARAMETER);
    207   }
    208 
    209   //
    210   // First make sure there is no old mapping
    211   //
    212   Status = EfiShellSetMap(NULL, Mapping);
    213   if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_FOUND)) {
    214     return (Status);
    215   }
    216 
    217   //
    218   // now add the new one.
    219   //
    220   Status = ShellCommandAddMapItemAndUpdatePath(Mapping, DevicePath, 0, FALSE);
    221 
    222   return(Status);
    223 }
    224 
    225 /**
    226   Gets the device path from the mapping.
    227 
    228   This function gets the device path associated with a mapping.
    229 
    230   @param Mapping                A pointer to the mapping
    231 
    232   @retval !=NULL                Pointer to the device path that corresponds to the
    233                                 device mapping. The returned pointer does not need
    234                                 to be freed.
    235   @retval NULL                  There is no device path associated with the
    236                                 specified mapping.
    237 **/
    238 CONST EFI_DEVICE_PATH_PROTOCOL *
    239 EFIAPI
    240 EfiShellGetDevicePathFromMap(
    241   IN CONST CHAR16 *Mapping
    242   )
    243 {
    244   SHELL_MAP_LIST  *MapListItem;
    245   CHAR16          *NewName;
    246   UINTN           Size;
    247 
    248   NewName = NULL;
    249   Size    = 0;
    250 
    251   StrnCatGrow(&NewName, &Size, Mapping, 0);
    252   if (Mapping[StrLen(Mapping)-1] != L':') {
    253     StrnCatGrow(&NewName, &Size, L":", 0);
    254   }
    255 
    256   MapListItem = ShellCommandFindMapItem(NewName);
    257 
    258   FreePool(NewName);
    259 
    260   if (MapListItem != NULL) {
    261     return (MapListItem->DevicePath);
    262   }
    263   return(NULL);
    264 }
    265 
    266 /**
    267   Gets the mapping(s) that most closely matches the device path.
    268 
    269   This function gets the mapping which corresponds to the device path *DevicePath. If
    270   there is no exact match, then the mapping which most closely matches *DevicePath
    271   is returned, and *DevicePath is updated to point to the remaining portion of the
    272   device path. If there is an exact match, the mapping is returned and *DevicePath
    273   points to the end-of-device-path node.
    274 
    275   If there are multiple map names they will be semi-colon seperated in the
    276   NULL-terminated string.
    277 
    278   @param DevicePath             On entry, points to a device path pointer. On
    279                                 exit, updates the pointer to point to the
    280                                 portion of the device path after the mapping.
    281 
    282   @retval NULL                  No mapping was found.
    283   @return !=NULL                Pointer to NULL-terminated mapping. The buffer
    284                                 is callee allocated and should be freed by the caller.
    285 **/
    286 CONST CHAR16 *
    287 EFIAPI
    288 EfiShellGetMapFromDevicePath(
    289   IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
    290   )
    291 {
    292   SHELL_MAP_LIST              *Node;
    293   CHAR16                      *PathForReturn;
    294   UINTN                       PathSize;
    295 //  EFI_HANDLE                  PathHandle;
    296 //  EFI_HANDLE                  MapHandle;
    297 //  EFI_STATUS                  Status;
    298 //  EFI_DEVICE_PATH_PROTOCOL    *DevicePathCopy;
    299 //  EFI_DEVICE_PATH_PROTOCOL    *MapPathCopy;
    300 
    301   if (DevicePath == NULL || *DevicePath == NULL) {
    302     return (NULL);
    303   }
    304 
    305   PathForReturn = NULL;
    306   PathSize      = 0;
    307 
    308   for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
    309       ; !IsNull(&gShellMapList.Link, &Node->Link)
    310       ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
    311      ){
    312     //
    313     // check for exact match
    314     //
    315     if (DevicePathCompare(DevicePath, &Node->DevicePath) == 0) {
    316       ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
    317       if (PathSize != 0) {
    318         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
    319       }
    320       PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
    321     }
    322   }
    323   if (PathForReturn != NULL) {
    324     while (!IsDevicePathEndType (*DevicePath)) {
    325       *DevicePath = NextDevicePathNode (*DevicePath);
    326     }
    327     SetDevicePathEndNode (*DevicePath);
    328   }
    329 /*
    330   ///@todo finish code for inexact matches.
    331   if (PathForReturn == NULL) {
    332     PathSize = 0;
    333 
    334     DevicePathCopy = DuplicateDevicePath(*DevicePath);
    335     ASSERT(DevicePathCopy != NULL);
    336     Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
    337     ASSERT_EFI_ERROR(Status);
    338     //
    339     //  check each of the device paths we have to get the root of the path for consist mappings
    340     //
    341     for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
    342         ; !IsNull(&gShellMapList.Link, &Node->Link)
    343         ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
    344        ){
    345       if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) == 0) {
    346         continue;
    347       }
    348       MapPathCopy = DuplicateDevicePath(Node->DevicePath);
    349       ASSERT(MapPathCopy != NULL);
    350       Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
    351       if (MapHandle == PathHandle) {
    352 
    353         *DevicePath = DevicePathCopy;
    354 
    355         MapPathCopy = NULL;
    356         DevicePathCopy = NULL;
    357         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
    358         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
    359         break;
    360       }
    361     }
    362     //
    363     // now add on the non-consistent mappings
    364     //
    365     for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
    366         ; !IsNull(&gShellMapList.Link, &Node->Link)
    367         ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
    368        ){
    369       if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) != 0) {
    370         continue;
    371       }
    372       MapPathCopy = Node->DevicePath;
    373       ASSERT(MapPathCopy != NULL);
    374       Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
    375       if (MapHandle == PathHandle) {
    376         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
    377         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
    378         break;
    379       }
    380     }
    381   }
    382 */
    383 
    384   return (AddBufferToFreeList(PathForReturn));
    385 }
    386 
    387 /**
    388   Converts a device path to a file system-style path.
    389 
    390   This function converts a device path to a file system path by replacing part, or all, of
    391   the device path with the file-system mapping. If there are more than one application
    392   file system mappings, the one that most closely matches Path will be used.
    393 
    394   @param Path                   The pointer to the device path
    395 
    396   @retval NULL                  the device path could not be found.
    397   @return all                   The pointer of the NULL-terminated file path. The path
    398                                 is callee-allocated and should be freed by the caller.
    399 **/
    400 CHAR16 *
    401 EFIAPI
    402 EfiShellGetFilePathFromDevicePath(
    403   IN CONST EFI_DEVICE_PATH_PROTOCOL *Path
    404   )
    405 {
    406   EFI_DEVICE_PATH_PROTOCOL  *DevicePathCopy;
    407   EFI_DEVICE_PATH_PROTOCOL        *MapPathCopy;
    408   SHELL_MAP_LIST                  *MapListItem;
    409   CHAR16                          *PathForReturn;
    410   UINTN                           PathSize;
    411   EFI_HANDLE                      PathHandle;
    412   EFI_HANDLE                      MapHandle;
    413   EFI_STATUS                      Status;
    414   FILEPATH_DEVICE_PATH            *FilePath;
    415   FILEPATH_DEVICE_PATH            *AlignedNode;
    416 
    417   PathForReturn = NULL;
    418   PathSize = 0;
    419 
    420   DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)Path;
    421   ASSERT(DevicePathCopy != NULL);
    422   if (DevicePathCopy == NULL) {
    423     return (NULL);
    424   }
    425   ///@todo BlockIo?
    426   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
    427 
    428   if (EFI_ERROR(Status)) {
    429     return (NULL);
    430   }
    431   //
    432   //  check each of the device paths we have to get the root of the path
    433   //
    434   for ( MapListItem = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
    435       ; !IsNull(&gShellMapList.Link, &MapListItem->Link)
    436       ; MapListItem = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListItem->Link)
    437      ){
    438     MapPathCopy = (EFI_DEVICE_PATH_PROTOCOL*)MapListItem->DevicePath;
    439     ASSERT(MapPathCopy != NULL);
    440     ///@todo BlockIo?
    441     Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
    442     if (MapHandle == PathHandle) {
    443       ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
    444       PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, MapListItem->MapName, 0);
    445       //
    446       // go through all the remaining nodes in the device path
    447       //
    448       for ( FilePath = (FILEPATH_DEVICE_PATH*)DevicePathCopy
    449           ; !IsDevicePathEnd (&FilePath->Header)
    450           ; FilePath = (FILEPATH_DEVICE_PATH*)NextDevicePathNode (&FilePath->Header)
    451          ){
    452         //
    453         // If any node is not a file path node, then the conversion can not be completed
    454         //
    455         if ((DevicePathType(&FilePath->Header) != MEDIA_DEVICE_PATH) ||
    456             (DevicePathSubType(&FilePath->Header) != MEDIA_FILEPATH_DP)) {
    457           FreePool(PathForReturn);
    458           return NULL;
    459         }
    460 
    461         //
    462         // append the path part onto the filepath.
    463         //
    464         ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
    465 
    466         AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePath), FilePath);
    467         if (AlignedNode == NULL) {
    468           FreePool (PathForReturn);
    469           return NULL;
    470         }
    471 
    472         // File Path Device Path Nodes 'can optionally add a "\" separator to
    473         //  the beginning and/or the end of the Path Name string.'
    474         // (UEFI Spec 2.4 section 9.3.6.4).
    475         // If necessary, add a "\", but otherwise don't
    476         // (This is specified in the above section, and also implied by the
    477         //  UEFI Shell spec section 3.7)
    478         if ((PathSize != 0)                        &&
    479             (PathForReturn != NULL)                &&
    480             (PathForReturn[PathSize - 1] != L'\\') &&
    481             (AlignedNode->PathName[0]    != L'\\')) {
    482           PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, L"\\", 1);
    483         }
    484 
    485         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, AlignedNode->PathName, 0);
    486         FreePool(AlignedNode);
    487       } // for loop of remaining nodes
    488     }
    489     if (PathForReturn != NULL) {
    490       break;
    491     }
    492   } // for loop of paths to check
    493   return(PathForReturn);
    494 }
    495 
    496 /**
    497   Converts a file system style name to a device path.
    498 
    499   This function converts a file system style name to a device path, by replacing any
    500   mapping references to the associated device path.
    501 
    502   @param[in] Path               The pointer to the path.
    503 
    504   @return                       The pointer of the file path. The file path is callee
    505                                 allocated and should be freed by the caller.
    506   @retval NULL                  The path could not be found.
    507   @retval NULL                  There was not enough available memory.
    508 **/
    509 EFI_DEVICE_PATH_PROTOCOL *
    510 EFIAPI
    511 EfiShellGetDevicePathFromFilePath(
    512   IN CONST CHAR16 *Path
    513   )
    514 {
    515   CHAR16                          *MapName;
    516   CHAR16                          *NewPath;
    517   CONST CHAR16                    *Cwd;
    518   UINTN                           Size;
    519   CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
    520   EFI_DEVICE_PATH_PROTOCOL        *DevicePathCopy;
    521   EFI_DEVICE_PATH_PROTOCOL        *DevicePathCopyForFree;
    522   EFI_DEVICE_PATH_PROTOCOL        *DevicePathForReturn;
    523   EFI_HANDLE                      Handle;
    524   EFI_STATUS                      Status;
    525 
    526   if (Path == NULL) {
    527     return (NULL);
    528   }
    529 
    530   MapName = NULL;
    531   NewPath = NULL;
    532 
    533   if (StrStr(Path, L":") == NULL) {
    534     Cwd = EfiShellGetCurDir(NULL);
    535     if (Cwd == NULL) {
    536       return (NULL);
    537     }
    538     Size = StrSize(Cwd) + StrSize(Path);
    539     NewPath = AllocateZeroPool(Size);
    540     if (NewPath == NULL) {
    541       return (NULL);
    542     }
    543     StrCpyS(NewPath, Size/sizeof(CHAR16), Cwd);
    544     StrCatS(NewPath, Size/sizeof(CHAR16), L"\\");
    545     if (*Path == L'\\') {
    546       Path++;
    547       while (PathRemoveLastItem(NewPath)) ;
    548     }
    549     StrCatS(NewPath, Size/sizeof(CHAR16), Path);
    550     DevicePathForReturn = EfiShellGetDevicePathFromFilePath(NewPath);
    551     FreePool(NewPath);
    552     return (DevicePathForReturn);
    553   }
    554 
    555   Size = 0;
    556   //
    557   // find the part before (but including) the : for the map name
    558   //
    559   ASSERT((MapName == NULL && Size == 0) || (MapName != NULL));
    560   MapName = StrnCatGrow(&MapName, &Size, Path, (StrStr(Path, L":")-Path+1));
    561   if (MapName == NULL || MapName[StrLen(MapName)-1] != L':') {
    562     return (NULL);
    563   }
    564 
    565   //
    566   // look up the device path in the map
    567   //
    568   DevicePath = EfiShellGetDevicePathFromMap(MapName);
    569   if (DevicePath == NULL) {
    570     //
    571     // Must have been a bad Mapname
    572     //
    573     return (NULL);
    574   }
    575 
    576   //
    577   // make a copy for LocateDevicePath to modify (also save a pointer to call FreePool with)
    578   //
    579   DevicePathCopyForFree = DevicePathCopy = DuplicateDevicePath(DevicePath);
    580   if (DevicePathCopy == NULL) {
    581     FreePool(MapName);
    582     return (NULL);
    583   }
    584 
    585   //
    586   // get the handle
    587   //
    588   ///@todo BlockIo?
    589   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);
    590   if (EFI_ERROR(Status)) {
    591     if (DevicePathCopyForFree != NULL) {
    592       FreePool(DevicePathCopyForFree);
    593     }
    594     FreePool(MapName);
    595     return (NULL);
    596   }
    597 
    598   //
    599   // build the full device path
    600   //
    601   if (*(Path+StrLen(MapName)+1) == CHAR_NULL) {
    602     DevicePathForReturn = FileDevicePath(Handle, L"\\");
    603   } else {
    604     DevicePathForReturn = FileDevicePath(Handle, Path+StrLen(MapName));
    605   }
    606 
    607   FreePool(MapName);
    608   if (DevicePathCopyForFree != NULL) {
    609     FreePool(DevicePathCopyForFree);
    610   }
    611 
    612   return (DevicePathForReturn);
    613 }
    614 
    615 /**
    616   Gets the name of the device specified by the device handle.
    617 
    618   This function gets the user-readable name of the device specified by the device
    619   handle. If no user-readable name could be generated, then *BestDeviceName will be
    620   NULL and EFI_NOT_FOUND will be returned.
    621 
    622   If EFI_DEVICE_NAME_USE_COMPONENT_NAME is set, then the function will return the
    623   device's name using the EFI_COMPONENT_NAME2_PROTOCOL, if present on
    624   DeviceHandle.
    625 
    626   If EFI_DEVICE_NAME_USE_DEVICE_PATH is set, then the function will return the
    627   device's name using the EFI_DEVICE_PATH_PROTOCOL, if present on DeviceHandle.
    628   If both EFI_DEVICE_NAME_USE_COMPONENT_NAME and
    629   EFI_DEVICE_NAME_USE_DEVICE_PATH are set, then
    630   EFI_DEVICE_NAME_USE_COMPONENT_NAME will have higher priority.
    631 
    632   @param DeviceHandle           The handle of the device.
    633   @param Flags                  Determines the possible sources of component names.
    634                                 Valid bits are:
    635                                   EFI_DEVICE_NAME_USE_COMPONENT_NAME
    636                                   EFI_DEVICE_NAME_USE_DEVICE_PATH
    637   @param Language               A pointer to the language specified for the device
    638                                 name, in the same format as described in the UEFI
    639                                 specification, Appendix M
    640   @param BestDeviceName         On return, points to the callee-allocated NULL-
    641                                 terminated name of the device. If no device name
    642                                 could be found, points to NULL. The name must be
    643                                 freed by the caller...
    644 
    645   @retval EFI_SUCCESS           Get the name successfully.
    646   @retval EFI_NOT_FOUND         Fail to get the device name.
    647   @retval EFI_INVALID_PARAMETER Flags did not have a valid bit set.
    648   @retval EFI_INVALID_PARAMETER BestDeviceName was NULL
    649   @retval EFI_INVALID_PARAMETER DeviceHandle was NULL
    650 **/
    651 EFI_STATUS
    652 EFIAPI
    653 EfiShellGetDeviceName(
    654   IN EFI_HANDLE DeviceHandle,
    655   IN EFI_SHELL_DEVICE_NAME_FLAGS Flags,
    656   IN CHAR8 *Language,
    657   OUT CHAR16 **BestDeviceName
    658   )
    659 {
    660   EFI_STATUS                        Status;
    661   EFI_COMPONENT_NAME2_PROTOCOL      *CompName2;
    662   EFI_DEVICE_PATH_PROTOCOL          *DevicePath;
    663   EFI_HANDLE                        *HandleList;
    664   UINTN                             HandleCount;
    665   UINTN                             LoopVar;
    666   CHAR16                            *DeviceNameToReturn;
    667   CHAR8                             *Lang;
    668   UINTN                             ParentControllerCount;
    669   EFI_HANDLE                        *ParentControllerBuffer;
    670   UINTN                             ParentDriverCount;
    671   EFI_HANDLE                        *ParentDriverBuffer;
    672 
    673   if (BestDeviceName == NULL ||
    674       DeviceHandle   == NULL
    675      ){
    676     return (EFI_INVALID_PARAMETER);
    677   }
    678 
    679   //
    680   // make sure one of the 2 supported bits is on
    681   //
    682   if (((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) == 0) &&
    683       ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) == 0)) {
    684     return (EFI_INVALID_PARAMETER);
    685   }
    686 
    687   DeviceNameToReturn  = NULL;
    688   *BestDeviceName     = NULL;
    689   HandleList          = NULL;
    690   HandleCount         = 0;
    691   Lang                = NULL;
    692 
    693   if ((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) != 0) {
    694     Status = ParseHandleDatabaseByRelationship(
    695       NULL,
    696       DeviceHandle,
    697       HR_DRIVER_BINDING_HANDLE|HR_DEVICE_DRIVER,
    698       &HandleCount,
    699       &HandleList);
    700     for (LoopVar = 0; LoopVar < HandleCount ; LoopVar++){
    701       //
    702       // Go through those handles until we get one that passes for GetComponentName
    703       //
    704       Status = gBS->OpenProtocol(
    705         HandleList[LoopVar],
    706         &gEfiComponentName2ProtocolGuid,
    707         (VOID**)&CompName2,
    708         gImageHandle,
    709         NULL,
    710         EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    711       if (EFI_ERROR(Status)) {
    712         Status = gBS->OpenProtocol(
    713           HandleList[LoopVar],
    714           &gEfiComponentNameProtocolGuid,
    715           (VOID**)&CompName2,
    716           gImageHandle,
    717           NULL,
    718           EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    719       }
    720 
    721       if (EFI_ERROR(Status)) {
    722         continue;
    723       }
    724       Lang = GetBestLanguageForDriver(CompName2->SupportedLanguages, Language, FALSE);
    725       Status = CompName2->GetControllerName(CompName2, DeviceHandle, NULL, Lang, &DeviceNameToReturn);
    726       FreePool(Lang);
    727       Lang = NULL;
    728       if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
    729         break;
    730       }
    731     }
    732     if (HandleList != NULL) {
    733       FreePool(HandleList);
    734     }
    735 
    736     //
    737     // Now check the parent controller using this as the child.
    738     //
    739     if (DeviceNameToReturn == NULL){
    740       PARSE_HANDLE_DATABASE_PARENTS(DeviceHandle, &ParentControllerCount, &ParentControllerBuffer);
    741       for (LoopVar = 0 ; LoopVar < ParentControllerCount ; LoopVar++) {
    742         PARSE_HANDLE_DATABASE_UEFI_DRIVERS(ParentControllerBuffer[LoopVar], &ParentDriverCount, &ParentDriverBuffer);
    743         for (HandleCount = 0 ; HandleCount < ParentDriverCount ; HandleCount++) {
    744           //
    745           // try using that driver's component name with controller and our driver as the child.
    746           //
    747           Status = gBS->OpenProtocol(
    748             ParentDriverBuffer[HandleCount],
    749             &gEfiComponentName2ProtocolGuid,
    750             (VOID**)&CompName2,
    751             gImageHandle,
    752             NULL,
    753             EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    754           if (EFI_ERROR(Status)) {
    755             Status = gBS->OpenProtocol(
    756               ParentDriverBuffer[HandleCount],
    757               &gEfiComponentNameProtocolGuid,
    758               (VOID**)&CompName2,
    759               gImageHandle,
    760               NULL,
    761               EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    762           }
    763 
    764           if (EFI_ERROR(Status)) {
    765             continue;
    766           }
    767           Lang = GetBestLanguageForDriver(CompName2->SupportedLanguages, Language, FALSE);
    768           Status = CompName2->GetControllerName(CompName2, ParentControllerBuffer[LoopVar], DeviceHandle, Lang, &DeviceNameToReturn);
    769           FreePool(Lang);
    770           Lang = NULL;
    771           if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
    772             break;
    773           }
    774 
    775 
    776 
    777         }
    778         SHELL_FREE_NON_NULL(ParentDriverBuffer);
    779         if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
    780           break;
    781         }
    782       }
    783       SHELL_FREE_NON_NULL(ParentControllerBuffer);
    784     }
    785     //
    786     // dont return on fail since we will try device path if that bit is on
    787     //
    788     if (DeviceNameToReturn != NULL){
    789       ASSERT(BestDeviceName != NULL);
    790       StrnCatGrow(BestDeviceName, NULL, DeviceNameToReturn, 0);
    791       return (EFI_SUCCESS);
    792     }
    793   }
    794   if ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) != 0) {
    795     Status = gBS->OpenProtocol(
    796       DeviceHandle,
    797       &gEfiDevicePathProtocolGuid,
    798       (VOID**)&DevicePath,
    799       gImageHandle,
    800       NULL,
    801       EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    802     if (!EFI_ERROR(Status)) {
    803       //
    804       // use device path to text on the device path
    805       //
    806       *BestDeviceName = ConvertDevicePathToText(DevicePath, TRUE, TRUE);
    807       return (EFI_SUCCESS);
    808     }
    809   }
    810   //
    811   // none of the selected bits worked.
    812   //
    813   return (EFI_NOT_FOUND);
    814 }
    815 
    816 /**
    817   Opens the root directory of a device on a handle
    818 
    819   This function opens the root directory of a device and returns a file handle to it.
    820 
    821   @param DeviceHandle           The handle of the device that contains the volume.
    822   @param FileHandle             On exit, points to the file handle corresponding to the root directory on the
    823                                 device.
    824 
    825   @retval EFI_SUCCESS           Root opened successfully.
    826   @retval EFI_NOT_FOUND         EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
    827                                 could not be opened.
    828   @retval EFI_VOLUME_CORRUPTED  The data structures in the volume were corrupted.
    829   @retval EFI_DEVICE_ERROR      The device had an error
    830 **/
    831 EFI_STATUS
    832 EFIAPI
    833 EfiShellOpenRootByHandle(
    834   IN EFI_HANDLE DeviceHandle,
    835   OUT SHELL_FILE_HANDLE *FileHandle
    836   )
    837 {
    838   EFI_STATUS                      Status;
    839   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
    840   EFI_FILE_PROTOCOL               *RealFileHandle;
    841   EFI_DEVICE_PATH_PROTOCOL        *DevPath;
    842 
    843   //
    844   // get the simple file system interface
    845   //
    846   Status = gBS->OpenProtocol(DeviceHandle,
    847                              &gEfiSimpleFileSystemProtocolGuid,
    848                              (VOID**)&SimpleFileSystem,
    849                              gImageHandle,
    850                              NULL,
    851                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    852   if (EFI_ERROR(Status)) {
    853     return (EFI_NOT_FOUND);
    854   }
    855 
    856   Status = gBS->OpenProtocol(DeviceHandle,
    857                              &gEfiDevicePathProtocolGuid,
    858                              (VOID**)&DevPath,
    859                              gImageHandle,
    860                              NULL,
    861                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    862   if (EFI_ERROR(Status)) {
    863     return (EFI_NOT_FOUND);
    864   }
    865   //
    866   // Open the root volume now...
    867   //
    868   Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &RealFileHandle);
    869   *FileHandle = ConvertEfiFileProtocolToShellHandle(RealFileHandle, EfiShellGetMapFromDevicePath(&DevPath));
    870   return (Status);
    871 }
    872 
    873 /**
    874   Opens the root directory of a device.
    875 
    876   This function opens the root directory of a device and returns a file handle to it.
    877 
    878   @param DevicePath             Points to the device path corresponding to the device where the
    879                                 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL is installed.
    880   @param FileHandle             On exit, points to the file handle corresponding to the root directory on the
    881                                 device.
    882 
    883   @retval EFI_SUCCESS           Root opened successfully.
    884   @retval EFI_NOT_FOUND         EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
    885                                 could not be opened.
    886   @retval EFI_VOLUME_CORRUPTED  The data structures in the volume were corrupted.
    887   @retval EFI_DEVICE_ERROR      The device had an error
    888   @retval EFI_INVALID_PARAMETER FileHandle is NULL.
    889 **/
    890 EFI_STATUS
    891 EFIAPI
    892 EfiShellOpenRoot(
    893   IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
    894   OUT SHELL_FILE_HANDLE *FileHandle
    895   )
    896 {
    897   EFI_STATUS Status;
    898   EFI_HANDLE Handle;
    899 
    900   if (FileHandle == NULL) {
    901     return (EFI_INVALID_PARAMETER);
    902   }
    903 
    904   //
    905   // find the handle of the device with that device handle and the file system
    906   //
    907   ///@todo BlockIo?
    908   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid,
    909                                  &DevicePath,
    910                                  &Handle);
    911   if (EFI_ERROR(Status)) {
    912     return (EFI_NOT_FOUND);
    913   }
    914 
    915   return (EfiShellOpenRootByHandle(Handle, FileHandle));
    916 }
    917 
    918 /**
    919   Returns whether any script files are currently being processed.
    920 
    921   @retval TRUE                 There is at least one script file active.
    922   @retval FALSE                No script files are active now.
    923 
    924 **/
    925 BOOLEAN
    926 EFIAPI
    927 EfiShellBatchIsActive (
    928   VOID
    929   )
    930 {
    931   if (ShellCommandGetCurrentScriptFile() == NULL) {
    932     return (FALSE);
    933   }
    934   return (TRUE);
    935 }
    936 
    937 /**
    938   Worker function to open a file based on a device path.  this will open the root
    939   of the volume and then traverse down to the file itself.
    940 
    941   @param DevicePath               Device Path of the file.
    942   @param FileHandle               Pointer to the file upon a successful return.
    943   @param OpenMode                 mode to open file in.
    944   @param Attributes               the File Attributes to use when creating a new file.
    945 
    946   @retval EFI_SUCCESS             the file is open and FileHandle is valid
    947   @retval EFI_UNSUPPORTED         the device path cotained non-path elements
    948   @retval other                   an error ocurred.
    949 **/
    950 EFI_STATUS
    951 InternalOpenFileDevicePath(
    952   IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath,
    953   OUT SHELL_FILE_HANDLE           *FileHandle,
    954   IN UINT64                       OpenMode,
    955   IN UINT64                       Attributes OPTIONAL
    956   )
    957 {
    958   EFI_STATUS                      Status;
    959   FILEPATH_DEVICE_PATH            *FilePathNode;
    960   EFI_HANDLE                      Handle;
    961   SHELL_FILE_HANDLE               ShellHandle;
    962   EFI_FILE_PROTOCOL               *Handle1;
    963   EFI_FILE_PROTOCOL               *Handle2;
    964   FILEPATH_DEVICE_PATH            *AlignedNode;
    965 
    966   if (FileHandle == NULL) {
    967     return (EFI_INVALID_PARAMETER);
    968   }
    969   *FileHandle   = NULL;
    970   Handle1       = NULL;
    971   Handle2       = NULL;
    972   Handle        = NULL;
    973   ShellHandle   = NULL;
    974   FilePathNode  = NULL;
    975   AlignedNode   = NULL;
    976 
    977   Status = EfiShellOpenRoot(DevicePath, &ShellHandle);
    978 
    979   if (!EFI_ERROR(Status)) {
    980     Handle1 = ConvertShellHandleToEfiFileProtocol(ShellHandle);
    981     if (Handle1 != NULL) {
    982       //
    983       // chop off the begining part before the file system part...
    984       //
    985       ///@todo BlockIo?
    986       Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid,
    987                                      &DevicePath,
    988                                      &Handle);
    989         if (!EFI_ERROR(Status)) {
    990         //
    991         // To access as a file system, the file path should only
    992         // contain file path components.  Follow the file path nodes
    993         // and find the target file
    994         //
    995         for ( FilePathNode = (FILEPATH_DEVICE_PATH *)DevicePath
    996             ; !IsDevicePathEnd (&FilePathNode->Header)
    997             ; FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode (&FilePathNode->Header)
    998            ){
    999           SHELL_FREE_NON_NULL(AlignedNode);
   1000           AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePathNode), FilePathNode);
   1001           //
   1002           // For file system access each node should be a file path component
   1003           //
   1004           if (DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH ||
   1005               DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP
   1006              ) {
   1007             Status = EFI_UNSUPPORTED;
   1008             break;
   1009           }
   1010 
   1011           //
   1012           // Open this file path node
   1013           //
   1014           Handle2 = Handle1;
   1015           Handle1 = NULL;
   1016 
   1017           //
   1018           // if this is the last node in the DevicePath always create (if that was requested).
   1019           //
   1020           if (IsDevicePathEnd ((NextDevicePathNode (&FilePathNode->Header)))) {
   1021             Status = Handle2->Open (
   1022                                   Handle2,
   1023                                   &Handle1,
   1024                                   AlignedNode->PathName,
   1025                                   OpenMode,
   1026                                   Attributes
   1027                                  );
   1028           } else {
   1029 
   1030             //
   1031             //  This is not the last node and we dont want to 'create' existing
   1032             //  directory entries...
   1033             //
   1034 
   1035             //
   1036             // open without letting it create
   1037             // prevents error on existing files/directories
   1038             //
   1039             Status = Handle2->Open (
   1040                                   Handle2,
   1041                                   &Handle1,
   1042                                   AlignedNode->PathName,
   1043                                   OpenMode &~EFI_FILE_MODE_CREATE,
   1044                                   Attributes
   1045                                  );
   1046             //
   1047             // if above failed now open and create the 'item'
   1048             // if OpenMode EFI_FILE_MODE_CREATE bit was on (but disabled above)
   1049             //
   1050             if ((EFI_ERROR (Status)) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) {
   1051               Status = Handle2->Open (
   1052                                     Handle2,
   1053                                     &Handle1,
   1054                                     AlignedNode->PathName,
   1055                                     OpenMode,
   1056                                     Attributes
   1057                                    );
   1058             }
   1059           }
   1060           //
   1061           // Close the last node
   1062           //
   1063           ShellInfoObject.NewEfiShellProtocol->CloseFile (Handle2);
   1064 
   1065           //
   1066           // If there's been an error, stop
   1067           //
   1068           if (EFI_ERROR (Status)) {
   1069             break;
   1070           }
   1071         } // for loop
   1072       }
   1073     }
   1074   }
   1075   SHELL_FREE_NON_NULL(AlignedNode);
   1076   if (EFI_ERROR(Status)) {
   1077     if (Handle1 != NULL) {
   1078       ShellInfoObject.NewEfiShellProtocol->CloseFile(Handle1);
   1079     }
   1080   } else {
   1081     *FileHandle = ConvertEfiFileProtocolToShellHandle(Handle1, ShellFileHandleGetPath(ShellHandle));
   1082   }
   1083   return (Status);
   1084 }
   1085 
   1086 /**
   1087   Creates a file or directory by name.
   1088 
   1089   This function creates an empty new file or directory with the specified attributes and
   1090   returns the new file's handle. If the file already exists and is read-only, then
   1091   EFI_INVALID_PARAMETER will be returned.
   1092 
   1093   If the file already existed, it is truncated and its attributes updated. If the file is
   1094   created successfully, the FileHandle is the file's handle, else, the FileHandle is NULL.
   1095 
   1096   If the file name begins with >v, then the file handle which is returned refers to the
   1097   shell environment variable with the specified name. If the shell environment variable
   1098   already exists and is non-volatile then EFI_INVALID_PARAMETER is returned.
   1099 
   1100   @param FileName           Pointer to NULL-terminated file path
   1101   @param FileAttribs        The new file's attrbiutes.  the different attributes are
   1102                             described in EFI_FILE_PROTOCOL.Open().
   1103   @param FileHandle         On return, points to the created file handle or directory's handle
   1104 
   1105   @retval EFI_SUCCESS       The file was opened.  FileHandle points to the new file's handle.
   1106   @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
   1107   @retval EFI_UNSUPPORTED   could not open the file path
   1108   @retval EFI_NOT_FOUND     the specified file could not be found on the devide, or could not
   1109                             file the file system on the device.
   1110   @retval EFI_NO_MEDIA      the device has no medium.
   1111   @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
   1112                             longer supported.
   1113   @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
   1114                             the DirName.
   1115   @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
   1116   @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
   1117                             when the media is write-protected.
   1118   @retval EFI_ACCESS_DENIED The service denied access to the file.
   1119   @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
   1120   @retval EFI_VOLUME_FULL   The volume is full.
   1121 **/
   1122 EFI_STATUS
   1123 EFIAPI
   1124 EfiShellCreateFile(
   1125   IN CONST CHAR16       *FileName,
   1126   IN UINT64             FileAttribs,
   1127   OUT SHELL_FILE_HANDLE *FileHandle
   1128   )
   1129 {
   1130   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
   1131   EFI_STATUS                Status;
   1132   BOOLEAN                   Volatile;
   1133 
   1134   //
   1135   // Is this for an environment variable
   1136   // do we start with >v
   1137   //
   1138   if (StrStr(FileName, L">v") == FileName) {
   1139     Status = IsVolatileEnv (FileName + 2, &Volatile);
   1140     if (EFI_ERROR (Status)) {
   1141       return Status;
   1142     }
   1143     if (!Volatile) {
   1144       return (EFI_INVALID_PARAMETER);
   1145     }
   1146     *FileHandle = CreateFileInterfaceEnv(FileName+2);
   1147     return (EFI_SUCCESS);
   1148   }
   1149 
   1150   //
   1151   // We are opening a regular file.
   1152   //
   1153   DevicePath = EfiShellGetDevicePathFromFilePath(FileName);
   1154   if (DevicePath == NULL) {
   1155     return (EFI_NOT_FOUND);
   1156   }
   1157 
   1158   Status = InternalOpenFileDevicePath(DevicePath, FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, FileAttribs);
   1159   FreePool(DevicePath);
   1160 
   1161   return(Status);
   1162 }
   1163 
   1164 /**
   1165   Register a GUID and a localized human readable name for it.
   1166 
   1167   If Guid is not assigned a name, then assign GuidName to Guid.  This list of GUID
   1168   names must be used whenever a shell command outputs GUID information.
   1169 
   1170   This function is only available when the major and minor versions in the
   1171   EfiShellProtocol are greater than or equal to 2 and 1, respectively.
   1172 
   1173   @param[in] Guid       A pointer to the GUID being registered.
   1174   @param[in] GuidName   A pointer to the localized name for the GUID being registered.
   1175 
   1176   @retval EFI_SUCCESS             The operation was successful.
   1177   @retval EFI_INVALID_PARAMETER   Guid was NULL.
   1178   @retval EFI_INVALID_PARAMETER   GuidName was NULL.
   1179   @retval EFI_ACCESS_DENIED       Guid already is assigned a name.
   1180 **/
   1181 EFI_STATUS
   1182 EFIAPI
   1183 EfiShellRegisterGuidName(
   1184   IN CONST EFI_GUID *Guid,
   1185   IN CONST CHAR16   *GuidName
   1186   )
   1187 {
   1188   return (AddNewGuidNameMapping(Guid, GuidName, NULL));
   1189 }
   1190 
   1191 /**
   1192   Opens a file or a directory by file name.
   1193 
   1194   This function opens the specified file in the specified OpenMode and returns a file
   1195   handle.
   1196   If the file name begins with >v, then the file handle which is returned refers to the
   1197   shell environment variable with the specified name. If the shell environment variable
   1198   exists, is non-volatile and the OpenMode indicates EFI_FILE_MODE_WRITE, then
   1199   EFI_INVALID_PARAMETER is returned.
   1200 
   1201   If the file name is >i, then the file handle which is returned refers to the standard
   1202   input. If the OpenMode indicates EFI_FILE_MODE_WRITE, then EFI_INVALID_PARAMETER
   1203   is returned.
   1204 
   1205   If the file name is >o, then the file handle which is returned refers to the standard
   1206   output. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
   1207   is returned.
   1208 
   1209   If the file name is >e, then the file handle which is returned refers to the standard
   1210   error. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
   1211   is returned.
   1212 
   1213   If the file name is NUL, then the file handle that is returned refers to the standard NUL
   1214   file. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER is
   1215   returned.
   1216 
   1217   If return EFI_SUCCESS, the FileHandle is the opened file's handle, else, the
   1218   FileHandle is NULL.
   1219 
   1220   @param FileName               Points to the NULL-terminated UCS-2 encoded file name.
   1221   @param FileHandle             On return, points to the file handle.
   1222   @param OpenMode               File open mode. Either EFI_FILE_MODE_READ or
   1223                                 EFI_FILE_MODE_WRITE from section 12.4 of the UEFI
   1224                                 Specification.
   1225   @retval EFI_SUCCESS           The file was opened. FileHandle has the opened file's handle.
   1226   @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. FileHandle is NULL.
   1227   @retval EFI_UNSUPPORTED       Could not open the file path. FileHandle is NULL.
   1228   @retval EFI_NOT_FOUND         The specified file could not be found on the device or the file
   1229                                 system could not be found on the device. FileHandle is NULL.
   1230   @retval EFI_NO_MEDIA          The device has no medium. FileHandle is NULL.
   1231   @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the medium is no
   1232                                 longer supported. FileHandle is NULL.
   1233   @retval EFI_DEVICE_ERROR      The device reported an error or can't get the file path according
   1234                                 the FileName. FileHandle is NULL.
   1235   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted. FileHandle is NULL.
   1236   @retval EFI_WRITE_PROTECTED   An attempt was made to create a file, or open a file for write
   1237                                 when the media is write-protected. FileHandle is NULL.
   1238   @retval EFI_ACCESS_DENIED     The service denied access to the file. FileHandle is NULL.
   1239   @retval EFI_OUT_OF_RESOURCES  Not enough resources were available to open the file. FileHandle
   1240                                 is NULL.
   1241   @retval EFI_VOLUME_FULL       The volume is full. FileHandle is NULL.
   1242 **/
   1243 EFI_STATUS
   1244 EFIAPI
   1245 EfiShellOpenFileByName(
   1246   IN CONST CHAR16       *FileName,
   1247   OUT SHELL_FILE_HANDLE *FileHandle,
   1248   IN UINT64             OpenMode
   1249   )
   1250 {
   1251   EFI_DEVICE_PATH_PROTOCOL        *DevicePath;
   1252   EFI_STATUS                      Status;
   1253   BOOLEAN                         Volatile;
   1254 
   1255   *FileHandle = NULL;
   1256 
   1257   //
   1258   // Is this for StdIn
   1259   //
   1260   if (StrCmp(FileName, L">i") == 0) {
   1261     //
   1262     // make sure not writing to StdIn
   1263     //
   1264     if ((OpenMode & EFI_FILE_MODE_WRITE) != 0) {
   1265       return (EFI_INVALID_PARAMETER);
   1266     }
   1267     *FileHandle = ShellInfoObject.NewShellParametersProtocol->StdIn;
   1268     ASSERT(*FileHandle != NULL);
   1269     return (EFI_SUCCESS);
   1270   }
   1271 
   1272   //
   1273   // Is this for StdOut
   1274   //
   1275   if (StrCmp(FileName, L">o") == 0) {
   1276     //
   1277     // make sure not writing to StdIn
   1278     //
   1279     if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
   1280       return (EFI_INVALID_PARAMETER);
   1281     }
   1282     *FileHandle = &FileInterfaceStdOut;
   1283     return (EFI_SUCCESS);
   1284   }
   1285 
   1286   //
   1287   // Is this for NUL / NULL file
   1288   //
   1289   if ((gUnicodeCollation->StriColl (gUnicodeCollation, (CHAR16*)FileName, L"NUL") == 0) ||
   1290       (gUnicodeCollation->StriColl (gUnicodeCollation, (CHAR16*)FileName, L"NULL") == 0)) {
   1291     *FileHandle = &FileInterfaceNulFile;
   1292     return (EFI_SUCCESS);
   1293   }
   1294 
   1295   //
   1296   // Is this for StdErr
   1297   //
   1298   if (StrCmp(FileName, L">e") == 0) {
   1299     //
   1300     // make sure not writing to StdIn
   1301     //
   1302     if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
   1303       return (EFI_INVALID_PARAMETER);
   1304     }
   1305     *FileHandle = &FileInterfaceStdErr;
   1306     return (EFI_SUCCESS);
   1307   }
   1308 
   1309   //
   1310   // Is this for an environment variable
   1311   // do we start with >v
   1312   //
   1313   if (StrStr(FileName, L">v") == FileName) {
   1314     Status = IsVolatileEnv (FileName + 2, &Volatile);
   1315     if (EFI_ERROR (Status)) {
   1316       return Status;
   1317     }
   1318     if (!Volatile &&
   1319         ((OpenMode & EFI_FILE_MODE_WRITE) != 0)) {
   1320       return (EFI_INVALID_PARAMETER);
   1321     }
   1322     *FileHandle = CreateFileInterfaceEnv(FileName+2);
   1323     return (EFI_SUCCESS);
   1324   }
   1325 
   1326   //
   1327   // We are opening a regular file.
   1328   //
   1329   DevicePath = EfiShellGetDevicePathFromFilePath(FileName);
   1330 //  DEBUG_CODE(InternalShellProtocolDebugPrintMessage (NULL, DevicePath););
   1331   if (DevicePath == NULL) {
   1332     return (EFI_NOT_FOUND);
   1333   }
   1334 
   1335   //
   1336   // Copy the device path, open the file, then free the memory
   1337   //
   1338   Status = InternalOpenFileDevicePath(DevicePath, FileHandle, OpenMode, 0); // 0 = no specific file attributes
   1339   FreePool(DevicePath);
   1340 
   1341   return(Status);
   1342 }
   1343 
   1344 /**
   1345   Deletes the file specified by the file name.
   1346 
   1347   This function deletes a file.
   1348 
   1349   @param FileName                 Points to the NULL-terminated file name.
   1350 
   1351   @retval EFI_SUCCESS             The file was closed and deleted, and the handle was closed.
   1352   @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted.
   1353   @sa EfiShellCreateFile
   1354 **/
   1355 EFI_STATUS
   1356 EFIAPI
   1357 EfiShellDeleteFileByName(
   1358   IN CONST CHAR16 *FileName
   1359   )
   1360 {
   1361   SHELL_FILE_HANDLE FileHandle;
   1362   EFI_STATUS        Status;
   1363 
   1364   FileHandle = NULL;
   1365 
   1366   //
   1367   // get a handle to the file
   1368   //
   1369   Status = EfiShellCreateFile(FileName,
   1370                               0,
   1371                               &FileHandle);
   1372   if (EFI_ERROR(Status)) {
   1373     return (Status);
   1374   }
   1375   //
   1376   // now delete the file
   1377   //
   1378   ShellFileHandleRemove(FileHandle);
   1379   return (ShellInfoObject.NewEfiShellProtocol->DeleteFile(FileHandle));
   1380 }
   1381 
   1382 /**
   1383   Disables the page break output mode.
   1384 **/
   1385 VOID
   1386 EFIAPI
   1387 EfiShellDisablePageBreak (
   1388   VOID
   1389   )
   1390 {
   1391   ShellInfoObject.PageBreakEnabled = FALSE;
   1392 }
   1393 
   1394 /**
   1395   Enables the page break output mode.
   1396 **/
   1397 VOID
   1398 EFIAPI
   1399 EfiShellEnablePageBreak (
   1400   VOID
   1401   )
   1402 {
   1403   ShellInfoObject.PageBreakEnabled = TRUE;
   1404 }
   1405 
   1406 /**
   1407   internal worker function to load and run an image via device path.
   1408 
   1409   @param ParentImageHandle      A handle of the image that is executing the specified
   1410                                 command line.
   1411   @param DevicePath             device path of the file to execute
   1412   @param CommandLine            Points to the NULL-terminated UCS-2 encoded string
   1413                                 containing the command line. If NULL then the command-
   1414                                 line will be empty.
   1415   @param Environment            Points to a NULL-terminated array of environment
   1416                                 variables with the format 'x=y', where x is the
   1417                                 environment variable name and y is the value. If this
   1418                                 is NULL, then the current shell environment is used.
   1419 
   1420   @param[out] StartImageStatus  Returned status from gBS->StartImage.
   1421 
   1422   @retval EFI_SUCCESS       The command executed successfully. The  status code
   1423                             returned by the command is pointed to by StatusCode.
   1424   @retval EFI_INVALID_PARAMETER The parameters are invalid.
   1425   @retval EFI_OUT_OF_RESOURCES Out of resources.
   1426   @retval EFI_UNSUPPORTED   Nested shell invocations are not allowed.
   1427 **/
   1428 EFI_STATUS
   1429 InternalShellExecuteDevicePath(
   1430   IN CONST EFI_HANDLE               *ParentImageHandle,
   1431   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
   1432   IN CONST CHAR16                   *CommandLine OPTIONAL,
   1433   IN CONST CHAR16                   **Environment OPTIONAL,
   1434   OUT EFI_STATUS                    *StartImageStatus OPTIONAL
   1435   )
   1436 {
   1437   EFI_STATUS                    Status;
   1438   EFI_STATUS                    StartStatus;
   1439   EFI_STATUS                    CleanupStatus;
   1440   EFI_HANDLE                    NewHandle;
   1441   EFI_LOADED_IMAGE_PROTOCOL     *LoadedImage;
   1442   LIST_ENTRY                    OrigEnvs;
   1443   EFI_SHELL_PARAMETERS_PROTOCOL ShellParamsProtocol;
   1444   CHAR16                        *ImagePath;
   1445   UINTN                         Index;
   1446   CHAR16                        *Walker;
   1447   CHAR16                        *NewCmdLine;
   1448 
   1449   if (ParentImageHandle == NULL) {
   1450     return (EFI_INVALID_PARAMETER);
   1451   }
   1452 
   1453   InitializeListHead(&OrigEnvs);
   1454   ZeroMem(&ShellParamsProtocol, sizeof(EFI_SHELL_PARAMETERS_PROTOCOL));
   1455 
   1456   NewHandle = NULL;
   1457 
   1458   NewCmdLine = AllocateCopyPool (StrSize (CommandLine), CommandLine);
   1459   if (NewCmdLine == NULL) {
   1460     return EFI_OUT_OF_RESOURCES;
   1461   }
   1462 
   1463   for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL ; Walker++) {
   1464     if (*Walker == L'^' && *(Walker+1) == L'#') {
   1465       CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0]));
   1466     }
   1467   }
   1468 
   1469   //
   1470   // Load the image with:
   1471   // FALSE - not from boot manager and NULL, 0 being not already in memory
   1472   //
   1473   Status = gBS->LoadImage(
   1474     FALSE,
   1475     *ParentImageHandle,
   1476     (EFI_DEVICE_PATH_PROTOCOL*)DevicePath,
   1477     NULL,
   1478     0,
   1479     &NewHandle);
   1480 
   1481   if (EFI_ERROR(Status)) {
   1482     if (NewHandle != NULL) {
   1483       gBS->UnloadImage(NewHandle);
   1484     }
   1485     FreePool (NewCmdLine);
   1486     return (Status);
   1487   }
   1488   Status = gBS->OpenProtocol(
   1489     NewHandle,
   1490     &gEfiLoadedImageProtocolGuid,
   1491     (VOID**)&LoadedImage,
   1492     gImageHandle,
   1493     NULL,
   1494     EFI_OPEN_PROTOCOL_GET_PROTOCOL);
   1495 
   1496   if (!EFI_ERROR(Status)) {
   1497     //
   1498     // If the image is not an app abort it.
   1499     //
   1500     if (LoadedImage->ImageCodeType != EfiLoaderCode){
   1501       ShellPrintHiiEx(
   1502         -1,
   1503         -1,
   1504         NULL,
   1505         STRING_TOKEN (STR_SHELL_IMAGE_NOT_APP),
   1506         ShellInfoObject.HiiHandle
   1507       );
   1508       goto UnloadImage;
   1509     }
   1510 
   1511     ASSERT(LoadedImage->LoadOptionsSize == 0);
   1512     if (NewCmdLine != NULL) {
   1513       LoadedImage->LoadOptionsSize  = (UINT32)StrSize(NewCmdLine);
   1514       LoadedImage->LoadOptions      = (VOID*)NewCmdLine;
   1515     }
   1516 
   1517     //
   1518     // Save our current environment settings for later restoration if necessary
   1519     //
   1520     if (Environment != NULL) {
   1521       Status = GetEnvironmentVariableList(&OrigEnvs);
   1522       if (!EFI_ERROR(Status)) {
   1523         Status = SetEnvironmentVariables(Environment);
   1524       }
   1525     }
   1526 
   1527     //
   1528     // Initialize and install a shell parameters protocol on the image.
   1529     //
   1530     ShellParamsProtocol.StdIn   = ShellInfoObject.NewShellParametersProtocol->StdIn;
   1531     ShellParamsProtocol.StdOut  = ShellInfoObject.NewShellParametersProtocol->StdOut;
   1532     ShellParamsProtocol.StdErr  = ShellInfoObject.NewShellParametersProtocol->StdErr;
   1533     Status = UpdateArgcArgv(&ShellParamsProtocol, NewCmdLine, Efi_Application, NULL, NULL);
   1534     ASSERT_EFI_ERROR(Status);
   1535     //
   1536     // Replace Argv[0] with the full path of the binary we're executing:
   1537     // If the command line was "foo", the binary might be called "foo.efi".
   1538     // "The first entry in [Argv] is always the full file path of the
   1539     //  executable" - UEFI Shell Spec section 2.3
   1540     //
   1541     ImagePath = EfiShellGetFilePathFromDevicePath (DevicePath);
   1542     // The image we're executing isn't necessarily in a filesystem - it might
   1543     // be memory mapped. In this case EfiShellGetFilePathFromDevicePath will
   1544     // return NULL, and we'll leave Argv[0] as UpdateArgcArgv set it.
   1545     if (ImagePath != NULL) {
   1546       if (ShellParamsProtocol.Argv == NULL) {
   1547         // Command line was empty or null.
   1548         // (UpdateArgcArgv sets Argv to NULL when CommandLine is "" or NULL)
   1549         ShellParamsProtocol.Argv = AllocatePool (sizeof (CHAR16 *));
   1550         if (ShellParamsProtocol.Argv == NULL) {
   1551           Status = EFI_OUT_OF_RESOURCES;
   1552           goto UnloadImage;
   1553         }
   1554         ShellParamsProtocol.Argc = 1;
   1555       } else {
   1556         // Free the string UpdateArgcArgv put in Argv[0];
   1557         FreePool (ShellParamsProtocol.Argv[0]);
   1558       }
   1559       ShellParamsProtocol.Argv[0] = ImagePath;
   1560     }
   1561 
   1562     Status = gBS->InstallProtocolInterface(&NewHandle, &gEfiShellParametersProtocolGuid, EFI_NATIVE_INTERFACE, &ShellParamsProtocol);
   1563     ASSERT_EFI_ERROR(Status);
   1564 
   1565     ///@todo initialize and install ShellInterface protocol on the new image for compatibility if - PcdGetBool(PcdShellSupportOldProtocols)
   1566 
   1567     //
   1568     // now start the image and if the caller wanted the return code pass it to them...
   1569     //
   1570     if (!EFI_ERROR(Status)) {
   1571       StartStatus      = gBS->StartImage(
   1572                           NewHandle,
   1573                           0,
   1574                           NULL
   1575                           );
   1576       if (StartImageStatus != NULL) {
   1577         *StartImageStatus = StartStatus;
   1578       }
   1579 
   1580       CleanupStatus = gBS->UninstallProtocolInterface(
   1581                             NewHandle,
   1582                             &gEfiShellParametersProtocolGuid,
   1583                             &ShellParamsProtocol
   1584                             );
   1585       ASSERT_EFI_ERROR(CleanupStatus);
   1586 
   1587       goto FreeAlloc;
   1588     }
   1589 
   1590 UnloadImage:
   1591     // Unload image - We should only get here if we didn't call StartImage
   1592     gBS->UnloadImage (NewHandle);
   1593 
   1594 FreeAlloc:
   1595     // Free Argv (Allocated in UpdateArgcArgv)
   1596     if (ShellParamsProtocol.Argv != NULL) {
   1597       for (Index = 0; Index < ShellParamsProtocol.Argc; Index++) {
   1598         if (ShellParamsProtocol.Argv[Index] != NULL) {
   1599           FreePool (ShellParamsProtocol.Argv[Index]);
   1600         }
   1601       }
   1602       FreePool (ShellParamsProtocol.Argv);
   1603     }
   1604   }
   1605 
   1606   // Restore environment variables
   1607   if (!IsListEmpty(&OrigEnvs)) {
   1608     CleanupStatus = SetEnvironmentVariableList(&OrigEnvs);
   1609     ASSERT_EFI_ERROR (CleanupStatus);
   1610   }
   1611 
   1612   FreePool (NewCmdLine);
   1613 
   1614   return(Status);
   1615 }
   1616 
   1617 /**
   1618   internal worker function to load and run an image in the current shell.
   1619 
   1620   @param CommandLine            Points to the NULL-terminated UCS-2 encoded string
   1621                                 containing the command line. If NULL then the command-
   1622                                 line will be empty.
   1623   @param Environment            Points to a NULL-terminated array of environment
   1624                                 variables with the format 'x=y', where x is the
   1625                                 environment variable name and y is the value. If this
   1626                                 is NULL, then the current shell environment is used.
   1627 
   1628   @param[out] StartImageStatus  Returned status from the command line.
   1629 
   1630   @retval EFI_SUCCESS       The command executed successfully. The  status code
   1631                             returned by the command is pointed to by StatusCode.
   1632   @retval EFI_INVALID_PARAMETER The parameters are invalid.
   1633   @retval EFI_OUT_OF_RESOURCES Out of resources.
   1634   @retval EFI_UNSUPPORTED   Nested shell invocations are not allowed.
   1635 **/
   1636 EFI_STATUS
   1637 InternalShellExecute(
   1638   IN CONST CHAR16                   *CommandLine OPTIONAL,
   1639   IN CONST CHAR16                   **Environment OPTIONAL,
   1640   OUT EFI_STATUS                    *StartImageStatus OPTIONAL
   1641   )
   1642 {
   1643   EFI_STATUS                    Status;
   1644   EFI_STATUS                    CleanupStatus;
   1645   LIST_ENTRY                    OrigEnvs;
   1646 
   1647   InitializeListHead(&OrigEnvs);
   1648 
   1649   //
   1650   // Save our current environment settings for later restoration if necessary
   1651   //
   1652   if (Environment != NULL) {
   1653     Status = GetEnvironmentVariableList(&OrigEnvs);
   1654     if (!EFI_ERROR(Status)) {
   1655       Status = SetEnvironmentVariables(Environment);
   1656     } else {
   1657       return Status;
   1658     }
   1659   }
   1660 
   1661   Status = RunShellCommand(CommandLine, StartImageStatus);
   1662 
   1663   // Restore environment variables
   1664   if (!IsListEmpty(&OrigEnvs)) {
   1665     CleanupStatus = SetEnvironmentVariableList(&OrigEnvs);
   1666     ASSERT_EFI_ERROR (CleanupStatus);
   1667   }
   1668 
   1669   return(Status);
   1670 }
   1671 
   1672 /**
   1673   Determine if the UEFI Shell is currently running with nesting enabled or disabled.
   1674 
   1675   @retval FALSE   nesting is required
   1676   @retval other   nesting is enabled
   1677 **/
   1678 STATIC
   1679 BOOLEAN
   1680 NestingEnabled(
   1681 )
   1682 {
   1683   EFI_STATUS  Status;
   1684   CHAR16      *Temp;
   1685   CHAR16      *Temp2;
   1686   UINTN       TempSize;
   1687   BOOLEAN     RetVal;
   1688 
   1689   RetVal = TRUE;
   1690   Temp   = NULL;
   1691   Temp2  = NULL;
   1692 
   1693   if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) {
   1694     TempSize = 0;
   1695     Temp     = NULL;
   1696     Status = SHELL_GET_ENVIRONMENT_VARIABLE(mNoNestingEnvVarName, &TempSize, Temp);
   1697     if (Status == EFI_BUFFER_TOO_SMALL) {
   1698       Temp = AllocateZeroPool(TempSize + sizeof(CHAR16));
   1699       if (Temp != NULL) {
   1700         Status = SHELL_GET_ENVIRONMENT_VARIABLE(mNoNestingEnvVarName, &TempSize, Temp);
   1701       }
   1702     }
   1703     Temp2 = StrnCatGrow(&Temp2, NULL, mNoNestingTrue, 0);
   1704     if (Temp != NULL && Temp2 != NULL && StringNoCaseCompare(&Temp, &Temp2) == 0) {
   1705       //
   1706       // Use the no nesting method.
   1707       //
   1708       RetVal = FALSE;
   1709     }
   1710   }
   1711 
   1712   SHELL_FREE_NON_NULL(Temp);
   1713   SHELL_FREE_NON_NULL(Temp2);
   1714   return (RetVal);
   1715 }
   1716 
   1717 /**
   1718   Execute the command line.
   1719 
   1720   This function creates a nested instance of the shell and executes the specified
   1721   command (CommandLine) with the specified environment (Environment). Upon return,
   1722   the status code returned by the specified command is placed in StatusCode.
   1723 
   1724   If Environment is NULL, then the current environment is used and all changes made
   1725   by the commands executed will be reflected in the current environment. If the
   1726   Environment is non-NULL, then the changes made will be discarded.
   1727 
   1728   The CommandLine is executed from the current working directory on the current
   1729   device.
   1730 
   1731   @param ParentImageHandle  A handle of the image that is executing the specified
   1732                             command line.
   1733   @param CommandLine        Points to the NULL-terminated UCS-2 encoded string
   1734                             containing the command line. If NULL then the command-
   1735                             line will be empty.
   1736   @param Environment        Points to a NULL-terminated array of environment
   1737                             variables with the format 'x=y', where x is the
   1738                             environment variable name and y is the value. If this
   1739                             is NULL, then the current shell environment is used.
   1740   @param StatusCode         Points to the status code returned by the CommandLine.
   1741 
   1742   @retval EFI_SUCCESS       The command executed successfully. The  status code
   1743                             returned by the command is pointed to by StatusCode.
   1744   @retval EFI_INVALID_PARAMETER The parameters are invalid.
   1745   @retval EFI_OUT_OF_RESOURCES Out of resources.
   1746   @retval EFI_UNSUPPORTED   Nested shell invocations are not allowed.
   1747   @retval EFI_UNSUPPORTED   The support level required for this function is not present.
   1748 
   1749   @sa InternalShellExecuteDevicePath
   1750 **/
   1751 EFI_STATUS
   1752 EFIAPI
   1753 EfiShellExecute(
   1754   IN EFI_HANDLE *ParentImageHandle,
   1755   IN CHAR16 *CommandLine OPTIONAL,
   1756   IN CHAR16 **Environment OPTIONAL,
   1757   OUT EFI_STATUS *StatusCode OPTIONAL
   1758   )
   1759 {
   1760   EFI_STATUS                Status;
   1761   CHAR16                    *Temp;
   1762   EFI_DEVICE_PATH_PROTOCOL  *DevPath;
   1763   UINTN                     Size;
   1764 
   1765   if ((PcdGet8(PcdShellSupportLevel) < 1)) {
   1766     return (EFI_UNSUPPORTED);
   1767   }
   1768 
   1769   if (NestingEnabled()) {
   1770     DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
   1771 
   1772     DEBUG_CODE_BEGIN();
   1773     Temp = ConvertDevicePathToText(ShellInfoObject.FileDevPath, TRUE, TRUE);
   1774     FreePool(Temp);
   1775     Temp = ConvertDevicePathToText(ShellInfoObject.ImageDevPath, TRUE, TRUE);
   1776     FreePool(Temp);
   1777     Temp = ConvertDevicePathToText(DevPath, TRUE, TRUE);
   1778     FreePool(Temp);
   1779     DEBUG_CODE_END();
   1780 
   1781     Temp = NULL;
   1782     Size = 0;
   1783     ASSERT((Temp == NULL && Size == 0) || (Temp != NULL));
   1784     StrnCatGrow(&Temp, &Size, L"Shell.efi -_exit ", 0);
   1785     StrnCatGrow(&Temp, &Size, CommandLine, 0);
   1786 
   1787     Status = InternalShellExecuteDevicePath(
   1788       ParentImageHandle,
   1789       DevPath,
   1790       Temp,
   1791       (CONST CHAR16**)Environment,
   1792       StatusCode);
   1793 
   1794     //
   1795     // de-allocate and return
   1796     //
   1797     FreePool(DevPath);
   1798     FreePool(Temp);
   1799   } else {
   1800     Status = InternalShellExecute(
   1801       (CONST CHAR16*)CommandLine,
   1802       (CONST CHAR16**)Environment,
   1803       StatusCode);
   1804   }
   1805 
   1806   return(Status);
   1807 }
   1808 
   1809 /**
   1810   Utility cleanup function for EFI_SHELL_FILE_INFO objects.
   1811 
   1812   1) frees all pointers (non-NULL)
   1813   2) Closes the SHELL_FILE_HANDLE
   1814 
   1815   @param FileListNode     pointer to the list node to free
   1816 **/
   1817 VOID
   1818 InternalFreeShellFileInfoNode(
   1819   IN EFI_SHELL_FILE_INFO *FileListNode
   1820   )
   1821 {
   1822   if (FileListNode->Info != NULL) {
   1823     FreePool((VOID*)FileListNode->Info);
   1824   }
   1825   if (FileListNode->FileName != NULL) {
   1826     FreePool((VOID*)FileListNode->FileName);
   1827   }
   1828   if (FileListNode->FullName != NULL) {
   1829     FreePool((VOID*)FileListNode->FullName);
   1830   }
   1831   if (FileListNode->Handle != NULL) {
   1832     ShellInfoObject.NewEfiShellProtocol->CloseFile(FileListNode->Handle);
   1833   }
   1834   FreePool(FileListNode);
   1835 }
   1836 /**
   1837   Frees the file list.
   1838 
   1839   This function cleans up the file list and any related data structures. It has no
   1840   impact on the files themselves.
   1841 
   1842   @param FileList               The file list to free. Type EFI_SHELL_FILE_INFO is
   1843                                 defined in OpenFileList()
   1844 
   1845   @retval EFI_SUCCESS           Free the file list successfully.
   1846   @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
   1847 **/
   1848 EFI_STATUS
   1849 EFIAPI
   1850 EfiShellFreeFileList(
   1851   IN EFI_SHELL_FILE_INFO **FileList
   1852   )
   1853 {
   1854   EFI_SHELL_FILE_INFO *ShellFileListItem;
   1855 
   1856   if (FileList == NULL || *FileList == NULL) {
   1857     return (EFI_INVALID_PARAMETER);
   1858   }
   1859 
   1860   for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
   1861       ; !IsListEmpty(&(*FileList)->Link)
   1862       ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
   1863      ){
   1864     RemoveEntryList(&ShellFileListItem->Link);
   1865     InternalFreeShellFileInfoNode(ShellFileListItem);
   1866   }
   1867   InternalFreeShellFileInfoNode(*FileList);
   1868   *FileList = NULL;
   1869   return(EFI_SUCCESS);
   1870 }
   1871 
   1872 /**
   1873   Deletes the duplicate file names files in the given file list.
   1874 
   1875   This function deletes the reduplicate files in the given file list.
   1876 
   1877   @param FileList               A pointer to the first entry in the file list.
   1878 
   1879   @retval EFI_SUCCESS           Always success.
   1880   @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
   1881 **/
   1882 EFI_STATUS
   1883 EFIAPI
   1884 EfiShellRemoveDupInFileList(
   1885   IN EFI_SHELL_FILE_INFO **FileList
   1886   )
   1887 {
   1888   EFI_SHELL_FILE_INFO *ShellFileListItem;
   1889   EFI_SHELL_FILE_INFO *ShellFileListItem2;
   1890   EFI_SHELL_FILE_INFO *TempNode;
   1891 
   1892   if (FileList == NULL || *FileList == NULL) {
   1893     return (EFI_INVALID_PARAMETER);
   1894   }
   1895   for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
   1896       ; !IsNull(&(*FileList)->Link, &ShellFileListItem->Link)
   1897       ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
   1898      ){
   1899     for ( ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
   1900         ; !IsNull(&(*FileList)->Link, &ShellFileListItem2->Link)
   1901         ; ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem2->Link)
   1902        ){
   1903       if (gUnicodeCollation->StriColl(
   1904             gUnicodeCollation,
   1905             (CHAR16*)ShellFileListItem->FullName,
   1906             (CHAR16*)ShellFileListItem2->FullName) == 0
   1907          ){
   1908         TempNode = (EFI_SHELL_FILE_INFO *)GetPreviousNode(
   1909                                             &(*FileList)->Link,
   1910                                             &ShellFileListItem2->Link
   1911                                             );
   1912         RemoveEntryList(&ShellFileListItem2->Link);
   1913         InternalFreeShellFileInfoNode(ShellFileListItem2);
   1914         // Set ShellFileListItem2 to PreviousNode so we don't access Freed
   1915         // memory in GetNextNode in the loop expression above.
   1916         ShellFileListItem2 = TempNode;
   1917       }
   1918     }
   1919   }
   1920   return (EFI_SUCCESS);
   1921 }
   1922 
   1923 //
   1924 // This is the same structure as the external version, but it has no CONST qualifiers.
   1925 //
   1926 typedef struct {
   1927   LIST_ENTRY        Link;       ///< Linked list members.
   1928   EFI_STATUS        Status;     ///< Status of opening the file.  Valid only if Handle != NULL.
   1929         CHAR16      *FullName;  ///< Fully qualified filename.
   1930         CHAR16      *FileName;  ///< name of this file.
   1931   SHELL_FILE_HANDLE Handle;     ///< Handle for interacting with the opened file or NULL if closed.
   1932   EFI_FILE_INFO     *Info;      ///< Pointer to the FileInfo struct for this file or NULL.
   1933 } EFI_SHELL_FILE_INFO_NO_CONST;
   1934 
   1935 /**
   1936   Allocates and duplicates a EFI_SHELL_FILE_INFO node.
   1937 
   1938   @param[in] Node     The node to copy from.
   1939   @param[in] Save     TRUE to set Node->Handle to NULL, FALSE otherwise.
   1940 
   1941   @retval NULL        a memory allocation error ocurred
   1942   @return != NULL     a pointer to the new node
   1943 **/
   1944 EFI_SHELL_FILE_INFO*
   1945 InternalDuplicateShellFileInfo(
   1946   IN       EFI_SHELL_FILE_INFO *Node,
   1947   IN BOOLEAN                   Save
   1948   )
   1949 {
   1950   EFI_SHELL_FILE_INFO_NO_CONST *NewNode;
   1951 
   1952   //
   1953   // try to confirm that the objects are in sync
   1954   //
   1955   ASSERT(sizeof(EFI_SHELL_FILE_INFO_NO_CONST) == sizeof(EFI_SHELL_FILE_INFO));
   1956 
   1957   NewNode = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
   1958   if (NewNode == NULL) {
   1959     return (NULL);
   1960   }
   1961   NewNode->FullName = AllocateCopyPool(StrSize(Node->FullName), Node->FullName);
   1962   NewNode->FileName = AllocateCopyPool(StrSize(Node->FileName), Node->FileName);
   1963   NewNode->Info     = AllocateCopyPool((UINTN)Node->Info->Size, Node->Info);
   1964   if ( NewNode->FullName == NULL
   1965     || NewNode->FileName == NULL
   1966     || NewNode->Info == NULL
   1967   ){
   1968     SHELL_FREE_NON_NULL(NewNode->FullName);
   1969     SHELL_FREE_NON_NULL(NewNode->FileName);
   1970     SHELL_FREE_NON_NULL(NewNode->Info);
   1971     SHELL_FREE_NON_NULL(NewNode);
   1972     return(NULL);
   1973   }
   1974   NewNode->Status = Node->Status;
   1975   NewNode->Handle = Node->Handle;
   1976   if (!Save) {
   1977     Node->Handle = NULL;
   1978   }
   1979 
   1980   return((EFI_SHELL_FILE_INFO*)NewNode);
   1981 }
   1982 
   1983 /**
   1984   Allocates and populates a EFI_SHELL_FILE_INFO structure.  if any memory operation
   1985   failed it will return NULL.
   1986 
   1987   @param[in] BasePath         the Path to prepend onto filename for FullPath
   1988   @param[in] Status           Status member initial value.
   1989   @param[in] FileName         FileName member initial value.
   1990   @param[in] Handle           Handle member initial value.
   1991   @param[in] Info             Info struct to copy.
   1992 
   1993   @retval NULL                An error ocurred.
   1994   @return                     a pointer to the newly allocated structure.
   1995 **/
   1996 EFI_SHELL_FILE_INFO *
   1997 CreateAndPopulateShellFileInfo(
   1998   IN CONST CHAR16 *BasePath,
   1999   IN CONST EFI_STATUS Status,
   2000   IN CONST CHAR16 *FileName,
   2001   IN CONST SHELL_FILE_HANDLE Handle,
   2002   IN CONST EFI_FILE_INFO *Info
   2003   )
   2004 {
   2005   EFI_SHELL_FILE_INFO *ShellFileListItem;
   2006   CHAR16              *TempString;
   2007   UINTN               Size;
   2008 
   2009   TempString = NULL;
   2010   Size = 0;
   2011 
   2012   ShellFileListItem = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
   2013   if (ShellFileListItem == NULL) {
   2014     return (NULL);
   2015   }
   2016   if (Info != NULL && Info->Size != 0) {
   2017     ShellFileListItem->Info = AllocateZeroPool((UINTN)Info->Size);
   2018     if (ShellFileListItem->Info == NULL) {
   2019       FreePool(ShellFileListItem);
   2020       return (NULL);
   2021     }
   2022     CopyMem(ShellFileListItem->Info, Info, (UINTN)Info->Size);
   2023   } else {
   2024     ShellFileListItem->Info = NULL;
   2025   }
   2026   if (FileName != NULL) {
   2027     ASSERT(TempString == NULL);
   2028     ShellFileListItem->FileName = StrnCatGrow(&TempString, 0, FileName, 0);
   2029     if (ShellFileListItem->FileName == NULL) {
   2030       FreePool(ShellFileListItem->Info);
   2031       FreePool(ShellFileListItem);
   2032       return (NULL);
   2033     }
   2034   } else {
   2035     ShellFileListItem->FileName = NULL;
   2036   }
   2037   Size = 0;
   2038   TempString = NULL;
   2039   if (BasePath != NULL) {
   2040     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
   2041     TempString = StrnCatGrow(&TempString, &Size, BasePath, 0);
   2042     if (TempString == NULL) {
   2043       FreePool((VOID*)ShellFileListItem->FileName);
   2044       SHELL_FREE_NON_NULL(ShellFileListItem->Info);
   2045       FreePool(ShellFileListItem);
   2046       return (NULL);
   2047     }
   2048   }
   2049   if (ShellFileListItem->FileName != NULL) {
   2050     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
   2051     TempString = StrnCatGrow(&TempString, &Size, ShellFileListItem->FileName, 0);
   2052     if (TempString == NULL) {
   2053       FreePool((VOID*)ShellFileListItem->FileName);
   2054       FreePool(ShellFileListItem->Info);
   2055       FreePool(ShellFileListItem);
   2056       return (NULL);
   2057     }
   2058   }
   2059 
   2060   TempString = PathCleanUpDirectories(TempString);
   2061 
   2062   ShellFileListItem->FullName = TempString;
   2063   ShellFileListItem->Status   = Status;
   2064   ShellFileListItem->Handle   = Handle;
   2065 
   2066   return (ShellFileListItem);
   2067 }
   2068 
   2069 /**
   2070   Find all files in a specified directory.
   2071 
   2072   @param FileDirHandle          Handle of the directory to search.
   2073   @param FileList               On return, points to the list of files in the directory
   2074                                 or NULL if there are no files in the directory.
   2075 
   2076   @retval EFI_SUCCESS           File information was returned successfully.
   2077   @retval EFI_VOLUME_CORRUPTED  The file system structures have been corrupted.
   2078   @retval EFI_DEVICE_ERROR      The device reported an error.
   2079   @retval EFI_NO_MEDIA          The device media is not present.
   2080   @retval EFI_INVALID_PARAMETER The FileDirHandle was not a directory.
   2081   @return                       An error from FileHandleGetFileName().
   2082 **/
   2083 EFI_STATUS
   2084 EFIAPI
   2085 EfiShellFindFilesInDir(
   2086   IN SHELL_FILE_HANDLE FileDirHandle,
   2087   OUT EFI_SHELL_FILE_INFO **FileList
   2088   )
   2089 {
   2090   EFI_SHELL_FILE_INFO       *ShellFileList;
   2091   EFI_SHELL_FILE_INFO       *ShellFileListItem;
   2092   EFI_FILE_INFO             *FileInfo;
   2093   EFI_STATUS                Status;
   2094   BOOLEAN                   NoFile;
   2095   CHAR16                    *TempString;
   2096   CHAR16                    *BasePath;
   2097   UINTN                     Size;
   2098   CHAR16                    *TempSpot;
   2099 
   2100   BasePath = NULL;
   2101   Status = FileHandleGetFileName(FileDirHandle, &BasePath);
   2102   if (EFI_ERROR(Status)) {
   2103     return (Status);
   2104   }
   2105 
   2106   if (ShellFileHandleGetPath(FileDirHandle) != NULL) {
   2107     TempString        = NULL;
   2108     Size              = 0;
   2109     TempString        = StrnCatGrow(&TempString, &Size, ShellFileHandleGetPath(FileDirHandle), 0);
   2110     if (TempString == NULL) {
   2111       SHELL_FREE_NON_NULL(BasePath);
   2112       return (EFI_OUT_OF_RESOURCES);
   2113     }
   2114     TempSpot          = StrStr(TempString, L";");
   2115 
   2116     if (TempSpot != NULL) {
   2117       *TempSpot = CHAR_NULL;
   2118     }
   2119 
   2120     TempString        = StrnCatGrow(&TempString, &Size, BasePath, 0);
   2121     if (TempString == NULL) {
   2122       SHELL_FREE_NON_NULL(BasePath);
   2123       return (EFI_OUT_OF_RESOURCES);
   2124     }
   2125     SHELL_FREE_NON_NULL(BasePath);
   2126     BasePath          = TempString;
   2127   }
   2128 
   2129   NoFile            = FALSE;
   2130   ShellFileList     = NULL;
   2131   ShellFileListItem = NULL;
   2132   FileInfo          = NULL;
   2133   Status            = EFI_SUCCESS;
   2134 
   2135 
   2136   for ( Status = FileHandleFindFirstFile(FileDirHandle, &FileInfo)
   2137       ; !EFI_ERROR(Status) && !NoFile
   2138       ; Status = FileHandleFindNextFile(FileDirHandle, FileInfo, &NoFile)
   2139      ){
   2140     if (ShellFileList == NULL) {
   2141       ShellFileList = (EFI_SHELL_FILE_INFO*)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
   2142       if (ShellFileList == NULL) {
   2143         SHELL_FREE_NON_NULL (BasePath);
   2144         return EFI_OUT_OF_RESOURCES;
   2145       }
   2146       InitializeListHead(&ShellFileList->Link);
   2147     }
   2148     //
   2149     // allocate a new EFI_SHELL_FILE_INFO and populate it...
   2150     //
   2151     ShellFileListItem = CreateAndPopulateShellFileInfo(
   2152       BasePath,
   2153       EFI_SUCCESS,  // success since we didnt fail to open it...
   2154       FileInfo->FileName,
   2155       NULL,         // no handle since not open
   2156       FileInfo);
   2157     if (ShellFileListItem == NULL) {
   2158       Status = EFI_OUT_OF_RESOURCES;
   2159       //
   2160       // Free resources outside the loop.
   2161       //
   2162       break;
   2163     }
   2164     InsertTailList(&ShellFileList->Link, &ShellFileListItem->Link);
   2165   }
   2166   if (EFI_ERROR(Status)) {
   2167     EfiShellFreeFileList(&ShellFileList);
   2168     *FileList = NULL;
   2169   } else {
   2170     *FileList = ShellFileList;
   2171   }
   2172   SHELL_FREE_NON_NULL(BasePath);
   2173   return(Status);
   2174 }
   2175 
   2176 /**
   2177   Get the GUID value from a human readable name.
   2178 
   2179   If GuidName is a known GUID name, then update Guid to have the correct value for
   2180   that GUID.
   2181 
   2182   This function is only available when the major and minor versions in the
   2183   EfiShellProtocol are greater than or equal to 2 and 1, respectively.
   2184 
   2185   @param[in]  GuidName   A pointer to the localized name for the GUID being queried.
   2186   @param[out] Guid       A pointer to the GUID structure to be filled in.
   2187 
   2188   @retval EFI_SUCCESS             The operation was successful.
   2189   @retval EFI_INVALID_PARAMETER   Guid was NULL.
   2190   @retval EFI_INVALID_PARAMETER   GuidName was NULL.
   2191   @retval EFI_NOT_FOUND           GuidName is not a known GUID Name.
   2192 **/
   2193 EFI_STATUS
   2194 EFIAPI
   2195 EfiShellGetGuidFromName(
   2196   IN  CONST CHAR16   *GuidName,
   2197   OUT       EFI_GUID *Guid
   2198   )
   2199 {
   2200   EFI_GUID    *NewGuid;
   2201   EFI_STATUS  Status;
   2202 
   2203   if (Guid == NULL || GuidName == NULL) {
   2204     return (EFI_INVALID_PARAMETER);
   2205   }
   2206 
   2207   Status = GetGuidFromStringName(GuidName, NULL, &NewGuid);
   2208 
   2209   if (!EFI_ERROR(Status)) {
   2210     CopyGuid(Guid, NewGuid);
   2211   }
   2212 
   2213   return (Status);
   2214 }
   2215 
   2216 /**
   2217   Get the human readable name for a GUID from the value.
   2218 
   2219   If Guid is assigned a name, then update *GuidName to point to the name. The callee
   2220   should not modify the value.
   2221 
   2222   This function is only available when the major and minor versions in the
   2223   EfiShellProtocol are greater than or equal to 2 and 1, respectively.
   2224 
   2225   @param[in]  Guid       A pointer to the GUID being queried.
   2226   @param[out] GuidName   A pointer to a pointer the localized to name for the GUID being requested
   2227 
   2228   @retval EFI_SUCCESS             The operation was successful.
   2229   @retval EFI_INVALID_PARAMETER   Guid was NULL.
   2230   @retval EFI_INVALID_PARAMETER   GuidName was NULL.
   2231   @retval EFI_NOT_FOUND           Guid is not assigned a name.
   2232 **/
   2233 EFI_STATUS
   2234 EFIAPI
   2235 EfiShellGetGuidName(
   2236   IN  CONST EFI_GUID *Guid,
   2237   OUT CONST CHAR16   **GuidName
   2238   )
   2239 {
   2240   CHAR16   *Name;
   2241 
   2242   if (Guid == NULL || GuidName == NULL) {
   2243     return (EFI_INVALID_PARAMETER);
   2244   }
   2245 
   2246   Name = GetStringNameFromGuid(Guid, NULL);
   2247   if (Name == NULL || StrLen(Name) == 0) {
   2248     SHELL_FREE_NON_NULL(Name);
   2249     return (EFI_NOT_FOUND);
   2250   }
   2251 
   2252   *GuidName = AddBufferToFreeList(Name);
   2253 
   2254   return (EFI_SUCCESS);
   2255 }
   2256 
   2257 /**
   2258   Updates a file name to be preceeded by the mapped drive name
   2259 
   2260   @param[in] BasePath      the Mapped drive name to prepend
   2261   @param[in, out] Path     pointer to pointer to the file name to update.
   2262 
   2263   @retval EFI_SUCCESS
   2264   @retval EFI_OUT_OF_RESOURCES
   2265 **/
   2266 EFI_STATUS
   2267 UpdateFileName(
   2268   IN CONST CHAR16 *BasePath,
   2269   IN OUT CHAR16   **Path
   2270   )
   2271 {
   2272   CHAR16              *Path2;
   2273   UINTN               Path2Size;
   2274 
   2275   Path2Size = 0;
   2276   Path2 = NULL;
   2277 
   2278   ASSERT(Path      != NULL);
   2279   ASSERT(*Path     != NULL);
   2280   ASSERT(BasePath  != NULL);
   2281 
   2282   //
   2283   // convert a local path to an absolute path
   2284   //
   2285   if (StrStr(*Path, L":") == NULL) {
   2286     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
   2287     StrnCatGrow(&Path2, &Path2Size, BasePath, 0);
   2288     if (Path2 == NULL) {
   2289       return (EFI_OUT_OF_RESOURCES);
   2290     }
   2291     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
   2292     StrnCatGrow(&Path2, &Path2Size, (*Path)[0] == L'\\'?(*Path) + 1 :*Path, 0);
   2293     if (Path2 == NULL) {
   2294       return (EFI_OUT_OF_RESOURCES);
   2295     }
   2296   }
   2297 
   2298   FreePool(*Path);
   2299   (*Path) = Path2;
   2300 
   2301   return (EFI_SUCCESS);
   2302 }
   2303 
   2304 /**
   2305   If FileHandle is a directory then the function reads from FileHandle and reads in
   2306   each of the FileInfo structures.  If one of them matches the Pattern's first
   2307   "level" then it opens that handle and calls itself on that handle.
   2308 
   2309   If FileHandle is a file and matches all of the remaining Pattern (which would be
   2310   on its last node), then add a EFI_SHELL_FILE_INFO object for this file to fileList.
   2311 
   2312   Upon a EFI_SUCCESS return fromt he function any the caller is responsible to call
   2313   FreeFileList with FileList.
   2314 
   2315   @param[in] FilePattern         The FilePattern to check against.
   2316   @param[in] UnicodeCollation    The pointer to EFI_UNICODE_COLLATION_PROTOCOL structure
   2317   @param[in] FileHandle          The FileHandle to start with
   2318   @param[in, out] FileList       pointer to pointer to list of found files.
   2319   @param[in] ParentNode          The node for the parent. Same file as identified by HANDLE.
   2320   @param[in] MapName             The file system name this file is on.
   2321 
   2322   @retval EFI_SUCCESS           all files were found and the FileList contains a list.
   2323   @retval EFI_NOT_FOUND         no files were found
   2324   @retval EFI_OUT_OF_RESOURCES  a memory allocation failed
   2325 **/
   2326 EFI_STATUS
   2327 ShellSearchHandle(
   2328   IN     CONST CHAR16                         *FilePattern,
   2329   IN           EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation,
   2330   IN           SHELL_FILE_HANDLE              FileHandle,
   2331   IN OUT       EFI_SHELL_FILE_INFO            **FileList,
   2332   IN     CONST EFI_SHELL_FILE_INFO            *ParentNode OPTIONAL,
   2333   IN     CONST CHAR16                         *MapName
   2334   )
   2335 {
   2336   EFI_STATUS          Status;
   2337   CONST CHAR16        *NextFilePatternStart;
   2338   CHAR16              *CurrentFilePattern;
   2339   EFI_SHELL_FILE_INFO *ShellInfo;
   2340   EFI_SHELL_FILE_INFO *ShellInfoNode;
   2341   EFI_SHELL_FILE_INFO *NewShellNode;
   2342   EFI_FILE_INFO       *FileInfo;
   2343   BOOLEAN             Directory;
   2344   CHAR16              *NewFullName;
   2345   UINTN               Size;
   2346 
   2347   if ( FilePattern      == NULL
   2348     || UnicodeCollation == NULL
   2349     || FileList         == NULL
   2350    ){
   2351     return (EFI_INVALID_PARAMETER);
   2352   }
   2353   ShellInfo = NULL;
   2354   CurrentFilePattern = NULL;
   2355 
   2356   if (*FilePattern == L'\\') {
   2357     FilePattern++;
   2358   }
   2359 
   2360   for( NextFilePatternStart = FilePattern
   2361      ; *NextFilePatternStart != CHAR_NULL && *NextFilePatternStart != L'\\'
   2362      ; NextFilePatternStart++);
   2363 
   2364   CurrentFilePattern = AllocateZeroPool((NextFilePatternStart-FilePattern+1)*sizeof(CHAR16));
   2365   if (CurrentFilePattern == NULL) {
   2366     return EFI_OUT_OF_RESOURCES;
   2367   }
   2368 
   2369   StrnCpyS(CurrentFilePattern, NextFilePatternStart-FilePattern+1, FilePattern, NextFilePatternStart-FilePattern);
   2370 
   2371   if (CurrentFilePattern[0]   == CHAR_NULL
   2372     &&NextFilePatternStart[0] == CHAR_NULL
   2373     ){
   2374     //
   2375     // we want the parent or root node (if no parent)
   2376     //
   2377     if (ParentNode == NULL) {
   2378       //
   2379       // We want the root node.  create the node.
   2380       //
   2381       FileInfo = FileHandleGetInfo(FileHandle);
   2382       NewShellNode = CreateAndPopulateShellFileInfo(
   2383         MapName,
   2384         EFI_SUCCESS,
   2385         L"\\",
   2386         FileHandle,
   2387         FileInfo
   2388         );
   2389       SHELL_FREE_NON_NULL(FileInfo);
   2390     } else {
   2391       //
   2392       // Add the current parameter FileHandle to the list, then end...
   2393       //
   2394       NewShellNode = InternalDuplicateShellFileInfo((EFI_SHELL_FILE_INFO*)ParentNode, TRUE);
   2395     }
   2396     if (NewShellNode == NULL) {
   2397       Status = EFI_OUT_OF_RESOURCES;
   2398     } else {
   2399       NewShellNode->Handle = NULL;
   2400       if (*FileList == NULL) {
   2401         *FileList = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
   2402         InitializeListHead(&((*FileList)->Link));
   2403       }
   2404 
   2405       //
   2406       // Add to the returning to use list
   2407       //
   2408       InsertTailList(&(*FileList)->Link, &NewShellNode->Link);
   2409 
   2410       Status = EFI_SUCCESS;
   2411     }
   2412   } else {
   2413     Status = EfiShellFindFilesInDir(FileHandle, &ShellInfo);
   2414 
   2415     if (!EFI_ERROR(Status)){
   2416       if (StrStr(NextFilePatternStart, L"\\") != NULL){
   2417         Directory = TRUE;
   2418       } else {
   2419         Directory = FALSE;
   2420       }
   2421       for ( ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetFirstNode(&ShellInfo->Link)
   2422           ; !IsNull (&ShellInfo->Link, &ShellInfoNode->Link)
   2423           ; ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetNextNode(&ShellInfo->Link, &ShellInfoNode->Link)
   2424          ){
   2425         if (UnicodeCollation->MetaiMatch(UnicodeCollation, (CHAR16*)ShellInfoNode->FileName, CurrentFilePattern)){
   2426           if (ShellInfoNode->FullName != NULL && StrStr(ShellInfoNode->FullName, L":") == NULL) {
   2427             Size = StrSize (ShellInfoNode->FullName) + StrSize (MapName);
   2428             NewFullName = AllocateZeroPool(Size);
   2429             if (NewFullName == NULL) {
   2430               Status = EFI_OUT_OF_RESOURCES;
   2431             } else {
   2432               StrCpyS(NewFullName, Size / sizeof(CHAR16), MapName);
   2433               StrCatS(NewFullName, Size / sizeof(CHAR16), ShellInfoNode->FullName);
   2434               FreePool ((VOID *) ShellInfoNode->FullName);
   2435               ShellInfoNode->FullName = NewFullName;
   2436             }
   2437           }
   2438           if (Directory && !EFI_ERROR(Status) && ShellInfoNode->FullName != NULL && ShellInfoNode->FileName != NULL){
   2439             //
   2440             // should be a directory
   2441             //
   2442 
   2443             //
   2444             // don't open the . and .. directories
   2445             //
   2446             if ( (StrCmp(ShellInfoNode->FileName, L".") != 0)
   2447               && (StrCmp(ShellInfoNode->FileName, L"..") != 0)
   2448              ){
   2449               //
   2450               //
   2451               //
   2452               if (EFI_ERROR(Status)) {
   2453                 break;
   2454               }
   2455               //
   2456               // Open the directory since we need that handle in the next recursion.
   2457               //
   2458               ShellInfoNode->Status = EfiShellOpenFileByName (ShellInfoNode->FullName, &ShellInfoNode->Handle, EFI_FILE_MODE_READ);
   2459 
   2460               //
   2461               // recurse with the next part of the pattern
   2462               //
   2463               Status = ShellSearchHandle(NextFilePatternStart, UnicodeCollation, ShellInfoNode->Handle, FileList, ShellInfoNode, MapName);
   2464               EfiShellClose(ShellInfoNode->Handle);
   2465               ShellInfoNode->Handle = NULL;
   2466             }
   2467           } else if (!EFI_ERROR(Status)) {
   2468             //
   2469             // should be a file
   2470             //
   2471 
   2472             //
   2473             // copy the information we need into a new Node
   2474             //
   2475             NewShellNode = InternalDuplicateShellFileInfo(ShellInfoNode, FALSE);
   2476             if (NewShellNode == NULL) {
   2477               Status = EFI_OUT_OF_RESOURCES;
   2478             }
   2479             if (*FileList == NULL) {
   2480               *FileList = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
   2481               InitializeListHead(&((*FileList)->Link));
   2482             }
   2483 
   2484             //
   2485             // Add to the returning to use list
   2486             //
   2487             InsertTailList(&(*FileList)->Link, &NewShellNode->Link);
   2488           }
   2489         }
   2490         if (EFI_ERROR(Status)) {
   2491           break;
   2492         }
   2493       }
   2494       if (EFI_ERROR(Status)) {
   2495         EfiShellFreeFileList(&ShellInfo);
   2496       } else {
   2497         Status = EfiShellFreeFileList(&ShellInfo);
   2498       }
   2499     }
   2500   }
   2501 
   2502   if (*FileList == NULL || (*FileList != NULL && IsListEmpty(&(*FileList)->Link))) {
   2503     Status = EFI_NOT_FOUND;
   2504   }
   2505 
   2506   FreePool(CurrentFilePattern);
   2507   return (Status);
   2508 }
   2509 
   2510 /**
   2511   Find files that match a specified pattern.
   2512 
   2513   This function searches for all files and directories that match the specified
   2514   FilePattern. The FilePattern can contain wild-card characters. The resulting file
   2515   information is placed in the file list FileList.
   2516 
   2517   Wildcards are processed
   2518   according to the rules specified in UEFI Shell 2.0 spec section 3.7.1.
   2519 
   2520   The files in the file list are not opened. The OpenMode field is set to 0 and the FileInfo
   2521   field is set to NULL.
   2522 
   2523   if *FileList is not NULL then it must be a pre-existing and properly initialized list.
   2524 
   2525   @param FilePattern      Points to a NULL-terminated shell file path, including wildcards.
   2526   @param FileList         On return, points to the start of a file list containing the names
   2527                           of all matching files or else points to NULL if no matching files
   2528                           were found.  only on a EFI_SUCCESS return will; this be non-NULL.
   2529 
   2530   @retval EFI_SUCCESS           Files found.  FileList is a valid list.
   2531   @retval EFI_NOT_FOUND         No files found.
   2532   @retval EFI_NO_MEDIA          The device has no media
   2533   @retval EFI_DEVICE_ERROR      The device reported an error
   2534   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted
   2535 **/
   2536 EFI_STATUS
   2537 EFIAPI
   2538 EfiShellFindFiles(
   2539   IN CONST CHAR16 *FilePattern,
   2540   OUT EFI_SHELL_FILE_INFO **FileList
   2541   )
   2542 {
   2543   EFI_STATUS                      Status;
   2544   CHAR16                          *PatternCopy;
   2545   CHAR16                          *PatternCurrentLocation;
   2546   EFI_DEVICE_PATH_PROTOCOL        *RootDevicePath;
   2547   SHELL_FILE_HANDLE               RootFileHandle;
   2548   CHAR16                          *MapName;
   2549   UINTN                           Count;
   2550 
   2551   if ( FilePattern      == NULL
   2552     || FileList         == NULL
   2553     || StrStr(FilePattern, L":") == NULL
   2554    ){
   2555     return (EFI_INVALID_PARAMETER);
   2556   }
   2557   Status = EFI_SUCCESS;
   2558   RootDevicePath = NULL;
   2559   RootFileHandle = NULL;
   2560   MapName        = NULL;
   2561   PatternCopy = AllocateCopyPool(StrSize(FilePattern), FilePattern);
   2562   if (PatternCopy == NULL) {
   2563     return (EFI_OUT_OF_RESOURCES);
   2564   }
   2565 
   2566   PatternCopy = PathCleanUpDirectories(PatternCopy);
   2567 
   2568   Count = StrStr(PatternCopy, L":") - PatternCopy + 1;
   2569   ASSERT (Count <= StrLen (PatternCopy));
   2570 
   2571   ASSERT(MapName == NULL);
   2572   MapName = StrnCatGrow(&MapName, NULL, PatternCopy, Count);
   2573   if (MapName == NULL) {
   2574     Status = EFI_OUT_OF_RESOURCES;
   2575   } else {
   2576     RootDevicePath = EfiShellGetDevicePathFromFilePath(PatternCopy);
   2577     if (RootDevicePath == NULL) {
   2578       Status = EFI_INVALID_PARAMETER;
   2579     } else {
   2580       Status = EfiShellOpenRoot(RootDevicePath, &RootFileHandle);
   2581       if (!EFI_ERROR(Status)) {
   2582         for ( PatternCurrentLocation = PatternCopy
   2583             ; *PatternCurrentLocation != ':'
   2584             ; PatternCurrentLocation++);
   2585         PatternCurrentLocation++;
   2586         Status = ShellSearchHandle(PatternCurrentLocation, gUnicodeCollation, RootFileHandle, FileList, NULL, MapName);
   2587         EfiShellClose(RootFileHandle);
   2588       }
   2589       FreePool(RootDevicePath);
   2590     }
   2591   }
   2592 
   2593   SHELL_FREE_NON_NULL(PatternCopy);
   2594   SHELL_FREE_NON_NULL(MapName);
   2595 
   2596   return(Status);
   2597 }
   2598 
   2599 /**
   2600   Opens the files that match the path specified.
   2601 
   2602   This function opens all of the files specified by Path. Wildcards are processed
   2603   according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. Each
   2604   matching file has an EFI_SHELL_FILE_INFO structure created in a linked list.
   2605 
   2606   @param Path                   A pointer to the path string.
   2607   @param OpenMode               Specifies the mode used to open each file, EFI_FILE_MODE_READ or
   2608                                 EFI_FILE_MODE_WRITE.
   2609   @param FileList               Points to the start of a list of files opened.
   2610 
   2611   @retval EFI_SUCCESS           Create the file list successfully.
   2612   @return Others                Can't create the file list.
   2613 **/
   2614 EFI_STATUS
   2615 EFIAPI
   2616 EfiShellOpenFileList(
   2617   IN CHAR16 *Path,
   2618   IN UINT64 OpenMode,
   2619   IN OUT EFI_SHELL_FILE_INFO **FileList
   2620   )
   2621 {
   2622   EFI_STATUS Status;
   2623   EFI_SHELL_FILE_INFO *ShellFileListItem;
   2624   CHAR16              *Path2;
   2625   UINTN               Path2Size;
   2626   CONST CHAR16        *CurDir;
   2627   BOOLEAN             Found;
   2628 
   2629   PathCleanUpDirectories(Path);
   2630 
   2631   Path2Size     = 0;
   2632   Path2         = NULL;
   2633 
   2634   if (FileList == NULL || *FileList == NULL) {
   2635     return (EFI_INVALID_PARAMETER);
   2636   }
   2637 
   2638   if (*Path == L'.' && *(Path+1) == L'\\') {
   2639     Path+=2;
   2640   }
   2641 
   2642   //
   2643   // convert a local path to an absolute path
   2644   //
   2645   if (StrStr(Path, L":") == NULL) {
   2646     CurDir = EfiShellGetCurDir(NULL);
   2647     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
   2648     StrnCatGrow(&Path2, &Path2Size, CurDir, 0);
   2649     StrnCatGrow(&Path2, &Path2Size, L"\\", 0);
   2650     if (*Path == L'\\') {
   2651       Path++;
   2652       while (PathRemoveLastItem(Path2)) ;
   2653     }
   2654     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
   2655     StrnCatGrow(&Path2, &Path2Size, Path, 0);
   2656   } else {
   2657     ASSERT(Path2 == NULL);
   2658     StrnCatGrow(&Path2, NULL, Path, 0);
   2659   }
   2660 
   2661   PathCleanUpDirectories (Path2);
   2662 
   2663   //
   2664   // do the search
   2665   //
   2666   Status = EfiShellFindFiles(Path2, FileList);
   2667 
   2668   FreePool(Path2);
   2669 
   2670   if (EFI_ERROR(Status)) {
   2671     return (Status);
   2672   }
   2673 
   2674   Found = FALSE;
   2675   //
   2676   // We had no errors so open all the files (that are not already opened...)
   2677   //
   2678   for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
   2679       ; !IsNull(&(*FileList)->Link, &ShellFileListItem->Link)
   2680       ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
   2681      ){
   2682     if (ShellFileListItem->Status == 0 && ShellFileListItem->Handle == NULL) {
   2683       ShellFileListItem->Status = EfiShellOpenFileByName (ShellFileListItem->FullName, &ShellFileListItem->Handle, OpenMode);
   2684       Found = TRUE;
   2685     }
   2686   }
   2687 
   2688   if (!Found) {
   2689     return (EFI_NOT_FOUND);
   2690   }
   2691   return(EFI_SUCCESS);
   2692 }
   2693 
   2694 /**
   2695   Gets the environment variable and Attributes, or list of environment variables.  Can be
   2696   used instead of GetEnv().
   2697 
   2698   This function returns the current value of the specified environment variable and
   2699   the Attributes. If no variable name was specified, then all of the known
   2700   variables will be returned.
   2701 
   2702   @param[in] Name               A pointer to the environment variable name. If Name is NULL,
   2703                                 then the function will return all of the defined shell
   2704                                 environment variables. In the case where multiple environment
   2705                                 variables are being returned, each variable will be terminated
   2706                                 by a NULL, and the list will be terminated by a double NULL.
   2707   @param[out] Attributes        If not NULL, a pointer to the returned attributes bitmask for
   2708                                 the environment variable. In the case where Name is NULL, and
   2709                                 multiple environment variables are being returned, Attributes
   2710                                 is undefined.
   2711 
   2712   @retval NULL                  The environment variable doesn't exist.
   2713   @return                       A non-NULL value points to the variable's value. The returned
   2714                                 pointer does not need to be freed by the caller.
   2715 **/
   2716 CONST CHAR16 *
   2717 EFIAPI
   2718 EfiShellGetEnvEx(
   2719   IN  CONST CHAR16 *Name,
   2720   OUT       UINT32 *Attributes OPTIONAL
   2721   )
   2722 {
   2723   EFI_STATUS  Status;
   2724   VOID        *Buffer;
   2725   UINTN       Size;
   2726   ENV_VAR_LIST *Node;
   2727   CHAR16      *CurrentWriteLocation;
   2728 
   2729   Size = 0;
   2730   Buffer = NULL;
   2731 
   2732   if (Name == NULL) {
   2733 
   2734     //
   2735     // Build the semi-colon delimited list. (2 passes)
   2736     //
   2737     for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
   2738       ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
   2739       ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node->Link)
   2740      ){
   2741       ASSERT(Node->Key != NULL);
   2742       Size += StrSize(Node->Key);
   2743     }
   2744 
   2745     Size += 2*sizeof(CHAR16);
   2746 
   2747     Buffer = AllocateZeroPool(Size);
   2748     if (Buffer == NULL) {
   2749       return (NULL);
   2750     }
   2751     CurrentWriteLocation = (CHAR16*)Buffer;
   2752 
   2753     for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
   2754       ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
   2755       ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node->Link)
   2756      ){
   2757       ASSERT(Node->Key != NULL);
   2758       StrCpyS( CurrentWriteLocation,
   2759                 (Size)/sizeof(CHAR16) - (CurrentWriteLocation - ((CHAR16*)Buffer)),
   2760                 Node->Key
   2761                 );
   2762       CurrentWriteLocation += StrLen(CurrentWriteLocation) + 1;
   2763     }
   2764 
   2765   } else {
   2766     //
   2767     // We are doing a specific environment variable
   2768     //
   2769     Status = ShellFindEnvVarInList(Name, (CHAR16**)&Buffer, &Size, Attributes);
   2770 
   2771     if (EFI_ERROR(Status)){
   2772       //
   2773       // get the size we need for this EnvVariable
   2774       //
   2775       Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(Name, Attributes, &Size, Buffer);
   2776       if (Status == EFI_BUFFER_TOO_SMALL) {
   2777         //
   2778         // Allocate the space and recall the get function
   2779         //
   2780         Buffer = AllocateZeroPool(Size);
   2781         Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(Name, Attributes, &Size, Buffer);
   2782       }
   2783       //
   2784       // we didnt get it (might not exist)
   2785       // free the memory if we allocated any and return NULL
   2786       //
   2787       if (EFI_ERROR(Status)) {
   2788         if (Buffer != NULL) {
   2789           FreePool(Buffer);
   2790         }
   2791         return (NULL);
   2792       } else {
   2793         //
   2794         // If we did not find the environment variable in the gShellEnvVarList
   2795         // but get it from UEFI variable storage successfully then we need update
   2796         // the gShellEnvVarList.
   2797         //
   2798         ShellFreeEnvVarList ();
   2799         Status = ShellInitEnvVarList ();
   2800         ASSERT (Status == EFI_SUCCESS);
   2801       }
   2802     }
   2803   }
   2804 
   2805   //
   2806   // return the buffer
   2807   //
   2808   return (AddBufferToFreeList(Buffer));
   2809 }
   2810 
   2811 /**
   2812   Gets either a single or list of environment variables.
   2813 
   2814   If name is not NULL then this function returns the current value of the specified
   2815   environment variable.
   2816 
   2817   If Name is NULL, then a list of all environment variable names is returned.  Each is a
   2818   NULL terminated string with a double NULL terminating the list.
   2819 
   2820   @param Name                   A pointer to the environment variable name.  If
   2821                                 Name is NULL, then the function will return all
   2822                                 of the defined shell environment variables.  In
   2823                                 the case where multiple environment variables are
   2824                                 being returned, each variable will be terminated by
   2825                                 a NULL, and the list will be terminated by a double
   2826                                 NULL.
   2827 
   2828   @retval !=NULL                A pointer to the returned string.
   2829                                 The returned pointer does not need to be freed by the caller.
   2830 
   2831   @retval NULL                  The environment variable doesn't exist or there are
   2832                                 no environment variables.
   2833 **/
   2834 CONST CHAR16 *
   2835 EFIAPI
   2836 EfiShellGetEnv(
   2837   IN CONST CHAR16 *Name
   2838   )
   2839 {
   2840   return (EfiShellGetEnvEx(Name, NULL));
   2841 }
   2842 
   2843 /**
   2844   Internal variable setting function.  Allows for setting of the read only variables.
   2845 
   2846   @param Name                   Points to the NULL-terminated environment variable name.
   2847   @param Value                  Points to the NULL-terminated environment variable value. If the value is an
   2848                                 empty string then the environment variable is deleted.
   2849   @param Volatile               Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
   2850 
   2851   @retval EFI_SUCCESS           The environment variable was successfully updated.
   2852 **/
   2853 EFI_STATUS
   2854 InternalEfiShellSetEnv(
   2855   IN CONST CHAR16 *Name,
   2856   IN CONST CHAR16 *Value,
   2857   IN BOOLEAN Volatile
   2858   )
   2859 {
   2860   EFI_STATUS      Status;
   2861 
   2862   if (Value == NULL || StrLen(Value) == 0) {
   2863     Status = SHELL_DELETE_ENVIRONMENT_VARIABLE(Name);
   2864     if (!EFI_ERROR(Status)) {
   2865       ShellRemvoeEnvVarFromList(Name);
   2866     }
   2867   } else {
   2868     SHELL_DELETE_ENVIRONMENT_VARIABLE(Name);
   2869     Status = ShellAddEnvVarToList(
   2870                Name, Value, StrSize(Value),
   2871                EFI_VARIABLE_BOOTSERVICE_ACCESS | (Volatile ? 0 : EFI_VARIABLE_NON_VOLATILE)
   2872                );
   2873     if (!EFI_ERROR (Status)) {
   2874       Status = Volatile
   2875              ? SHELL_SET_ENVIRONMENT_VARIABLE_V (Name, StrSize (Value) - sizeof (CHAR16), Value)
   2876              : SHELL_SET_ENVIRONMENT_VARIABLE_NV (Name, StrSize (Value) - sizeof (CHAR16), Value);
   2877       if (EFI_ERROR (Status)) {
   2878         ShellRemvoeEnvVarFromList(Name);
   2879       }
   2880     }
   2881   }
   2882   return Status;
   2883 }
   2884 
   2885 /**
   2886   Sets the environment variable.
   2887 
   2888   This function changes the current value of the specified environment variable. If the
   2889   environment variable exists and the Value is an empty string, then the environment
   2890   variable is deleted. If the environment variable exists and the Value is not an empty
   2891   string, then the value of the environment variable is changed. If the environment
   2892   variable does not exist and the Value is an empty string, there is no action. If the
   2893   environment variable does not exist and the Value is a non-empty string, then the
   2894   environment variable is created and assigned the specified value.
   2895 
   2896   For a description of volatile and non-volatile environment variables, see UEFI Shell
   2897   2.0 specification section 3.6.1.
   2898 
   2899   @param Name                   Points to the NULL-terminated environment variable name.
   2900   @param Value                  Points to the NULL-terminated environment variable value. If the value is an
   2901                                 empty string then the environment variable is deleted.
   2902   @param Volatile               Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
   2903 
   2904   @retval EFI_SUCCESS           The environment variable was successfully updated.
   2905 **/
   2906 EFI_STATUS
   2907 EFIAPI
   2908 EfiShellSetEnv(
   2909   IN CONST CHAR16 *Name,
   2910   IN CONST CHAR16 *Value,
   2911   IN BOOLEAN Volatile
   2912   )
   2913 {
   2914   if (Name == NULL || *Name == CHAR_NULL) {
   2915     return (EFI_INVALID_PARAMETER);
   2916   }
   2917   //
   2918   // Make sure we dont 'set' a predefined read only variable
   2919   //
   2920   if (gUnicodeCollation->StriColl(
   2921         gUnicodeCollation,
   2922         (CHAR16*)Name,
   2923         L"cwd") == 0
   2924     ||gUnicodeCollation->StriColl(
   2925         gUnicodeCollation,
   2926         (CHAR16*)Name,
   2927         L"Lasterror") == 0
   2928     ||gUnicodeCollation->StriColl(
   2929         gUnicodeCollation,
   2930         (CHAR16*)Name,
   2931         L"profiles") == 0
   2932     ||gUnicodeCollation->StriColl(
   2933         gUnicodeCollation,
   2934         (CHAR16*)Name,
   2935         L"uefishellsupport") == 0
   2936     ||gUnicodeCollation->StriColl(
   2937         gUnicodeCollation,
   2938         (CHAR16*)Name,
   2939         L"uefishellversion") == 0
   2940     ||gUnicodeCollation->StriColl(
   2941         gUnicodeCollation,
   2942         (CHAR16*)Name,
   2943         L"uefiversion") == 0
   2944     ||(!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest &&
   2945       gUnicodeCollation->StriColl(
   2946         gUnicodeCollation,
   2947         (CHAR16*)Name,
   2948         (CHAR16*)mNoNestingEnvVarName) == 0)
   2949        ){
   2950     return (EFI_INVALID_PARAMETER);
   2951   }
   2952   return (InternalEfiShellSetEnv(Name, Value, Volatile));
   2953 }
   2954 
   2955 /**
   2956   Returns the current directory on the specified device.
   2957 
   2958   If FileSystemMapping is NULL, it returns the current working directory. If the
   2959   FileSystemMapping is not NULL, it returns the current directory associated with the
   2960   FileSystemMapping. In both cases, the returned name includes the file system
   2961   mapping (i.e. fs0:\current-dir).
   2962 
   2963   Note that the current directory string should exclude the tailing backslash character.
   2964 
   2965   @param FileSystemMapping      A pointer to the file system mapping. If NULL,
   2966                                 then the current working directory is returned.
   2967 
   2968   @retval !=NULL                The current directory.
   2969   @retval NULL                  Current directory does not exist.
   2970 **/
   2971 CONST CHAR16 *
   2972 EFIAPI
   2973 EfiShellGetCurDir(
   2974   IN CONST CHAR16 *FileSystemMapping OPTIONAL
   2975   )
   2976 {
   2977   CHAR16  *PathToReturn;
   2978   UINTN   Size;
   2979   SHELL_MAP_LIST *MapListItem;
   2980   if (!IsListEmpty(&gShellMapList.Link)) {
   2981     //
   2982     // if parameter is NULL, use current
   2983     //
   2984     if (FileSystemMapping == NULL) {
   2985       return (EfiShellGetEnv(L"cwd"));
   2986     } else {
   2987       Size = 0;
   2988       PathToReturn = NULL;
   2989       MapListItem = ShellCommandFindMapItem(FileSystemMapping);
   2990       if (MapListItem != NULL) {
   2991         ASSERT((PathToReturn == NULL && Size == 0) || (PathToReturn != NULL));
   2992         PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->MapName, 0);
   2993         PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->CurrentDirectoryPath, 0);
   2994       }
   2995     }
   2996     return (AddBufferToFreeList(PathToReturn));
   2997   } else {
   2998     return (NULL);
   2999   }
   3000 }
   3001 
   3002 /**
   3003   Changes the current directory on the specified device.
   3004 
   3005   If the FileSystem is NULL, and the directory Dir does not contain a file system's
   3006   mapped name, this function changes the current working directory.
   3007 
   3008   If the FileSystem is NULL and the directory Dir contains a mapped name, then the
   3009   current file system and the current directory on that file system are changed.
   3010 
   3011   If FileSystem is NULL, and Dir is not NULL, then this changes the current working file
   3012   system.
   3013 
   3014   If FileSystem is not NULL and Dir is not NULL, then this function changes the current
   3015   directory on the specified file system.
   3016 
   3017   If the current working directory or the current working file system is changed then the
   3018   %cwd% environment variable will be updated
   3019 
   3020   Note that the current directory string should exclude the tailing backslash character.
   3021 
   3022   @param FileSystem             A pointer to the file system's mapped name. If NULL, then the current working
   3023                                 directory is changed.
   3024   @param Dir                    Points to the NULL-terminated directory on the device specified by FileSystem.
   3025 
   3026   @retval EFI_SUCCESS           The operation was sucessful
   3027   @retval EFI_NOT_FOUND         The file system could not be found
   3028 **/
   3029 EFI_STATUS
   3030 EFIAPI
   3031 EfiShellSetCurDir(
   3032   IN CONST CHAR16 *FileSystem OPTIONAL,
   3033   IN CONST CHAR16 *Dir
   3034   )
   3035 {
   3036   CHAR16          *MapName;
   3037   SHELL_MAP_LIST  *MapListItem;
   3038   UINTN           Size;
   3039   EFI_STATUS      Status;
   3040   CHAR16          *TempString;
   3041   CHAR16          *DirectoryName;
   3042   UINTN           TempLen;
   3043 
   3044   Size          = 0;
   3045   MapName       = NULL;
   3046   MapListItem   = NULL;
   3047   TempString    = NULL;
   3048   DirectoryName = NULL;
   3049 
   3050   if ((FileSystem == NULL && Dir == NULL) || Dir == NULL) {
   3051     return (EFI_INVALID_PARAMETER);
   3052   }
   3053 
   3054   if (IsListEmpty(&gShellMapList.Link)){
   3055     return (EFI_NOT_FOUND);
   3056   }
   3057 
   3058   DirectoryName = StrnCatGrow(&DirectoryName, NULL, Dir, 0);
   3059   ASSERT(DirectoryName != NULL);
   3060 
   3061   PathCleanUpDirectories(DirectoryName);
   3062 
   3063   if (FileSystem == NULL) {
   3064     //
   3065     // determine the file system mapping to use
   3066     //
   3067     if (StrStr(DirectoryName, L":") != NULL) {
   3068       ASSERT(MapName == NULL);
   3069       MapName = StrnCatGrow(&MapName, NULL, DirectoryName, (StrStr(DirectoryName, L":")-DirectoryName+1));
   3070     }
   3071     //
   3072     // find the file system mapping's entry in the list
   3073     // or use current
   3074     //
   3075     if (MapName != NULL) {
   3076       MapListItem = ShellCommandFindMapItem(MapName);
   3077 
   3078       //
   3079       // make that the current file system mapping
   3080       //
   3081       if (MapListItem != NULL) {
   3082         gShellCurDir = MapListItem;
   3083       }
   3084     } else {
   3085       MapListItem = gShellCurDir;
   3086     }
   3087 
   3088     if (MapListItem == NULL) {
   3089       FreePool (DirectoryName);
   3090       SHELL_FREE_NON_NULL(MapName);
   3091       return (EFI_NOT_FOUND);
   3092     }
   3093 
   3094     //
   3095     // now update the MapListItem's current directory
   3096     //
   3097     if (MapListItem->CurrentDirectoryPath != NULL && DirectoryName[StrLen(DirectoryName) - 1] != L':') {
   3098       FreePool(MapListItem->CurrentDirectoryPath);
   3099       MapListItem->CurrentDirectoryPath = NULL;
   3100     }
   3101     if (MapName != NULL) {
   3102       TempLen = StrLen(MapName);
   3103       if (TempLen != StrLen(DirectoryName)) {
   3104         ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   3105         MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName+StrLen(MapName), 0);
   3106       }
   3107       FreePool (MapName);
   3108     } else {
   3109       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   3110       MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
   3111     }
   3112     if ((MapListItem->CurrentDirectoryPath != NULL && MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] == L'\\') || (MapListItem->CurrentDirectoryPath == NULL)) {
   3113       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   3114       if (MapListItem->CurrentDirectoryPath != NULL) {
   3115         MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] = CHAR_NULL;
   3116     }
   3117     }
   3118   } else {
   3119     //
   3120     // cant have a mapping in the directory...
   3121     //
   3122     if (StrStr(DirectoryName, L":") != NULL) {
   3123       FreePool (DirectoryName);
   3124       return (EFI_INVALID_PARAMETER);
   3125     }
   3126     //
   3127     // FileSystem != NULL
   3128     //
   3129     MapListItem = ShellCommandFindMapItem(FileSystem);
   3130     if (MapListItem == NULL) {
   3131       FreePool (DirectoryName);
   3132       return (EFI_INVALID_PARAMETER);
   3133     }
   3134 //    gShellCurDir = MapListItem;
   3135     if (DirectoryName != NULL) {
   3136       //
   3137       // change current dir on that file system
   3138       //
   3139 
   3140       if (MapListItem->CurrentDirectoryPath != NULL) {
   3141         FreePool(MapListItem->CurrentDirectoryPath);
   3142         DEBUG_CODE(MapListItem->CurrentDirectoryPath = NULL;);
   3143       }
   3144 //      ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   3145 //      MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, FileSystem, 0);
   3146       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   3147       MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);
   3148       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   3149       MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
   3150       if (MapListItem->CurrentDirectoryPath != NULL && MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] == L'\\') {
   3151         ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   3152         MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] = CHAR_NULL;
   3153       }
   3154     }
   3155   }
   3156   FreePool (DirectoryName);
   3157   //
   3158   // if updated the current directory then update the environment variable
   3159   //
   3160   if (MapListItem == gShellCurDir) {
   3161     Size = 0;
   3162     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
   3163     StrnCatGrow(&TempString, &Size, MapListItem->MapName, 0);
   3164     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
   3165     StrnCatGrow(&TempString, &Size, MapListItem->CurrentDirectoryPath, 0);
   3166     Status =  InternalEfiShellSetEnv(L"cwd", TempString, TRUE);
   3167     FreePool(TempString);
   3168     return (Status);
   3169   }
   3170   return(EFI_SUCCESS);
   3171 }
   3172 
   3173 /**
   3174   Return help information about a specific command.
   3175 
   3176   This function returns the help information for the specified command. The help text
   3177   can be internal to the shell or can be from a UEFI Shell manual page.
   3178 
   3179   If Sections is specified, then each section name listed will be compared in a casesensitive
   3180   manner, to the section names described in Appendix B. If the section exists,
   3181   it will be appended to the returned help text. If the section does not exist, no
   3182   information will be returned. If Sections is NULL, then all help text information
   3183   available will be returned.
   3184 
   3185   @param Command                Points to the NULL-terminated UEFI Shell command name.
   3186   @param Sections               Points to the NULL-terminated comma-delimited
   3187                                 section names to return. If NULL, then all
   3188                                 sections will be returned.
   3189   @param HelpText               On return, points to a callee-allocated buffer
   3190                                 containing all specified help text.
   3191 
   3192   @retval EFI_SUCCESS           The help text was returned.
   3193   @retval EFI_OUT_OF_RESOURCES  The necessary buffer could not be allocated to hold the
   3194                                 returned help text.
   3195   @retval EFI_INVALID_PARAMETER HelpText is NULL
   3196   @retval EFI_NOT_FOUND         There is no help text available for Command.
   3197 **/
   3198 EFI_STATUS
   3199 EFIAPI
   3200 EfiShellGetHelpText(
   3201   IN CONST CHAR16 *Command,
   3202   IN CONST CHAR16 *Sections OPTIONAL,
   3203   OUT CHAR16 **HelpText
   3204   )
   3205 {
   3206   CONST CHAR16  *ManFileName;
   3207   CHAR16        *FixCommand;
   3208   EFI_STATUS    Status;
   3209 
   3210   ASSERT(HelpText != NULL);
   3211   FixCommand = NULL;
   3212 
   3213   ManFileName = ShellCommandGetManFileNameHandler(Command);
   3214 
   3215   if (ManFileName != NULL) {
   3216     return (ProcessManFile(ManFileName, Command, Sections, NULL, HelpText));
   3217   } else {
   3218     if ((StrLen(Command)> 4)
   3219     && (Command[StrLen(Command)-1] == L'i' || Command[StrLen(Command)-1] == L'I')
   3220     && (Command[StrLen(Command)-2] == L'f' || Command[StrLen(Command)-2] == L'F')
   3221     && (Command[StrLen(Command)-3] == L'e' || Command[StrLen(Command)-3] == L'E')
   3222     && (Command[StrLen(Command)-4] == L'.')
   3223     ) {
   3224       FixCommand = AllocateZeroPool(StrSize(Command) - 4 * sizeof (CHAR16));
   3225       if (FixCommand == NULL) {
   3226         return EFI_OUT_OF_RESOURCES;
   3227       }
   3228 
   3229       StrnCpyS( FixCommand,
   3230                 (StrSize(Command) - 4 * sizeof (CHAR16))/sizeof(CHAR16),
   3231                 Command,
   3232                 StrLen(Command)-4
   3233                 );
   3234       Status = ProcessManFile(FixCommand, FixCommand, Sections, NULL, HelpText);
   3235       FreePool(FixCommand);
   3236       return Status;
   3237     } else {
   3238       return (ProcessManFile(Command, Command, Sections, NULL, HelpText));
   3239     }
   3240   }
   3241 }
   3242 
   3243 /**
   3244   Gets the enable status of the page break output mode.
   3245 
   3246   User can use this function to determine current page break mode.
   3247 
   3248   @retval TRUE                  The page break output mode is enabled.
   3249   @retval FALSE                 The page break output mode is disabled.
   3250 **/
   3251 BOOLEAN
   3252 EFIAPI
   3253 EfiShellGetPageBreak(
   3254   VOID
   3255   )
   3256 {
   3257   return(ShellInfoObject.PageBreakEnabled);
   3258 }
   3259 
   3260 /**
   3261   Judges whether the active shell is the root shell.
   3262 
   3263   This function makes the user to know that whether the active Shell is the root shell.
   3264 
   3265   @retval TRUE                  The active Shell is the root Shell.
   3266   @retval FALSE                 The active Shell is NOT the root Shell.
   3267 **/
   3268 BOOLEAN
   3269 EFIAPI
   3270 EfiShellIsRootShell(
   3271   VOID
   3272   )
   3273 {
   3274   return(ShellInfoObject.RootShellInstance);
   3275 }
   3276 
   3277 /**
   3278   function to return a semi-colon delimeted list of all alias' in the current shell
   3279 
   3280   up to caller to free the memory.
   3281 
   3282   @retval NULL    No alias' were found
   3283   @retval NULL    An error ocurred getting alias'
   3284   @return !NULL   a list of all alias'
   3285 **/
   3286 CHAR16 *
   3287 InternalEfiShellGetListAlias(
   3288   )
   3289 {
   3290 
   3291   EFI_STATUS        Status;
   3292   EFI_GUID          Guid;
   3293   CHAR16            *VariableName;
   3294   UINTN             NameSize;
   3295   UINTN             NameBufferSize;
   3296   CHAR16            *RetVal;
   3297   UINTN             RetSize;
   3298 
   3299   NameBufferSize = INIT_NAME_BUFFER_SIZE;
   3300   VariableName  = AllocateZeroPool(NameBufferSize);
   3301   RetSize       = 0;
   3302   RetVal        = NULL;
   3303 
   3304   if (VariableName == NULL) {
   3305     return (NULL);
   3306   }
   3307 
   3308   VariableName[0] = CHAR_NULL;
   3309 
   3310   while (TRUE) {
   3311     NameSize = NameBufferSize;
   3312     Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
   3313     if (Status == EFI_NOT_FOUND){
   3314       break;
   3315     } else if (Status == EFI_BUFFER_TOO_SMALL) {
   3316       NameBufferSize = NameSize > NameBufferSize * 2 ? NameSize : NameBufferSize * 2;
   3317       SHELL_FREE_NON_NULL(VariableName);
   3318       VariableName = AllocateZeroPool(NameBufferSize);
   3319       if (VariableName == NULL) {
   3320         Status = EFI_OUT_OF_RESOURCES;
   3321         SHELL_FREE_NON_NULL(RetVal);
   3322         RetVal = NULL;
   3323         break;
   3324       }
   3325 
   3326       NameSize = NameBufferSize;
   3327       Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
   3328     }
   3329 
   3330     if (EFI_ERROR (Status)) {
   3331       SHELL_FREE_NON_NULL(RetVal);
   3332       RetVal = NULL;
   3333       break;
   3334     }
   3335 
   3336     if (CompareGuid(&Guid, &gShellAliasGuid)){
   3337       ASSERT((RetVal == NULL && RetSize == 0) || (RetVal != NULL));
   3338       RetVal = StrnCatGrow(&RetVal, &RetSize, VariableName, 0);
   3339       RetVal = StrnCatGrow(&RetVal, &RetSize, L";", 0);
   3340     } // compare guid
   3341   } // while
   3342   SHELL_FREE_NON_NULL(VariableName);
   3343 
   3344   return (RetVal);
   3345 }
   3346 
   3347 /**
   3348   Convert a null-terminated unicode string, in-place, to all lowercase.
   3349   Then return it.
   3350 
   3351   @param  Str    The null-terminated string to be converted to all lowercase.
   3352 
   3353   @return        The null-terminated string converted into all lowercase.
   3354 **/
   3355 CHAR16 *
   3356 ToLower (
   3357   CHAR16 *Str
   3358   )
   3359 {
   3360   UINTN Index;
   3361 
   3362   for (Index = 0; Str[Index] != L'\0'; Index++) {
   3363     if (Str[Index] >= L'A' && Str[Index] <= L'Z') {
   3364       Str[Index] -= (CHAR16)(L'A' - L'a');
   3365     }
   3366   }
   3367   return Str;
   3368 }
   3369 
   3370 /**
   3371   This function returns the command associated with a alias or a list of all
   3372   alias'.
   3373 
   3374   @param[in] Alias              Points to the NULL-terminated shell alias.
   3375                                 If this parameter is NULL, then all
   3376                                 aliases will be returned in ReturnedData.
   3377   @param[out] Volatile          upon return of a single command if TRUE indicates
   3378                                 this is stored in a volatile fashion.  FALSE otherwise.
   3379 
   3380   @return                      	If Alias is not NULL, it will return a pointer to
   3381                                 the NULL-terminated command for that alias.
   3382                                 If Alias is NULL, ReturnedData points to a ';'
   3383                                 delimited list of alias (e.g.
   3384                                 ReturnedData = "dir;del;copy;mfp") that is NULL-terminated.
   3385   @retval NULL                  an error ocurred
   3386   @retval NULL                  Alias was not a valid Alias
   3387 **/
   3388 CONST CHAR16 *
   3389 EFIAPI
   3390 EfiShellGetAlias(
   3391   IN  CONST CHAR16 *Alias,
   3392   OUT BOOLEAN      *Volatile OPTIONAL
   3393   )
   3394 {
   3395   CHAR16      *RetVal;
   3396   UINTN       RetSize;
   3397   UINT32      Attribs;
   3398   EFI_STATUS  Status;
   3399   CHAR16      *AliasLower;
   3400   CHAR16      *AliasVal;
   3401 
   3402   // Convert to lowercase to make aliases case-insensitive
   3403   if (Alias != NULL) {
   3404     AliasLower = AllocateCopyPool (StrSize (Alias), Alias);
   3405     if (AliasLower == NULL) {
   3406       return NULL;
   3407     }
   3408     ToLower (AliasLower);
   3409 
   3410     if (Volatile == NULL) {
   3411       GetVariable2 (AliasLower, &gShellAliasGuid, (VOID **)&AliasVal, NULL);
   3412       FreePool(AliasLower);
   3413       return (AddBufferToFreeList(AliasVal));
   3414     }
   3415     RetSize = 0;
   3416     RetVal = NULL;
   3417     Status = gRT->GetVariable(AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
   3418     if (Status == EFI_BUFFER_TOO_SMALL) {
   3419       RetVal = AllocateZeroPool(RetSize);
   3420       Status = gRT->GetVariable(AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
   3421     }
   3422     if (EFI_ERROR(Status)) {
   3423       if (RetVal != NULL) {
   3424         FreePool(RetVal);
   3425       }
   3426       FreePool(AliasLower);
   3427       return (NULL);
   3428     }
   3429     if ((EFI_VARIABLE_NON_VOLATILE & Attribs) == EFI_VARIABLE_NON_VOLATILE) {
   3430       *Volatile = FALSE;
   3431     } else {
   3432       *Volatile = TRUE;
   3433     }
   3434 
   3435     FreePool (AliasLower);
   3436     return (AddBufferToFreeList(RetVal));
   3437   }
   3438   return (AddBufferToFreeList(InternalEfiShellGetListAlias()));
   3439 }
   3440 
   3441 /**
   3442   Changes a shell command alias.
   3443 
   3444   This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
   3445 
   3446   this function does not check for built in alias'.
   3447 
   3448   @param[in] Command            Points to the NULL-terminated shell command or existing alias.
   3449   @param[in] Alias              Points to the NULL-terminated alias for the shell command. If this is NULL, and
   3450                                 Command refers to an alias, that alias will be deleted.
   3451   @param[in] Volatile           if TRUE the Alias being set will be stored in a volatile fashion.  if FALSE the
   3452                                 Alias being set will be stored in a non-volatile fashion.
   3453 
   3454   @retval EFI_SUCCESS           Alias created or deleted successfully.
   3455   @retval EFI_NOT_FOUND         the Alias intended to be deleted was not found
   3456 **/
   3457 EFI_STATUS
   3458 InternalSetAlias(
   3459   IN CONST CHAR16 *Command,
   3460   IN CONST CHAR16 *Alias,
   3461   IN BOOLEAN Volatile
   3462   )
   3463 {
   3464   EFI_STATUS  Status;
   3465   CHAR16      *AliasLower;
   3466 
   3467   // Convert to lowercase to make aliases case-insensitive
   3468   if (Alias != NULL) {
   3469     AliasLower = AllocateCopyPool (StrSize (Alias), Alias);
   3470     if (AliasLower == NULL) {
   3471       return EFI_OUT_OF_RESOURCES;
   3472     }
   3473     ToLower (AliasLower);
   3474   } else {
   3475     AliasLower = NULL;
   3476   }
   3477 
   3478   //
   3479   // We must be trying to remove one if Alias is NULL
   3480   //
   3481   if (Alias == NULL) {
   3482     //
   3483     // remove an alias (but passed in COMMAND parameter)
   3484     //
   3485     Status = (gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL));
   3486   } else {
   3487     //
   3488     // Add and replace are the same
   3489     //
   3490 
   3491     // We dont check the error return on purpose since the variable may not exist.
   3492     gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL);
   3493 
   3494     Status = (gRT->SetVariable((CHAR16*)Alias, &gShellAliasGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS|(Volatile?0:EFI_VARIABLE_NON_VOLATILE), StrSize(Command), (VOID*)Command));
   3495   }
   3496 
   3497   if (Alias != NULL) {
   3498     FreePool (AliasLower);
   3499   }
   3500   return Status;
   3501 }
   3502 
   3503 /**
   3504   Changes a shell command alias.
   3505 
   3506   This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
   3507 
   3508 
   3509   @param[in] Command            Points to the NULL-terminated shell command or existing alias.
   3510   @param[in] Alias              Points to the NULL-terminated alias for the shell command. If this is NULL, and
   3511                                 Command refers to an alias, that alias will be deleted.
   3512   @param[in] Replace            If TRUE and the alias already exists, then the existing alias will be replaced. If
   3513                                 FALSE and the alias already exists, then the existing alias is unchanged and
   3514                                 EFI_ACCESS_DENIED is returned.
   3515   @param[in] Volatile           if TRUE the Alias being set will be stored in a volatile fashion.  if FALSE the
   3516                                 Alias being set will be stored in a non-volatile fashion.
   3517 
   3518   @retval EFI_SUCCESS           Alias created or deleted successfully.
   3519   @retval EFI_NOT_FOUND         the Alias intended to be deleted was not found
   3520   @retval EFI_ACCESS_DENIED     The alias is a built-in alias or already existed and Replace was set to
   3521                                 FALSE.
   3522   @retval EFI_INVALID_PARAMETER Command is null or the empty string.
   3523 **/
   3524 EFI_STATUS
   3525 EFIAPI
   3526 EfiShellSetAlias(
   3527   IN CONST CHAR16 *Command,
   3528   IN CONST CHAR16 *Alias,
   3529   IN BOOLEAN Replace,
   3530   IN BOOLEAN Volatile
   3531   )
   3532 {
   3533   if (ShellCommandIsOnAliasList(Alias==NULL?Command:Alias)) {
   3534     //
   3535     // cant set over a built in alias
   3536     //
   3537     return (EFI_ACCESS_DENIED);
   3538   } else if (Command == NULL || *Command == CHAR_NULL || StrLen(Command) == 0) {
   3539     //
   3540     // Command is null or empty
   3541     //
   3542     return (EFI_INVALID_PARAMETER);
   3543   } else if (EfiShellGetAlias(Command, NULL) != NULL && !Replace) {
   3544     //
   3545     // Alias already exists, Replace not set
   3546     //
   3547     return (EFI_ACCESS_DENIED);
   3548   } else {
   3549     return (InternalSetAlias(Command, Alias, Volatile));
   3550   }
   3551 }
   3552 
   3553 // Pure FILE_HANDLE operations are passed to FileHandleLib
   3554 // these functions are indicated by the *
   3555 EFI_SHELL_PROTOCOL         mShellProtocol = {
   3556   EfiShellExecute,
   3557   EfiShellGetEnv,
   3558   EfiShellSetEnv,
   3559   EfiShellGetAlias,
   3560   EfiShellSetAlias,
   3561   EfiShellGetHelpText,
   3562   EfiShellGetDevicePathFromMap,
   3563   EfiShellGetMapFromDevicePath,
   3564   EfiShellGetDevicePathFromFilePath,
   3565   EfiShellGetFilePathFromDevicePath,
   3566   EfiShellSetMap,
   3567   EfiShellGetCurDir,
   3568   EfiShellSetCurDir,
   3569   EfiShellOpenFileList,
   3570   EfiShellFreeFileList,
   3571   EfiShellRemoveDupInFileList,
   3572   EfiShellBatchIsActive,
   3573   EfiShellIsRootShell,
   3574   EfiShellEnablePageBreak,
   3575   EfiShellDisablePageBreak,
   3576   EfiShellGetPageBreak,
   3577   EfiShellGetDeviceName,
   3578   (EFI_SHELL_GET_FILE_INFO)FileHandleGetInfo,         //*
   3579   (EFI_SHELL_SET_FILE_INFO)FileHandleSetInfo,         //*
   3580   EfiShellOpenFileByName,
   3581   EfiShellClose,
   3582   EfiShellCreateFile,
   3583   (EFI_SHELL_READ_FILE)FileHandleRead,                //*
   3584   (EFI_SHELL_WRITE_FILE)FileHandleWrite,              //*
   3585   (EFI_SHELL_DELETE_FILE)FileHandleDelete,            //*
   3586   EfiShellDeleteFileByName,
   3587   (EFI_SHELL_GET_FILE_POSITION)FileHandleGetPosition, //*
   3588   (EFI_SHELL_SET_FILE_POSITION)FileHandleSetPosition, //*
   3589   (EFI_SHELL_FLUSH_FILE)FileHandleFlush,              //*
   3590   EfiShellFindFiles,
   3591   EfiShellFindFilesInDir,
   3592   (EFI_SHELL_GET_FILE_SIZE)FileHandleGetSize,         //*
   3593   EfiShellOpenRoot,
   3594   EfiShellOpenRootByHandle,
   3595   NULL,
   3596   SHELL_MAJOR_VERSION,
   3597   SHELL_MINOR_VERSION,
   3598 
   3599   // New for UEFI Shell 2.1
   3600   EfiShellRegisterGuidName,
   3601   EfiShellGetGuidName,
   3602   EfiShellGetGuidFromName,
   3603   EfiShellGetEnvEx
   3604 };
   3605 
   3606 /**
   3607   Function to create and install on the current handle.
   3608 
   3609   Will overwrite any existing ShellProtocols in the system to be sure that
   3610   the current shell is in control.
   3611 
   3612   This must be removed via calling CleanUpShellProtocol().
   3613 
   3614   @param[in, out] NewShell   The pointer to the pointer to the structure
   3615   to install.
   3616 
   3617   @retval EFI_SUCCESS     The operation was successful.
   3618   @return                 An error from LocateHandle, CreateEvent, or other core function.
   3619 **/
   3620 EFI_STATUS
   3621 CreatePopulateInstallShellProtocol (
   3622   IN OUT EFI_SHELL_PROTOCOL  **NewShell
   3623   )
   3624 {
   3625   EFI_STATUS                  Status;
   3626   UINTN                       BufferSize;
   3627   EFI_HANDLE                  *Buffer;
   3628   UINTN                       HandleCounter;
   3629   SHELL_PROTOCOL_HANDLE_LIST  *OldProtocolNode;
   3630   EFI_SHELL_PROTOCOL          *OldShell;
   3631 
   3632   if (NewShell == NULL) {
   3633     return (EFI_INVALID_PARAMETER);
   3634   }
   3635 
   3636   BufferSize = 0;
   3637   Buffer = NULL;
   3638   OldProtocolNode = NULL;
   3639   InitializeListHead(&ShellInfoObject.OldShellList.Link);
   3640 
   3641   //
   3642   // Initialize EfiShellProtocol object...
   3643   //
   3644   Status = gBS->CreateEvent(0,
   3645                             0,
   3646                             NULL,
   3647                             NULL,
   3648                             &mShellProtocol.ExecutionBreak);
   3649   if (EFI_ERROR(Status)) {
   3650     return (Status);
   3651   }
   3652 
   3653   //
   3654   // Get the size of the buffer we need.
   3655   //
   3656   Status = gBS->LocateHandle(ByProtocol,
   3657                              &gEfiShellProtocolGuid,
   3658                              NULL,
   3659                              &BufferSize,
   3660                              Buffer);
   3661   if (Status == EFI_BUFFER_TOO_SMALL) {
   3662     //
   3663     // Allocate and recall with buffer of correct size
   3664     //
   3665     Buffer = AllocateZeroPool(BufferSize);
   3666     if (Buffer == NULL) {
   3667       return (EFI_OUT_OF_RESOURCES);
   3668     }
   3669     Status = gBS->LocateHandle(ByProtocol,
   3670                                &gEfiShellProtocolGuid,
   3671                                NULL,
   3672                                &BufferSize,
   3673                                Buffer);
   3674     if (EFI_ERROR(Status)) {
   3675       FreePool(Buffer);
   3676       return (Status);
   3677     }
   3678     //
   3679     // now overwrite each of them, but save the info to restore when we end.
   3680     //
   3681     for (HandleCounter = 0 ; HandleCounter < (BufferSize/sizeof(EFI_HANDLE)) ; HandleCounter++) {
   3682       Status = gBS->OpenProtocol(Buffer[HandleCounter],
   3683                                 &gEfiShellProtocolGuid,
   3684                                 (VOID **) &OldShell,
   3685                                 gImageHandle,
   3686                                 NULL,
   3687                                 EFI_OPEN_PROTOCOL_GET_PROTOCOL
   3688                                );
   3689       if (!EFI_ERROR(Status)) {
   3690         OldProtocolNode = AllocateZeroPool(sizeof(SHELL_PROTOCOL_HANDLE_LIST));
   3691         if (OldProtocolNode == NULL) {
   3692           if (!IsListEmpty (&ShellInfoObject.OldShellList.Link)) {
   3693             CleanUpShellProtocol (&mShellProtocol);
   3694           }
   3695           Status = EFI_OUT_OF_RESOURCES;
   3696           break;
   3697         }
   3698         //
   3699         // reinstall over the old one...
   3700         //
   3701         OldProtocolNode->Handle    = Buffer[HandleCounter];
   3702         OldProtocolNode->Interface = OldShell;
   3703         Status = gBS->ReinstallProtocolInterface(
   3704                             OldProtocolNode->Handle,
   3705                             &gEfiShellProtocolGuid,
   3706                             OldProtocolNode->Interface,
   3707                             (VOID*)(&mShellProtocol));
   3708         if (!EFI_ERROR(Status)) {
   3709           //
   3710           // we reinstalled sucessfully.  log this so we can reverse it later.
   3711           //
   3712 
   3713           //
   3714           // add to the list for subsequent...
   3715           //
   3716           InsertTailList(&ShellInfoObject.OldShellList.Link, &OldProtocolNode->Link);
   3717         }
   3718       }
   3719     }
   3720     FreePool(Buffer);
   3721   } else if (Status == EFI_NOT_FOUND) {
   3722     ASSERT(IsListEmpty(&ShellInfoObject.OldShellList.Link));
   3723     //
   3724     // no one else published yet.  just publish it ourselves.
   3725     //
   3726     Status = gBS->InstallProtocolInterface (
   3727                       &gImageHandle,
   3728                       &gEfiShellProtocolGuid,
   3729                       EFI_NATIVE_INTERFACE,
   3730                       (VOID*)(&mShellProtocol));
   3731   }
   3732 
   3733   if (PcdGetBool(PcdShellSupportOldProtocols)){
   3734     ///@todo support ShellEnvironment2
   3735     ///@todo do we need to support ShellEnvironment (not ShellEnvironment2) also?
   3736   }
   3737 
   3738   if (!EFI_ERROR(Status)) {
   3739     *NewShell = &mShellProtocol;
   3740   }
   3741   return (Status);
   3742 }
   3743 
   3744 /**
   3745   Opposite of CreatePopulateInstallShellProtocol.
   3746 
   3747   Free all memory and restore the system to the state it was in before calling
   3748   CreatePopulateInstallShellProtocol.
   3749 
   3750   @param[in, out] NewShell   The pointer to the new shell protocol structure.
   3751 
   3752   @retval EFI_SUCCESS       The operation was successful.
   3753 **/
   3754 EFI_STATUS
   3755 CleanUpShellProtocol (
   3756   IN OUT EFI_SHELL_PROTOCOL  *NewShell
   3757   )
   3758 {
   3759   SHELL_PROTOCOL_HANDLE_LIST        *Node2;
   3760 
   3761   //
   3762   // if we need to restore old protocols...
   3763   //
   3764   if (!IsListEmpty(&ShellInfoObject.OldShellList.Link)) {
   3765     for (Node2 = (SHELL_PROTOCOL_HANDLE_LIST *) GetFirstNode (&ShellInfoObject.OldShellList.Link)
   3766          ; !IsListEmpty (&ShellInfoObject.OldShellList.Link)
   3767          ; Node2 = (SHELL_PROTOCOL_HANDLE_LIST *) GetFirstNode (&ShellInfoObject.OldShellList.Link)
   3768          ) {
   3769       RemoveEntryList (&Node2->Link);
   3770       gBS->ReinstallProtocolInterface (Node2->Handle, &gEfiShellProtocolGuid, NewShell, Node2->Interface);
   3771       FreePool (Node2);
   3772     }
   3773   } else {
   3774     //
   3775     // no need to restore
   3776     //
   3777     gBS->UninstallProtocolInterface (gImageHandle, &gEfiShellProtocolGuid, NewShell);
   3778   }
   3779   return EFI_SUCCESS;
   3780 }
   3781 
   3782 /**
   3783   Cleanup the shell environment.
   3784 
   3785   @param[in, out] NewShell   The pointer to the new shell protocol structure.
   3786 
   3787   @retval EFI_SUCCESS       The operation was successful.
   3788 **/
   3789 EFI_STATUS
   3790 CleanUpShellEnvironment (
   3791   IN OUT EFI_SHELL_PROTOCOL  *NewShell
   3792   )
   3793 {
   3794   EFI_STATUS                        Status;
   3795   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
   3796 
   3797   CleanUpShellProtocol (NewShell);
   3798 
   3799   Status = gBS->CloseEvent(NewShell->ExecutionBreak);
   3800   NewShell->ExecutionBreak = NULL;
   3801 
   3802   Status = gBS->OpenProtocol(
   3803     gST->ConsoleInHandle,
   3804     &gEfiSimpleTextInputExProtocolGuid,
   3805     (VOID**)&SimpleEx,
   3806     gImageHandle,
   3807     NULL,
   3808     EFI_OPEN_PROTOCOL_GET_PROTOCOL);
   3809 
   3810   if (!EFI_ERROR (Status)) {
   3811     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle1);
   3812     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle2);
   3813     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle3);
   3814     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle4);
   3815     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle1);
   3816     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle2);
   3817     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle3);
   3818     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle4);
   3819   }
   3820   return (Status);
   3821 }
   3822 
   3823 /**
   3824   Notification function for keystrokes.
   3825 
   3826   @param[in] KeyData    The key that was pressed.
   3827 
   3828   @retval EFI_SUCCESS   The operation was successful.
   3829 **/
   3830 EFI_STATUS
   3831 EFIAPI
   3832 NotificationFunction(
   3833   IN EFI_KEY_DATA *KeyData
   3834   )
   3835 {
   3836   if ( ((KeyData->Key.UnicodeChar == L'c') &&
   3837         (KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED) || KeyData->KeyState.KeyShiftState  == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED))) ||
   3838       (KeyData->Key.UnicodeChar == 3)
   3839       ){
   3840     if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) {
   3841       return (EFI_UNSUPPORTED);
   3842     }
   3843     return (gBS->SignalEvent(ShellInfoObject.NewEfiShellProtocol->ExecutionBreak));
   3844   } else if  ((KeyData->Key.UnicodeChar == L's') &&
   3845               (KeyData->KeyState.KeyShiftState  == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED) || KeyData->KeyState.KeyShiftState  == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED))
   3846               ){
   3847     ShellInfoObject.HaltOutput = TRUE;
   3848   }
   3849   return (EFI_SUCCESS);
   3850 }
   3851 
   3852 /**
   3853   Function to start monitoring for CTRL-C using SimpleTextInputEx.  This
   3854   feature's enabled state was not known when the shell initially launched.
   3855 
   3856   @retval EFI_SUCCESS           The feature is enabled.
   3857   @retval EFI_OUT_OF_RESOURCES  There is not enough mnemory available.
   3858 **/
   3859 EFI_STATUS
   3860 InernalEfiShellStartMonitor(
   3861   VOID
   3862   )
   3863 {
   3864   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
   3865   EFI_KEY_DATA                      KeyData;
   3866   EFI_STATUS                        Status;
   3867 
   3868   Status = gBS->OpenProtocol(
   3869     gST->ConsoleInHandle,
   3870     &gEfiSimpleTextInputExProtocolGuid,
   3871     (VOID**)&SimpleEx,
   3872     gImageHandle,
   3873     NULL,
   3874     EFI_OPEN_PROTOCOL_GET_PROTOCOL);
   3875   if (EFI_ERROR(Status)) {
   3876     ShellPrintHiiEx(
   3877       -1,
   3878       -1,
   3879       NULL,
   3880       STRING_TOKEN (STR_SHELL_NO_IN_EX),
   3881       ShellInfoObject.HiiHandle);
   3882     return (EFI_SUCCESS);
   3883   }
   3884 
   3885   if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) {
   3886     return (EFI_UNSUPPORTED);
   3887   }
   3888 
   3889   KeyData.KeyState.KeyToggleState = 0;
   3890   KeyData.Key.ScanCode            = 0;
   3891   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
   3892   KeyData.Key.UnicodeChar         = L'c';
   3893 
   3894   Status = SimpleEx->RegisterKeyNotify(
   3895     SimpleEx,
   3896     &KeyData,
   3897     NotificationFunction,
   3898     &ShellInfoObject.CtrlCNotifyHandle1);
   3899 
   3900   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
   3901   if (!EFI_ERROR(Status)) {
   3902     Status = SimpleEx->RegisterKeyNotify(
   3903       SimpleEx,
   3904       &KeyData,
   3905       NotificationFunction,
   3906       &ShellInfoObject.CtrlCNotifyHandle2);
   3907   }
   3908   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
   3909   KeyData.Key.UnicodeChar         = 3;
   3910   if (!EFI_ERROR(Status)) {
   3911     Status = SimpleEx->RegisterKeyNotify(
   3912       SimpleEx,
   3913       &KeyData,
   3914       NotificationFunction,
   3915       &ShellInfoObject.CtrlCNotifyHandle3);
   3916   }
   3917   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
   3918   if (!EFI_ERROR(Status)) {
   3919     Status = SimpleEx->RegisterKeyNotify(
   3920       SimpleEx,
   3921       &KeyData,
   3922       NotificationFunction,
   3923       &ShellInfoObject.CtrlCNotifyHandle4);
   3924   }
   3925   return (Status);
   3926 }
   3927 
   3928