Home | History | Annotate | Download | only in UefiShellLib
      1 /** @file
      2   Provides interface to shell functionality for shell commands and applications.
      3 
      4   (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
      5   Copyright 2016 Dell Inc.
      6   Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
      7   This program and the accompanying materials
      8   are licensed and made available under the terms and conditions of the BSD License
      9   which accompanies this distribution.  The full text of the license may be found at
     10   http://opensource.org/licenses/bsd-license.php
     11 
     12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     13   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 
     15 **/
     16 
     17 #include "UefiShellLib.h"
     18 #include <Library/SortLib.h>
     19 #include <Library/BaseLib.h>
     20 
     21 #define FIND_XXXXX_FILE_BUFFER_SIZE (SIZE_OF_EFI_FILE_INFO + MAX_FILE_NAME_LEN)
     22 
     23 //
     24 // globals...
     25 //
     26 SHELL_PARAM_ITEM EmptyParamList[] = {
     27   {NULL, TypeMax}
     28   };
     29 SHELL_PARAM_ITEM SfoParamList[] = {
     30   {L"-sfo", TypeFlag},
     31   {NULL, TypeMax}
     32   };
     33 EFI_SHELL_ENVIRONMENT2        *mEfiShellEnvironment2;
     34 EFI_SHELL_INTERFACE           *mEfiShellInterface;
     35 EFI_SHELL_PROTOCOL            *gEfiShellProtocol;
     36 EFI_SHELL_PARAMETERS_PROTOCOL *gEfiShellParametersProtocol;
     37 EFI_HANDLE                    mEfiShellEnvironment2Handle;
     38 FILE_HANDLE_FUNCTION_MAP      FileFunctionMap;
     39 EFI_UNICODE_COLLATION_PROTOCOL  *mUnicodeCollationProtocol;
     40 
     41 /**
     42   Check if a Unicode character is a hexadecimal character.
     43 
     44   This internal function checks if a Unicode character is a
     45   numeric character.  The valid hexadecimal characters are
     46   L'0' to L'9', L'a' to L'f', or L'A' to L'F'.
     47 
     48   @param  Char  The character to check against.
     49 
     50   @retval TRUE  If the Char is a hexadecmial character.
     51   @retval FALSE If the Char is not a hexadecmial character.
     52 
     53 **/
     54 BOOLEAN
     55 EFIAPI
     56 ShellIsHexaDecimalDigitCharacter (
     57   IN      CHAR16                    Char
     58   )
     59 {
     60   return (BOOLEAN) ((Char >= L'0' && Char <= L'9') || (Char >= L'A' && Char <= L'F') || (Char >= L'a' && Char <= L'f'));
     61 }
     62 
     63 /**
     64   Check if a Unicode character is a decimal character.
     65 
     66   This internal function checks if a Unicode character is a
     67   decimal character.  The valid characters are
     68   L'0' to L'9'.
     69 
     70 
     71   @param  Char  The character to check against.
     72 
     73   @retval TRUE  If the Char is a hexadecmial character.
     74   @retval FALSE If the Char is not a hexadecmial character.
     75 
     76 **/
     77 BOOLEAN
     78 EFIAPI
     79 ShellIsDecimalDigitCharacter (
     80   IN      CHAR16                    Char
     81   )
     82 {
     83   return (BOOLEAN) (Char >= L'0' && Char <= L'9');
     84 }
     85 
     86 /**
     87   Helper function to find ShellEnvironment2 for constructor.
     88 
     89   @param[in] ImageHandle    A copy of the calling image's handle.
     90 
     91   @retval EFI_OUT_OF_RESOURCES    Memory allocation failed.
     92 **/
     93 EFI_STATUS
     94 ShellFindSE2 (
     95   IN EFI_HANDLE        ImageHandle
     96   )
     97 {
     98   EFI_STATUS  Status;
     99   EFI_HANDLE  *Buffer;
    100   UINTN       BufferSize;
    101   UINTN       HandleIndex;
    102 
    103   BufferSize = 0;
    104   Buffer = NULL;
    105   Status = gBS->OpenProtocol(ImageHandle,
    106                              &gEfiShellEnvironment2Guid,
    107                              (VOID **)&mEfiShellEnvironment2,
    108                              ImageHandle,
    109                              NULL,
    110                              EFI_OPEN_PROTOCOL_GET_PROTOCOL
    111                             );
    112   //
    113   // look for the mEfiShellEnvironment2 protocol at a higher level
    114   //
    115   if (EFI_ERROR (Status) || !(CompareGuid (&mEfiShellEnvironment2->SESGuid, &gEfiShellEnvironment2ExtGuid))){
    116     //
    117     // figure out how big of a buffer we need.
    118     //
    119     Status = gBS->LocateHandle (ByProtocol,
    120                                 &gEfiShellEnvironment2Guid,
    121                                 NULL, // ignored for ByProtocol
    122                                 &BufferSize,
    123                                 Buffer
    124                                );
    125     //
    126     // maybe it's not there???
    127     //
    128     if (Status == EFI_BUFFER_TOO_SMALL) {
    129       Buffer = (EFI_HANDLE*)AllocateZeroPool(BufferSize);
    130       if (Buffer == NULL) {
    131         return (EFI_OUT_OF_RESOURCES);
    132       }
    133       Status = gBS->LocateHandle (ByProtocol,
    134                                   &gEfiShellEnvironment2Guid,
    135                                   NULL, // ignored for ByProtocol
    136                                   &BufferSize,
    137                                   Buffer
    138                                  );
    139     }
    140     if (!EFI_ERROR (Status) && Buffer != NULL) {
    141       //
    142       // now parse the list of returned handles
    143       //
    144       Status = EFI_NOT_FOUND;
    145       for (HandleIndex = 0; HandleIndex < (BufferSize/sizeof(Buffer[0])); HandleIndex++) {
    146         Status = gBS->OpenProtocol(Buffer[HandleIndex],
    147                                    &gEfiShellEnvironment2Guid,
    148                                    (VOID **)&mEfiShellEnvironment2,
    149                                    ImageHandle,
    150                                    NULL,
    151                                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
    152                                   );
    153          if (CompareGuid (&mEfiShellEnvironment2->SESGuid, &gEfiShellEnvironment2ExtGuid)) {
    154           mEfiShellEnvironment2Handle = Buffer[HandleIndex];
    155           Status = EFI_SUCCESS;
    156           break;
    157         }
    158       }
    159     }
    160   }
    161   if (Buffer != NULL) {
    162     FreePool (Buffer);
    163   }
    164   return (Status);
    165 }
    166 
    167 /**
    168   Function to do most of the work of the constructor.  Allows for calling
    169   multiple times without complete re-initialization.
    170 
    171   @param[in] ImageHandle  A copy of the ImageHandle.
    172   @param[in] SystemTable  A pointer to the SystemTable for the application.
    173 
    174   @retval EFI_SUCCESS   The operationw as successful.
    175 **/
    176 EFI_STATUS
    177 ShellLibConstructorWorker (
    178   IN EFI_HANDLE        ImageHandle,
    179   IN EFI_SYSTEM_TABLE  *SystemTable
    180   )
    181 {
    182   EFI_STATUS  Status;
    183 
    184   //
    185   // UEFI 2.0 shell interfaces (used preferentially)
    186   //
    187   Status = gBS->OpenProtocol(
    188     ImageHandle,
    189     &gEfiShellProtocolGuid,
    190     (VOID **)&gEfiShellProtocol,
    191     ImageHandle,
    192     NULL,
    193     EFI_OPEN_PROTOCOL_GET_PROTOCOL
    194    );
    195   if (EFI_ERROR(Status)) {
    196     //
    197     // Search for the shell protocol
    198     //
    199     Status = gBS->LocateProtocol(
    200       &gEfiShellProtocolGuid,
    201       NULL,
    202       (VOID **)&gEfiShellProtocol
    203      );
    204     if (EFI_ERROR(Status)) {
    205       gEfiShellProtocol = NULL;
    206     }
    207   }
    208   Status = gBS->OpenProtocol(
    209     ImageHandle,
    210     &gEfiShellParametersProtocolGuid,
    211     (VOID **)&gEfiShellParametersProtocol,
    212     ImageHandle,
    213     NULL,
    214     EFI_OPEN_PROTOCOL_GET_PROTOCOL
    215    );
    216   if (EFI_ERROR(Status)) {
    217     gEfiShellParametersProtocol = NULL;
    218   }
    219 
    220   if (gEfiShellParametersProtocol == NULL || gEfiShellProtocol == NULL) {
    221     //
    222     // Moved to seperate function due to complexity
    223     //
    224     Status = ShellFindSE2(ImageHandle);
    225 
    226     if (EFI_ERROR(Status)) {
    227       DEBUG((DEBUG_ERROR, "Status: 0x%08x\r\n", Status));
    228       mEfiShellEnvironment2 = NULL;
    229     }
    230     Status = gBS->OpenProtocol(ImageHandle,
    231                                &gEfiShellInterfaceGuid,
    232                                (VOID **)&mEfiShellInterface,
    233                                ImageHandle,
    234                                NULL,
    235                                EFI_OPEN_PROTOCOL_GET_PROTOCOL
    236                               );
    237     if (EFI_ERROR(Status)) {
    238       mEfiShellInterface = NULL;
    239     }
    240   }
    241 
    242   //
    243   // only success getting 2 of either the old or new, but no 1/2 and 1/2
    244   //
    245   if ((mEfiShellEnvironment2 != NULL && mEfiShellInterface          != NULL) ||
    246       (gEfiShellProtocol     != NULL && gEfiShellParametersProtocol != NULL)   ) {
    247     if (gEfiShellProtocol != NULL) {
    248       FileFunctionMap.GetFileInfo     = gEfiShellProtocol->GetFileInfo;
    249       FileFunctionMap.SetFileInfo     = gEfiShellProtocol->SetFileInfo;
    250       FileFunctionMap.ReadFile        = gEfiShellProtocol->ReadFile;
    251       FileFunctionMap.WriteFile       = gEfiShellProtocol->WriteFile;
    252       FileFunctionMap.CloseFile       = gEfiShellProtocol->CloseFile;
    253       FileFunctionMap.DeleteFile      = gEfiShellProtocol->DeleteFile;
    254       FileFunctionMap.GetFilePosition = gEfiShellProtocol->GetFilePosition;
    255       FileFunctionMap.SetFilePosition = gEfiShellProtocol->SetFilePosition;
    256       FileFunctionMap.FlushFile       = gEfiShellProtocol->FlushFile;
    257       FileFunctionMap.GetFileSize     = gEfiShellProtocol->GetFileSize;
    258     } else {
    259       FileFunctionMap.GetFileInfo     = (EFI_SHELL_GET_FILE_INFO)FileHandleGetInfo;
    260       FileFunctionMap.SetFileInfo     = (EFI_SHELL_SET_FILE_INFO)FileHandleSetInfo;
    261       FileFunctionMap.ReadFile        = (EFI_SHELL_READ_FILE)FileHandleRead;
    262       FileFunctionMap.WriteFile       = (EFI_SHELL_WRITE_FILE)FileHandleWrite;
    263       FileFunctionMap.CloseFile       = (EFI_SHELL_CLOSE_FILE)FileHandleClose;
    264       FileFunctionMap.DeleteFile      = (EFI_SHELL_DELETE_FILE)FileHandleDelete;
    265       FileFunctionMap.GetFilePosition = (EFI_SHELL_GET_FILE_POSITION)FileHandleGetPosition;
    266       FileFunctionMap.SetFilePosition = (EFI_SHELL_SET_FILE_POSITION)FileHandleSetPosition;
    267       FileFunctionMap.FlushFile       = (EFI_SHELL_FLUSH_FILE)FileHandleFlush;
    268       FileFunctionMap.GetFileSize     = (EFI_SHELL_GET_FILE_SIZE)FileHandleGetSize;
    269     }
    270     return (EFI_SUCCESS);
    271   }
    272   return (EFI_NOT_FOUND);
    273 }
    274 /**
    275   Constructor for the Shell library.
    276 
    277   Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell.
    278 
    279   @param ImageHandle    the image handle of the process
    280   @param SystemTable    the EFI System Table pointer
    281 
    282   @retval EFI_SUCCESS   the initialization was complete sucessfully
    283   @return others        an error ocurred during initialization
    284 **/
    285 EFI_STATUS
    286 EFIAPI
    287 ShellLibConstructor (
    288   IN EFI_HANDLE        ImageHandle,
    289   IN EFI_SYSTEM_TABLE  *SystemTable
    290   )
    291 {
    292   mEfiShellEnvironment2       = NULL;
    293   gEfiShellProtocol           = NULL;
    294   gEfiShellParametersProtocol = NULL;
    295   mEfiShellInterface          = NULL;
    296   mEfiShellEnvironment2Handle = NULL;
    297   mUnicodeCollationProtocol   = NULL;
    298 
    299   //
    300   // verify that auto initialize is not set false
    301   //
    302   if (PcdGetBool(PcdShellLibAutoInitialize) == 0) {
    303     return (EFI_SUCCESS);
    304   }
    305 
    306   return (ShellLibConstructorWorker(ImageHandle, SystemTable));
    307 }
    308 
    309 /**
    310   Destructor for the library.  free any resources.
    311 
    312   @param[in] ImageHandle  A copy of the ImageHandle.
    313   @param[in] SystemTable  A pointer to the SystemTable for the application.
    314 
    315   @retval EFI_SUCCESS   The operation was successful.
    316   @return               An error from the CloseProtocol function.
    317 **/
    318 EFI_STATUS
    319 EFIAPI
    320 ShellLibDestructor (
    321   IN EFI_HANDLE        ImageHandle,
    322   IN EFI_SYSTEM_TABLE  *SystemTable
    323   )
    324 {
    325   if (mEfiShellEnvironment2 != NULL) {
    326     gBS->CloseProtocol(mEfiShellEnvironment2Handle==NULL?ImageHandle:mEfiShellEnvironment2Handle,
    327                        &gEfiShellEnvironment2Guid,
    328                        ImageHandle,
    329                        NULL);
    330     mEfiShellEnvironment2 = NULL;
    331   }
    332   if (mEfiShellInterface != NULL) {
    333     gBS->CloseProtocol(ImageHandle,
    334                        &gEfiShellInterfaceGuid,
    335                        ImageHandle,
    336                        NULL);
    337     mEfiShellInterface = NULL;
    338   }
    339   if (gEfiShellProtocol != NULL) {
    340     gBS->CloseProtocol(ImageHandle,
    341                        &gEfiShellProtocolGuid,
    342                        ImageHandle,
    343                        NULL);
    344     gEfiShellProtocol = NULL;
    345   }
    346   if (gEfiShellParametersProtocol != NULL) {
    347     gBS->CloseProtocol(ImageHandle,
    348                        &gEfiShellParametersProtocolGuid,
    349                        ImageHandle,
    350                        NULL);
    351     gEfiShellParametersProtocol = NULL;
    352   }
    353   mEfiShellEnvironment2Handle = NULL;
    354 
    355   return (EFI_SUCCESS);
    356 }
    357 
    358 /**
    359   This function causes the shell library to initialize itself.  If the shell library
    360   is already initialized it will de-initialize all the current protocol poitners and
    361   re-populate them again.
    362 
    363   When the library is used with PcdShellLibAutoInitialize set to true this function
    364   will return EFI_SUCCESS and perform no actions.
    365 
    366   This function is intended for internal access for shell commands only.
    367 
    368   @retval EFI_SUCCESS   the initialization was complete sucessfully
    369 
    370 **/
    371 EFI_STATUS
    372 EFIAPI
    373 ShellInitialize (
    374   )
    375 {
    376   EFI_STATUS Status;
    377 
    378   //
    379   // if auto initialize is not false then skip
    380   //
    381   if (PcdGetBool(PcdShellLibAutoInitialize) != 0) {
    382     return (EFI_SUCCESS);
    383   }
    384 
    385   //
    386   // deinit the current stuff
    387   //
    388   Status = ShellLibDestructor (gImageHandle, gST);
    389   ASSERT_EFI_ERROR (Status);
    390 
    391   //
    392   // init the new stuff
    393   //
    394   return (ShellLibConstructorWorker(gImageHandle, gST));
    395 }
    396 
    397 /**
    398   This function will retrieve the information about the file for the handle
    399   specified and store it in allocated pool memory.
    400 
    401   This function allocates a buffer to store the file's information. It is the
    402   caller's responsibility to free the buffer
    403 
    404   @param  FileHandle  The file handle of the file for which information is
    405   being requested.
    406 
    407   @retval NULL information could not be retrieved.
    408 
    409   @return the information about the file
    410 **/
    411 EFI_FILE_INFO*
    412 EFIAPI
    413 ShellGetFileInfo (
    414   IN SHELL_FILE_HANDLE                     FileHandle
    415   )
    416 {
    417   return (FileFunctionMap.GetFileInfo(FileHandle));
    418 }
    419 
    420 /**
    421   This function sets the information about the file for the opened handle
    422   specified.
    423 
    424   @param[in]  FileHandle        The file handle of the file for which information
    425                                 is being set.
    426 
    427   @param[in]  FileInfo          The information to set.
    428 
    429   @retval EFI_SUCCESS           The information was set.
    430   @retval EFI_INVALID_PARAMETER A parameter was out of range or invalid.
    431   @retval EFI_UNSUPPORTED       The FileHandle does not support FileInfo.
    432   @retval EFI_NO_MEDIA          The device has no medium.
    433   @retval EFI_DEVICE_ERROR      The device reported an error.
    434   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
    435   @retval EFI_WRITE_PROTECTED   The file or medium is write protected.
    436   @retval EFI_ACCESS_DENIED     The file was opened read only.
    437   @retval EFI_VOLUME_FULL       The volume is full.
    438 **/
    439 EFI_STATUS
    440 EFIAPI
    441 ShellSetFileInfo (
    442   IN SHELL_FILE_HANDLE                    FileHandle,
    443   IN EFI_FILE_INFO              *FileInfo
    444   )
    445 {
    446   return (FileFunctionMap.SetFileInfo(FileHandle, FileInfo));
    447 }
    448 
    449   /**
    450   This function will open a file or directory referenced by DevicePath.
    451 
    452   This function opens a file with the open mode according to the file path. The
    453   Attributes is valid only for EFI_FILE_MODE_CREATE.
    454 
    455   @param  FilePath        on input the device path to the file.  On output
    456                           the remaining device path.
    457   @param  DeviceHandle    pointer to the system device handle.
    458   @param  FileHandle      pointer to the file handle.
    459   @param  OpenMode        the mode to open the file with.
    460   @param  Attributes      the file's file attributes.
    461 
    462   @retval EFI_SUCCESS           The information was set.
    463   @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
    464   @retval EFI_UNSUPPORTED       Could not open the file path.
    465   @retval EFI_NOT_FOUND         The specified file could not be found on the
    466                                 device or the file system could not be found on
    467                                 the device.
    468   @retval EFI_NO_MEDIA          The device has no medium.
    469   @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the
    470                                 medium is no longer supported.
    471   @retval EFI_DEVICE_ERROR      The device reported an error.
    472   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
    473   @retval EFI_WRITE_PROTECTED   The file or medium is write protected.
    474   @retval EFI_ACCESS_DENIED     The file was opened read only.
    475   @retval EFI_OUT_OF_RESOURCES  Not enough resources were available to open the
    476                                 file.
    477   @retval EFI_VOLUME_FULL       The volume is full.
    478 **/
    479 EFI_STATUS
    480 EFIAPI
    481 ShellOpenFileByDevicePath(
    482   IN OUT EFI_DEVICE_PATH_PROTOCOL     **FilePath,
    483   OUT EFI_HANDLE                      *DeviceHandle,
    484   OUT SHELL_FILE_HANDLE               *FileHandle,
    485   IN UINT64                           OpenMode,
    486   IN UINT64                           Attributes
    487   )
    488 {
    489   CHAR16                          *FileName;
    490   EFI_STATUS                      Status;
    491   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *EfiSimpleFileSystemProtocol;
    492   EFI_FILE_PROTOCOL               *Handle1;
    493   EFI_FILE_PROTOCOL               *Handle2;
    494   CHAR16                          *FnafPathName;
    495   UINTN                           PathLen;
    496 
    497   if (FilePath == NULL || FileHandle == NULL || DeviceHandle == NULL) {
    498     return (EFI_INVALID_PARAMETER);
    499   }
    500 
    501   //
    502   // which shell interface should we use
    503   //
    504   if (gEfiShellProtocol != NULL) {
    505     //
    506     // use UEFI Shell 2.0 method.
    507     //
    508     FileName = gEfiShellProtocol->GetFilePathFromDevicePath(*FilePath);
    509     if (FileName == NULL) {
    510       return (EFI_INVALID_PARAMETER);
    511     }
    512     Status = ShellOpenFileByName(FileName, FileHandle, OpenMode, Attributes);
    513     FreePool(FileName);
    514     return (Status);
    515   }
    516 
    517 
    518   //
    519   // use old shell method.
    520   //
    521   Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid,
    522                                   FilePath,
    523                                   DeviceHandle);
    524   if (EFI_ERROR (Status)) {
    525     return Status;
    526   }
    527   Status = gBS->OpenProtocol(*DeviceHandle,
    528                              &gEfiSimpleFileSystemProtocolGuid,
    529                              (VOID**)&EfiSimpleFileSystemProtocol,
    530                              gImageHandle,
    531                              NULL,
    532                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    533   if (EFI_ERROR (Status)) {
    534     return Status;
    535   }
    536   Status = EfiSimpleFileSystemProtocol->OpenVolume(EfiSimpleFileSystemProtocol, &Handle1);
    537   if (EFI_ERROR (Status)) {
    538     FileHandle = NULL;
    539     return Status;
    540   }
    541 
    542   //
    543   // go down directories one node at a time.
    544   //
    545   while (!IsDevicePathEnd (*FilePath)) {
    546     //
    547     // For file system access each node should be a file path component
    548     //
    549     if (DevicePathType    (*FilePath) != MEDIA_DEVICE_PATH ||
    550         DevicePathSubType (*FilePath) != MEDIA_FILEPATH_DP
    551        ) {
    552       FileHandle = NULL;
    553       return (EFI_INVALID_PARAMETER);
    554     }
    555     //
    556     // Open this file path node
    557     //
    558     Handle2  = Handle1;
    559     Handle1 = NULL;
    560 
    561     //
    562     // File Name Alignment Fix (FNAF)
    563     // Handle2->Open may be incapable of handling a unaligned CHAR16 data.
    564     // The structure pointed to by FilePath may be not CHAR16 aligned.
    565     // This code copies the potentially unaligned PathName data from the
    566     // FilePath structure to the aligned FnafPathName for use in the
    567     // calls to Handl2->Open.
    568     //
    569 
    570     //
    571     // Determine length of PathName, in bytes.
    572     //
    573     PathLen = DevicePathNodeLength (*FilePath) - SIZE_OF_FILEPATH_DEVICE_PATH;
    574 
    575     //
    576     // Allocate memory for the aligned copy of the string Extra allocation is to allow for forced alignment
    577     // Copy bytes from possibly unaligned location to aligned location
    578     //
    579     FnafPathName = AllocateCopyPool(PathLen, (UINT8 *)((FILEPATH_DEVICE_PATH*)*FilePath)->PathName);
    580     if (FnafPathName == NULL) {
    581       return EFI_OUT_OF_RESOURCES;
    582     }
    583 
    584     //
    585     // Try to test opening an existing file
    586     //
    587     Status = Handle2->Open (
    588                           Handle2,
    589                           &Handle1,
    590                           FnafPathName,
    591                           OpenMode &~EFI_FILE_MODE_CREATE,
    592                           0
    593                          );
    594 
    595     //
    596     // see if the error was that it needs to be created
    597     //
    598     if ((EFI_ERROR (Status)) && (OpenMode != (OpenMode &~EFI_FILE_MODE_CREATE))) {
    599       Status = Handle2->Open (
    600                             Handle2,
    601                             &Handle1,
    602                             FnafPathName,
    603                             OpenMode,
    604                             Attributes
    605                            );
    606     }
    607 
    608     //
    609     // Free the alignment buffer
    610     //
    611     FreePool(FnafPathName);
    612 
    613     //
    614     // Close the last node
    615     //
    616     Handle2->Close (Handle2);
    617 
    618     if (EFI_ERROR(Status)) {
    619       return (Status);
    620     }
    621 
    622     //
    623     // Get the next node
    624     //
    625     *FilePath = NextDevicePathNode (*FilePath);
    626   }
    627 
    628   //
    629   // This is a weak spot since if the undefined SHELL_FILE_HANDLE format changes this must change also!
    630   //
    631   *FileHandle = (VOID*)Handle1;
    632   return (EFI_SUCCESS);
    633 }
    634 
    635 /**
    636   This function will open a file or directory referenced by filename.
    637 
    638   If return is EFI_SUCCESS, the Filehandle is the opened file's handle;
    639   otherwise, the Filehandle is NULL. The Attributes is valid only for
    640   EFI_FILE_MODE_CREATE.
    641 
    642   if FileName is NULL then ASSERT()
    643 
    644   @param  FileName      pointer to file name
    645   @param  FileHandle    pointer to the file handle.
    646   @param  OpenMode      the mode to open the file with.
    647   @param  Attributes    the file's file attributes.
    648 
    649   @retval EFI_SUCCESS           The information was set.
    650   @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
    651   @retval EFI_UNSUPPORTED       Could not open the file path.
    652   @retval EFI_NOT_FOUND         The specified file could not be found on the
    653                                 device or the file system could not be found
    654                                 on the device.
    655   @retval EFI_NO_MEDIA          The device has no medium.
    656   @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the
    657                                 medium is no longer supported.
    658   @retval EFI_DEVICE_ERROR      The device reported an error.
    659   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
    660   @retval EFI_WRITE_PROTECTED   The file or medium is write protected.
    661   @retval EFI_ACCESS_DENIED     The file was opened read only.
    662   @retval EFI_OUT_OF_RESOURCES  Not enough resources were available to open the
    663                                 file.
    664   @retval EFI_VOLUME_FULL       The volume is full.
    665 **/
    666 EFI_STATUS
    667 EFIAPI
    668 ShellOpenFileByName(
    669   IN CONST CHAR16               *FileName,
    670   OUT SHELL_FILE_HANDLE         *FileHandle,
    671   IN UINT64                     OpenMode,
    672   IN UINT64                     Attributes
    673   )
    674 {
    675   EFI_HANDLE                    DeviceHandle;
    676   EFI_DEVICE_PATH_PROTOCOL      *FilePath;
    677   EFI_STATUS                    Status;
    678   EFI_FILE_INFO                 *FileInfo;
    679   CHAR16                        *FileNameCopy;
    680   EFI_STATUS                    Status2;
    681 
    682   //
    683   // ASSERT if FileName is NULL
    684   //
    685   ASSERT(FileName != NULL);
    686 
    687   if (FileName == NULL) {
    688     return (EFI_INVALID_PARAMETER);
    689   }
    690 
    691   if (gEfiShellProtocol != NULL) {
    692     if ((OpenMode & EFI_FILE_MODE_CREATE) == EFI_FILE_MODE_CREATE) {
    693 
    694       //
    695       // Create only a directory
    696       //
    697       if ((Attributes & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY) {
    698         return ShellCreateDirectory(FileName, FileHandle);
    699       }
    700 
    701       //
    702       // Create the directory to create the file in
    703       //
    704       FileNameCopy = AllocateCopyPool (StrSize (FileName), FileName);
    705       if (FileName == NULL) {
    706         return (EFI_OUT_OF_RESOURCES);
    707       }
    708       PathCleanUpDirectories (FileNameCopy);
    709       if (PathRemoveLastItem (FileNameCopy)) {
    710         if (!EFI_ERROR(ShellCreateDirectory (FileNameCopy, FileHandle))) {
    711           ShellCloseFile (FileHandle);
    712         }
    713       }
    714       SHELL_FREE_NON_NULL (FileNameCopy);
    715     }
    716 
    717     //
    718     // Use UEFI Shell 2.0 method to create the file
    719     //
    720     Status = gEfiShellProtocol->OpenFileByName(FileName,
    721                                                FileHandle,
    722                                                OpenMode);
    723     if (EFI_ERROR(Status)) {
    724       return Status;
    725     }
    726 
    727     if (mUnicodeCollationProtocol == NULL) {
    728       Status = gBS->LocateProtocol (&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&mUnicodeCollationProtocol);
    729       if (EFI_ERROR (Status)) {
    730         gEfiShellProtocol->CloseFile (*FileHandle);
    731         return Status;
    732       }
    733     }
    734 
    735     if ((mUnicodeCollationProtocol->StriColl (mUnicodeCollationProtocol, (CHAR16*)FileName, L"NUL") != 0) &&
    736         (mUnicodeCollationProtocol->StriColl (mUnicodeCollationProtocol, (CHAR16*)FileName, L"NULL") != 0) &&
    737          !EFI_ERROR(Status) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)){
    738       FileInfo = FileFunctionMap.GetFileInfo(*FileHandle);
    739       ASSERT(FileInfo != NULL);
    740       FileInfo->Attribute = Attributes;
    741       Status2 = FileFunctionMap.SetFileInfo(*FileHandle, FileInfo);
    742       FreePool(FileInfo);
    743       if (EFI_ERROR (Status2)) {
    744         gEfiShellProtocol->CloseFile(*FileHandle);
    745       }
    746       Status = Status2;
    747     }
    748     return (Status);
    749   }
    750   //
    751   // Using EFI Shell version
    752   // this means convert name to path and call that function
    753   // since this will use EFI method again that will open it.
    754   //
    755   ASSERT(mEfiShellEnvironment2 != NULL);
    756   FilePath = mEfiShellEnvironment2->NameToPath ((CHAR16*)FileName);
    757   if (FilePath != NULL) {
    758     return (ShellOpenFileByDevicePath(&FilePath,
    759                                       &DeviceHandle,
    760                                       FileHandle,
    761                                       OpenMode,
    762                                       Attributes));
    763   }
    764   return (EFI_DEVICE_ERROR);
    765 }
    766 /**
    767   This function create a directory
    768 
    769   If return is EFI_SUCCESS, the Filehandle is the opened directory's handle;
    770   otherwise, the Filehandle is NULL. If the directory already existed, this
    771   function opens the existing directory.
    772 
    773   @param  DirectoryName   pointer to directory name
    774   @param  FileHandle      pointer to the file handle.
    775 
    776   @retval EFI_SUCCESS           The information was set.
    777   @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
    778   @retval EFI_UNSUPPORTED       Could not open the file path.
    779   @retval EFI_NOT_FOUND         The specified file could not be found on the
    780                                 device or the file system could not be found
    781                                 on the device.
    782   @retval EFI_NO_MEDIA          The device has no medium.
    783   @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the
    784                                 medium is no longer supported.
    785   @retval EFI_DEVICE_ERROR      The device reported an error.
    786   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
    787   @retval EFI_WRITE_PROTECTED   The file or medium is write protected.
    788   @retval EFI_ACCESS_DENIED     The file was opened read only.
    789   @retval EFI_OUT_OF_RESOURCES  Not enough resources were available to open the
    790                                 file.
    791   @retval EFI_VOLUME_FULL       The volume is full.
    792   @sa ShellOpenFileByName
    793 **/
    794 EFI_STATUS
    795 EFIAPI
    796 ShellCreateDirectory(
    797   IN CONST CHAR16             *DirectoryName,
    798   OUT SHELL_FILE_HANDLE                  *FileHandle
    799   )
    800 {
    801   if (gEfiShellProtocol != NULL) {
    802     //
    803     // Use UEFI Shell 2.0 method
    804     //
    805     return (gEfiShellProtocol->CreateFile(DirectoryName,
    806                           EFI_FILE_DIRECTORY,
    807                           FileHandle
    808                          ));
    809   } else {
    810     return (ShellOpenFileByName(DirectoryName,
    811                                 FileHandle,
    812                                 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE,
    813                                 EFI_FILE_DIRECTORY
    814                                ));
    815   }
    816 }
    817 
    818 /**
    819   This function reads information from an opened file.
    820 
    821   If FileHandle is not a directory, the function reads the requested number of
    822   bytes from the file at the file's current position and returns them in Buffer.
    823   If the read goes beyond the end of the file, the read length is truncated to the
    824   end of the file. The file's current position is increased by the number of bytes
    825   returned.  If FileHandle is a directory, the function reads the directory entry
    826   at the file's current position and returns the entry in Buffer. If the Buffer
    827   is not large enough to hold the current directory entry, then
    828   EFI_BUFFER_TOO_SMALL is returned and the current file position is not updated.
    829   BufferSize is set to be the size of the buffer needed to read the entry. On
    830   success, the current position is updated to the next directory entry. If there
    831   are no more directory entries, the read returns a zero-length buffer.
    832   EFI_FILE_INFO is the structure returned as the directory entry.
    833 
    834   @param FileHandle             the opened file handle
    835   @param BufferSize             on input the size of buffer in bytes.  on return
    836                                 the number of bytes written.
    837   @param Buffer                 the buffer to put read data into.
    838 
    839   @retval EFI_SUCCESS           Data was read.
    840   @retval EFI_NO_MEDIA          The device has no media.
    841   @retval EFI_DEVICE_ERROR  The device reported an error.
    842   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
    843   @retval EFI_BUFFER_TO_SMALL Buffer is too small. ReadSize contains required
    844                                 size.
    845 
    846 **/
    847 EFI_STATUS
    848 EFIAPI
    849 ShellReadFile(
    850   IN SHELL_FILE_HANDLE                     FileHandle,
    851   IN OUT UINTN                  *BufferSize,
    852   OUT VOID                      *Buffer
    853   )
    854 {
    855   return (FileFunctionMap.ReadFile(FileHandle, BufferSize, Buffer));
    856 }
    857 
    858 
    859 /**
    860   Write data to a file.
    861 
    862   This function writes the specified number of bytes to the file at the current
    863   file position. The current file position is advanced the actual number of bytes
    864   written, which is returned in BufferSize. Partial writes only occur when there
    865   has been a data error during the write attempt (such as "volume space full").
    866   The file is automatically grown to hold the data if required. Direct writes to
    867   opened directories are not supported.
    868 
    869   @param FileHandle           The opened file for writing
    870   @param BufferSize           on input the number of bytes in Buffer.  On output
    871                               the number of bytes written.
    872   @param Buffer               the buffer containing data to write is stored.
    873 
    874  @retval EFI_SUCCESS          Data was written.
    875  @retval EFI_UNSUPPORTED      Writes to an open directory are not supported.
    876  @retval EFI_NO_MEDIA         The device has no media.
    877  @retval EFI_DEVICE_ERROR     The device reported an error.
    878  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
    879  @retval EFI_WRITE_PROTECTED  The device is write-protected.
    880  @retval EFI_ACCESS_DENIED    The file was open for read only.
    881  @retval EFI_VOLUME_FULL      The volume is full.
    882 **/
    883 EFI_STATUS
    884 EFIAPI
    885 ShellWriteFile(
    886   IN SHELL_FILE_HANDLE          FileHandle,
    887   IN OUT UINTN                  *BufferSize,
    888   IN VOID                       *Buffer
    889   )
    890 {
    891   return (FileFunctionMap.WriteFile(FileHandle, BufferSize, Buffer));
    892 }
    893 
    894 /**
    895   Close an open file handle.
    896 
    897   This function closes a specified file handle. All "dirty" cached file data is
    898   flushed to the device, and the file is closed. In all cases the handle is
    899   closed.
    900 
    901 @param FileHandle               the file handle to close.
    902 
    903 @retval EFI_SUCCESS             the file handle was closed sucessfully.
    904 **/
    905 EFI_STATUS
    906 EFIAPI
    907 ShellCloseFile (
    908   IN SHELL_FILE_HANDLE                     *FileHandle
    909   )
    910 {
    911   return (FileFunctionMap.CloseFile(*FileHandle));
    912 }
    913 
    914 /**
    915   Delete a file and close the handle
    916 
    917   This function closes and deletes a file. In all cases the file handle is closed.
    918   If the file cannot be deleted, the warning code EFI_WARN_DELETE_FAILURE is
    919   returned, but the handle is still closed.
    920 
    921   @param FileHandle             the file handle to delete
    922 
    923   @retval EFI_SUCCESS           the file was closed sucessfully
    924   @retval EFI_WARN_DELETE_FAILURE the handle was closed, but the file was not
    925                                 deleted
    926   @retval INVALID_PARAMETER     One of the parameters has an invalid value.
    927 **/
    928 EFI_STATUS
    929 EFIAPI
    930 ShellDeleteFile (
    931   IN SHELL_FILE_HANDLE            *FileHandle
    932   )
    933 {
    934   return (FileFunctionMap.DeleteFile(*FileHandle));
    935 }
    936 
    937 /**
    938   Set the current position in a file.
    939 
    940   This function sets the current file position for the handle to the position
    941   supplied. With the exception of seeking to position 0xFFFFFFFFFFFFFFFF, only
    942   absolute positioning is supported, and seeking past the end of the file is
    943   allowed (a subsequent write would grow the file). Seeking to position
    944   0xFFFFFFFFFFFFFFFF causes the current position to be set to the end of the file.
    945   If FileHandle is a directory, the only position that may be set is zero. This
    946   has the effect of starting the read process of the directory entries over.
    947 
    948   @param FileHandle             The file handle on which the position is being set
    949   @param Position               Byte position from begining of file
    950 
    951   @retval EFI_SUCCESS           Operation completed sucessfully.
    952   @retval EFI_UNSUPPORTED       the seek request for non-zero is not valid on
    953                                 directories.
    954   @retval INVALID_PARAMETER     One of the parameters has an invalid value.
    955 **/
    956 EFI_STATUS
    957 EFIAPI
    958 ShellSetFilePosition (
    959   IN SHELL_FILE_HANDLE              FileHandle,
    960   IN UINT64             Position
    961   )
    962 {
    963   return (FileFunctionMap.SetFilePosition(FileHandle, Position));
    964 }
    965 
    966 /**
    967   Gets a file's current position
    968 
    969   This function retrieves the current file position for the file handle. For
    970   directories, the current file position has no meaning outside of the file
    971   system driver and as such the operation is not supported. An error is returned
    972   if FileHandle is a directory.
    973 
    974   @param FileHandle             The open file handle on which to get the position.
    975   @param Position               Byte position from begining of file.
    976 
    977   @retval EFI_SUCCESS           the operation completed sucessfully.
    978   @retval INVALID_PARAMETER     One of the parameters has an invalid value.
    979   @retval EFI_UNSUPPORTED       the request is not valid on directories.
    980 **/
    981 EFI_STATUS
    982 EFIAPI
    983 ShellGetFilePosition (
    984   IN SHELL_FILE_HANDLE                     FileHandle,
    985   OUT UINT64                    *Position
    986   )
    987 {
    988   return (FileFunctionMap.GetFilePosition(FileHandle, Position));
    989 }
    990 /**
    991   Flushes data on a file
    992 
    993   This function flushes all modified data associated with a file to a device.
    994 
    995   @param FileHandle             The file handle on which to flush data
    996 
    997   @retval EFI_SUCCESS           The data was flushed.
    998   @retval EFI_NO_MEDIA          The device has no media.
    999   @retval EFI_DEVICE_ERROR      The device reported an error.
   1000   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
   1001   @retval EFI_WRITE_PROTECTED   The file or medium is write protected.
   1002   @retval EFI_ACCESS_DENIED     The file was opened for read only.
   1003 **/
   1004 EFI_STATUS
   1005 EFIAPI
   1006 ShellFlushFile (
   1007   IN SHELL_FILE_HANDLE                     FileHandle
   1008   )
   1009 {
   1010   return (FileFunctionMap.FlushFile(FileHandle));
   1011 }
   1012 
   1013 /** Retrieve first entry from a directory.
   1014 
   1015   This function takes an open directory handle and gets information from the
   1016   first entry in the directory.  A buffer is allocated to contain
   1017   the information and a pointer to the buffer is returned in *Buffer.  The
   1018   caller can use ShellFindNextFile() to get subsequent directory entries.
   1019 
   1020   The buffer will be freed by ShellFindNextFile() when the last directory
   1021   entry is read.  Otherwise, the caller must free the buffer, using FreePool,
   1022   when finished with it.
   1023 
   1024   @param[in]  DirHandle         The file handle of the directory to search.
   1025   @param[out] Buffer            The pointer to the buffer for the file's information.
   1026 
   1027   @retval EFI_SUCCESS           Found the first file.
   1028   @retval EFI_NOT_FOUND         Cannot find the directory.
   1029   @retval EFI_NO_MEDIA          The device has no media.
   1030   @retval EFI_DEVICE_ERROR      The device reported an error.
   1031   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
   1032   @return Others                status of ShellGetFileInfo, ShellSetFilePosition,
   1033                                 or ShellReadFile
   1034 **/
   1035 EFI_STATUS
   1036 EFIAPI
   1037 ShellFindFirstFile (
   1038   IN SHELL_FILE_HANDLE                     DirHandle,
   1039   OUT EFI_FILE_INFO             **Buffer
   1040   )
   1041 {
   1042   //
   1043   // pass to file handle lib
   1044   //
   1045   return (FileHandleFindFirstFile(DirHandle, Buffer));
   1046 }
   1047 /** Retrieve next entries from a directory.
   1048 
   1049   To use this function, the caller must first call the ShellFindFirstFile()
   1050   function to get the first directory entry.  Subsequent directory entries are
   1051   retrieved by using the ShellFindNextFile() function.  This function can
   1052   be called several times to get each entry from the directory.  If the call of
   1053   ShellFindNextFile() retrieved the last directory entry, the next call of
   1054   this function will set *NoFile to TRUE and free the buffer.
   1055 
   1056   @param[in]  DirHandle         The file handle of the directory.
   1057   @param[out] Buffer            The pointer to buffer for file's information.
   1058   @param[out] NoFile            The pointer to boolean when last file is found.
   1059 
   1060   @retval EFI_SUCCESS           Found the next file, or reached last file
   1061   @retval EFI_NO_MEDIA          The device has no media.
   1062   @retval EFI_DEVICE_ERROR      The device reported an error.
   1063   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
   1064 **/
   1065 EFI_STATUS
   1066 EFIAPI
   1067 ShellFindNextFile(
   1068   IN SHELL_FILE_HANDLE                      DirHandle,
   1069   OUT EFI_FILE_INFO              *Buffer,
   1070   OUT BOOLEAN                    *NoFile
   1071   )
   1072 {
   1073   //
   1074   // pass to file handle lib
   1075   //
   1076   return (FileHandleFindNextFile(DirHandle, Buffer, NoFile));
   1077 }
   1078 /**
   1079   Retrieve the size of a file.
   1080 
   1081   if FileHandle is NULL then ASSERT()
   1082   if Size is NULL then ASSERT()
   1083 
   1084   This function extracts the file size info from the FileHandle's EFI_FILE_INFO
   1085   data.
   1086 
   1087   @param FileHandle             file handle from which size is retrieved
   1088   @param Size                   pointer to size
   1089 
   1090   @retval EFI_SUCCESS           operation was completed sucessfully
   1091   @retval EFI_DEVICE_ERROR      cannot access the file
   1092 **/
   1093 EFI_STATUS
   1094 EFIAPI
   1095 ShellGetFileSize (
   1096   IN SHELL_FILE_HANDLE                     FileHandle,
   1097   OUT UINT64                    *Size
   1098   )
   1099 {
   1100   return (FileFunctionMap.GetFileSize(FileHandle, Size));
   1101 }
   1102 /**
   1103   Retrieves the status of the break execution flag
   1104 
   1105   this function is useful to check whether the application is being asked to halt by the shell.
   1106 
   1107   @retval TRUE                  the execution break is enabled
   1108   @retval FALSE                 the execution break is not enabled
   1109 **/
   1110 BOOLEAN
   1111 EFIAPI
   1112 ShellGetExecutionBreakFlag(
   1113   VOID
   1114   )
   1115 {
   1116   //
   1117   // Check for UEFI Shell 2.0 protocols
   1118   //
   1119   if (gEfiShellProtocol != NULL) {
   1120 
   1121     //
   1122     // We are using UEFI Shell 2.0; see if the event has been triggered
   1123     //
   1124     if (gBS->CheckEvent(gEfiShellProtocol->ExecutionBreak) != EFI_SUCCESS) {
   1125       return (FALSE);
   1126     }
   1127     return (TRUE);
   1128   }
   1129 
   1130   //
   1131   // using EFI Shell; call the function to check
   1132   //
   1133   if (mEfiShellEnvironment2 != NULL) {
   1134     return (mEfiShellEnvironment2->GetExecutionBreak());
   1135   }
   1136 
   1137   return (FALSE);
   1138 }
   1139 /**
   1140   return the value of an environment variable
   1141 
   1142   this function gets the value of the environment variable set by the
   1143   ShellSetEnvironmentVariable function
   1144 
   1145   @param EnvKey                 The key name of the environment variable.
   1146 
   1147   @retval NULL                  the named environment variable does not exist.
   1148   @return != NULL               pointer to the value of the environment variable
   1149 **/
   1150 CONST CHAR16*
   1151 EFIAPI
   1152 ShellGetEnvironmentVariable (
   1153   IN CONST CHAR16                *EnvKey
   1154   )
   1155 {
   1156   //
   1157   // Check for UEFI Shell 2.0 protocols
   1158   //
   1159   if (gEfiShellProtocol != NULL) {
   1160     return (gEfiShellProtocol->GetEnv(EnvKey));
   1161   }
   1162 
   1163   //
   1164   // Check for EFI shell
   1165   //
   1166   if (mEfiShellEnvironment2 != NULL) {
   1167     return (mEfiShellEnvironment2->GetEnv((CHAR16*)EnvKey));
   1168   }
   1169 
   1170   return NULL;
   1171 }
   1172 /**
   1173   set the value of an environment variable
   1174 
   1175 This function changes the current value of the specified environment variable. If the
   1176 environment variable exists and the Value is an empty string, then the environment
   1177 variable is deleted. If the environment variable exists and the Value is not an empty
   1178 string, then the value of the environment variable is changed. If the environment
   1179 variable does not exist and the Value is an empty string, there is no action. If the
   1180 environment variable does not exist and the Value is a non-empty string, then the
   1181 environment variable is created and assigned the specified value.
   1182 
   1183   This is not supported pre-UEFI Shell 2.0.
   1184 
   1185   @param EnvKey                 The key name of the environment variable.
   1186   @param EnvVal                 The Value of the environment variable
   1187   @param Volatile               Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
   1188 
   1189   @retval EFI_SUCCESS           the operation was completed sucessfully
   1190   @retval EFI_UNSUPPORTED       This operation is not allowed in pre UEFI 2.0 Shell environments
   1191 **/
   1192 EFI_STATUS
   1193 EFIAPI
   1194 ShellSetEnvironmentVariable (
   1195   IN CONST CHAR16               *EnvKey,
   1196   IN CONST CHAR16               *EnvVal,
   1197   IN BOOLEAN                    Volatile
   1198   )
   1199 {
   1200   //
   1201   // Check for UEFI Shell 2.0 protocols
   1202   //
   1203   if (gEfiShellProtocol != NULL) {
   1204     return (gEfiShellProtocol->SetEnv(EnvKey, EnvVal, Volatile));
   1205   }
   1206 
   1207   //
   1208   // This feature does not exist under EFI shell
   1209   //
   1210   return (EFI_UNSUPPORTED);
   1211 }
   1212 
   1213 /**
   1214   Cause the shell to parse and execute a command line.
   1215 
   1216   This function creates a nested instance of the shell and executes the specified
   1217   command (CommandLine) with the specified environment (Environment). Upon return,
   1218   the status code returned by the specified command is placed in StatusCode.
   1219   If Environment is NULL, then the current environment is used and all changes made
   1220   by the commands executed will be reflected in the current environment. If the
   1221   Environment is non-NULL, then the changes made will be discarded.
   1222   The CommandLine is executed from the current working directory on the current
   1223   device.
   1224 
   1225   The EnvironmentVariables pararemeter is ignored in a pre-UEFI Shell 2.0
   1226   environment.  The values pointed to by the parameters will be unchanged by the
   1227   ShellExecute() function.  The Output parameter has no effect in a
   1228   UEFI Shell 2.0 environment.
   1229 
   1230   @param[in] ParentHandle         The parent image starting the operation.
   1231   @param[in] CommandLine          The pointer to a NULL terminated command line.
   1232   @param[in] Output               True to display debug output.  False to hide it.
   1233   @param[in] EnvironmentVariables Optional pointer to array of environment variables
   1234                                   in the form "x=y".  If NULL, the current set is used.
   1235   @param[out] Status              The status of the run command line.
   1236 
   1237   @retval EFI_SUCCESS             The operation completed sucessfully.  Status
   1238                                   contains the status code returned.
   1239   @retval EFI_INVALID_PARAMETER   A parameter contains an invalid value.
   1240   @retval EFI_OUT_OF_RESOURCES    Out of resources.
   1241   @retval EFI_UNSUPPORTED         The operation is not allowed.
   1242 **/
   1243 EFI_STATUS
   1244 EFIAPI
   1245 ShellExecute (
   1246   IN EFI_HANDLE                 *ParentHandle,
   1247   IN CHAR16                     *CommandLine OPTIONAL,
   1248   IN BOOLEAN                    Output OPTIONAL,
   1249   IN CHAR16                     **EnvironmentVariables OPTIONAL,
   1250   OUT EFI_STATUS                *Status OPTIONAL
   1251   )
   1252 {
   1253   EFI_STATUS                CmdStatus;
   1254   //
   1255   // Check for UEFI Shell 2.0 protocols
   1256   //
   1257   if (gEfiShellProtocol != NULL) {
   1258     //
   1259     // Call UEFI Shell 2.0 version (not using Output parameter)
   1260     //
   1261     return (gEfiShellProtocol->Execute(ParentHandle,
   1262                                       CommandLine,
   1263                                       EnvironmentVariables,
   1264                                       Status));
   1265   }
   1266 
   1267   //
   1268   // Check for EFI shell
   1269   //
   1270   if (mEfiShellEnvironment2 != NULL) {
   1271     //
   1272     // Call EFI Shell version.
   1273     // Due to oddity in the EFI shell we want to dereference the ParentHandle here
   1274     //
   1275     CmdStatus = (mEfiShellEnvironment2->Execute(*ParentHandle,
   1276                                           CommandLine,
   1277                                           Output));
   1278     //
   1279     // No Status output parameter so just use the returned status
   1280     //
   1281     if (Status != NULL) {
   1282       *Status = CmdStatus;
   1283     }
   1284     //
   1285     // If there was an error, we can't tell if it was from the command or from
   1286     // the Execute() function, so we'll just assume the shell ran successfully
   1287     // and the error came from the command.
   1288     //
   1289     return EFI_SUCCESS;
   1290   }
   1291 
   1292   return (EFI_UNSUPPORTED);
   1293 }
   1294 
   1295 /**
   1296   Retreives the current directory path
   1297 
   1298   If the DeviceName is NULL, it returns the current device's current directory
   1299   name. If the DeviceName is not NULL, it returns the current directory name
   1300   on specified drive.
   1301 
   1302   Note that the current directory string should exclude the tailing backslash character.
   1303 
   1304   @param DeviceName             the name of the drive to get directory on
   1305 
   1306   @retval NULL                  the directory does not exist
   1307   @return != NULL               the directory
   1308 **/
   1309 CONST CHAR16*
   1310 EFIAPI
   1311 ShellGetCurrentDir (
   1312   IN CHAR16                     * CONST DeviceName OPTIONAL
   1313   )
   1314 {
   1315   //
   1316   // Check for UEFI Shell 2.0 protocols
   1317   //
   1318   if (gEfiShellProtocol != NULL) {
   1319     return (gEfiShellProtocol->GetCurDir(DeviceName));
   1320   }
   1321 
   1322   //
   1323   // Check for EFI shell
   1324   //
   1325   if (mEfiShellEnvironment2 != NULL) {
   1326     return (mEfiShellEnvironment2->CurDir(DeviceName));
   1327   }
   1328 
   1329   return (NULL);
   1330 }
   1331 /**
   1332   sets (enabled or disabled) the page break mode
   1333 
   1334   when page break mode is enabled the screen will stop scrolling
   1335   and wait for operator input before scrolling a subsequent screen.
   1336 
   1337   @param CurrentState           TRUE to enable and FALSE to disable
   1338 **/
   1339 VOID
   1340 EFIAPI
   1341 ShellSetPageBreakMode (
   1342   IN BOOLEAN                    CurrentState
   1343   )
   1344 {
   1345   //
   1346   // check for enabling
   1347   //
   1348   if (CurrentState != 0x00) {
   1349     //
   1350     // check for UEFI Shell 2.0
   1351     //
   1352     if (gEfiShellProtocol != NULL) {
   1353       //
   1354       // Enable with UEFI 2.0 Shell
   1355       //
   1356       gEfiShellProtocol->EnablePageBreak();
   1357       return;
   1358     } else {
   1359       //
   1360       // Check for EFI shell
   1361       //
   1362       if (mEfiShellEnvironment2 != NULL) {
   1363         //
   1364         // Enable with EFI Shell
   1365         //
   1366         mEfiShellEnvironment2->EnablePageBreak (DEFAULT_INIT_ROW, DEFAULT_AUTO_LF);
   1367         return;
   1368       }
   1369     }
   1370   } else {
   1371     //
   1372     // check for UEFI Shell 2.0
   1373     //
   1374     if (gEfiShellProtocol != NULL) {
   1375       //
   1376       // Disable with UEFI 2.0 Shell
   1377       //
   1378       gEfiShellProtocol->DisablePageBreak();
   1379       return;
   1380     } else {
   1381       //
   1382       // Check for EFI shell
   1383       //
   1384       if (mEfiShellEnvironment2 != NULL) {
   1385         //
   1386         // Disable with EFI Shell
   1387         //
   1388         mEfiShellEnvironment2->DisablePageBreak ();
   1389         return;
   1390       }
   1391     }
   1392   }
   1393 }
   1394 
   1395 ///
   1396 /// version of EFI_SHELL_FILE_INFO struct, except has no CONST pointers.
   1397 /// This allows for the struct to be populated.
   1398 ///
   1399 typedef struct {
   1400   LIST_ENTRY Link;
   1401   EFI_STATUS Status;
   1402   CHAR16 *FullName;
   1403   CHAR16 *FileName;
   1404   SHELL_FILE_HANDLE          Handle;
   1405   EFI_FILE_INFO *Info;
   1406 } EFI_SHELL_FILE_INFO_NO_CONST;
   1407 
   1408 /**
   1409   Converts a EFI shell list of structures to the coresponding UEFI Shell 2.0 type of list.
   1410 
   1411   if OldStyleFileList is NULL then ASSERT()
   1412 
   1413   this function will convert a SHELL_FILE_ARG based list into a callee allocated
   1414   EFI_SHELL_FILE_INFO based list.  it is up to the caller to free the memory via
   1415   the ShellCloseFileMetaArg function.
   1416 
   1417   @param[in] FileList           the EFI shell list type
   1418   @param[in, out] ListHead      the list to add to
   1419 
   1420   @retval the resultant head of the double linked new format list;
   1421 **/
   1422 LIST_ENTRY*
   1423 InternalShellConvertFileListType (
   1424   IN LIST_ENTRY                 *FileList,
   1425   IN OUT LIST_ENTRY             *ListHead
   1426   )
   1427 {
   1428   SHELL_FILE_ARG                *OldInfo;
   1429   LIST_ENTRY                    *Link;
   1430   EFI_SHELL_FILE_INFO_NO_CONST  *NewInfo;
   1431 
   1432   //
   1433   // ASSERTs
   1434   //
   1435   ASSERT(FileList  != NULL);
   1436   ASSERT(ListHead  != NULL);
   1437 
   1438   //
   1439   // enumerate through each member of the old list and copy
   1440   //
   1441   for (Link = FileList->ForwardLink; Link != FileList; Link = Link->ForwardLink) {
   1442     OldInfo = CR (Link, SHELL_FILE_ARG, Link, SHELL_FILE_ARG_SIGNATURE);
   1443     ASSERT(OldInfo           != NULL);
   1444 
   1445     //
   1446     // Skip ones that failed to open...
   1447     //
   1448     if (OldInfo->Status != EFI_SUCCESS) {
   1449       continue;
   1450     }
   1451 
   1452     //
   1453     // make sure the old list was valid
   1454     //
   1455     ASSERT(OldInfo->Info     != NULL);
   1456     ASSERT(OldInfo->FullName != NULL);
   1457     ASSERT(OldInfo->FileName != NULL);
   1458 
   1459     //
   1460     // allocate a new EFI_SHELL_FILE_INFO object
   1461     //
   1462     NewInfo               = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
   1463     if (NewInfo == NULL) {
   1464       ShellCloseFileMetaArg((EFI_SHELL_FILE_INFO**)(&ListHead));
   1465       ListHead = NULL;
   1466       break;
   1467     }
   1468 
   1469     //
   1470     // copy the simple items
   1471     //
   1472     NewInfo->Handle       = OldInfo->Handle;
   1473     NewInfo->Status       = OldInfo->Status;
   1474 
   1475     // old shell checks for 0 not NULL
   1476     OldInfo->Handle = 0;
   1477 
   1478     //
   1479     // allocate new space to copy strings and structure
   1480     //
   1481     NewInfo->FullName     = AllocateCopyPool(StrSize(OldInfo->FullName), OldInfo->FullName);
   1482     NewInfo->FileName     = AllocateCopyPool(StrSize(OldInfo->FileName), OldInfo->FileName);
   1483     NewInfo->Info         = AllocateCopyPool((UINTN)OldInfo->Info->Size, OldInfo->Info);
   1484 
   1485     //
   1486     // make sure all the memory allocations were sucessful
   1487     //
   1488     if (NULL == NewInfo->FullName || NewInfo->FileName == NULL || NewInfo->Info == NULL) {
   1489       //
   1490       // Free the partially allocated new node
   1491       //
   1492       SHELL_FREE_NON_NULL(NewInfo->FullName);
   1493       SHELL_FREE_NON_NULL(NewInfo->FileName);
   1494       SHELL_FREE_NON_NULL(NewInfo->Info);
   1495       SHELL_FREE_NON_NULL(NewInfo);
   1496 
   1497       //
   1498       // Free the previously converted stuff
   1499       //
   1500       ShellCloseFileMetaArg((EFI_SHELL_FILE_INFO**)(&ListHead));
   1501       ListHead = NULL;
   1502       break;
   1503     }
   1504 
   1505     //
   1506     // add that to the list
   1507     //
   1508     InsertTailList(ListHead, &NewInfo->Link);
   1509   }
   1510   return (ListHead);
   1511 }
   1512 /**
   1513   Opens a group of files based on a path.
   1514 
   1515   This function uses the Arg to open all the matching files. Each matched
   1516   file has a SHELL_FILE_INFO structure to record the file information. These
   1517   structures are placed on the list ListHead. Users can get the SHELL_FILE_INFO
   1518   structures from ListHead to access each file. This function supports wildcards
   1519   and will process '?' and '*' as such.  the list must be freed with a call to
   1520   ShellCloseFileMetaArg().
   1521 
   1522   If you are NOT appending to an existing list *ListHead must be NULL.  If
   1523   *ListHead is NULL then it must be callee freed.
   1524 
   1525   @param Arg                    pointer to path string
   1526   @param OpenMode               mode to open files with
   1527   @param ListHead               head of linked list of results
   1528 
   1529   @retval EFI_SUCCESS           the operation was sucessful and the list head
   1530                                 contains the list of opened files
   1531   @return != EFI_SUCCESS        the operation failed
   1532 
   1533   @sa InternalShellConvertFileListType
   1534 **/
   1535 EFI_STATUS
   1536 EFIAPI
   1537 ShellOpenFileMetaArg (
   1538   IN CHAR16                     *Arg,
   1539   IN UINT64                     OpenMode,
   1540   IN OUT EFI_SHELL_FILE_INFO    **ListHead
   1541   )
   1542 {
   1543   EFI_STATUS                    Status;
   1544   LIST_ENTRY                    mOldStyleFileList;
   1545   CHAR16                        *CleanFilePathStr;
   1546 
   1547   //
   1548   // ASSERT that Arg and ListHead are not NULL
   1549   //
   1550   ASSERT(Arg      != NULL);
   1551   ASSERT(ListHead != NULL);
   1552 
   1553   CleanFilePathStr = NULL;
   1554 
   1555   Status = InternalShellStripQuotes (Arg, &CleanFilePathStr);
   1556   if (EFI_ERROR (Status)) {
   1557     return Status;
   1558   }
   1559 
   1560   //
   1561   // Check for UEFI Shell 2.0 protocols
   1562   //
   1563   if (gEfiShellProtocol != NULL) {
   1564     if (*ListHead == NULL) {
   1565       *ListHead = (EFI_SHELL_FILE_INFO*)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
   1566       if (*ListHead == NULL) {
   1567         FreePool(CleanFilePathStr);
   1568         return (EFI_OUT_OF_RESOURCES);
   1569       }
   1570       InitializeListHead(&((*ListHead)->Link));
   1571     }
   1572     Status = gEfiShellProtocol->OpenFileList(CleanFilePathStr,
   1573                                            OpenMode,
   1574                                            ListHead);
   1575     if (EFI_ERROR(Status)) {
   1576       gEfiShellProtocol->RemoveDupInFileList(ListHead);
   1577     } else {
   1578       Status = gEfiShellProtocol->RemoveDupInFileList(ListHead);
   1579     }
   1580     if (*ListHead != NULL && IsListEmpty(&(*ListHead)->Link)) {
   1581       FreePool(*ListHead);
   1582       FreePool(CleanFilePathStr);
   1583       *ListHead = NULL;
   1584       return (EFI_NOT_FOUND);
   1585     }
   1586     FreePool(CleanFilePathStr);
   1587     return (Status);
   1588   }
   1589 
   1590   //
   1591   // Check for EFI shell
   1592   //
   1593   if (mEfiShellEnvironment2 != NULL) {
   1594     //
   1595     // make sure the list head is initialized
   1596     //
   1597     InitializeListHead(&mOldStyleFileList);
   1598 
   1599     //
   1600     // Get the EFI Shell list of files
   1601     //
   1602     Status = mEfiShellEnvironment2->FileMetaArg(CleanFilePathStr, &mOldStyleFileList);
   1603     if (EFI_ERROR(Status)) {
   1604       *ListHead = NULL;
   1605       FreePool(CleanFilePathStr);
   1606       return (Status);
   1607     }
   1608 
   1609     if (*ListHead == NULL) {
   1610       *ListHead = (EFI_SHELL_FILE_INFO    *)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
   1611       if (*ListHead == NULL) {
   1612         FreePool(CleanFilePathStr);
   1613         return (EFI_OUT_OF_RESOURCES);
   1614       }
   1615       InitializeListHead(&((*ListHead)->Link));
   1616     }
   1617 
   1618     //
   1619     // Convert that to equivalent of UEFI Shell 2.0 structure
   1620     //
   1621     InternalShellConvertFileListType(&mOldStyleFileList, &(*ListHead)->Link);
   1622 
   1623     //
   1624     // Free the EFI Shell version that was converted.
   1625     //
   1626     mEfiShellEnvironment2->FreeFileList(&mOldStyleFileList);
   1627 
   1628     if ((*ListHead)->Link.ForwardLink == (*ListHead)->Link.BackLink && (*ListHead)->Link.BackLink == &((*ListHead)->Link)) {
   1629       FreePool(*ListHead);
   1630       *ListHead = NULL;
   1631       Status = EFI_NOT_FOUND;
   1632     }
   1633     FreePool(CleanFilePathStr);
   1634     return (Status);
   1635   }
   1636 
   1637   FreePool(CleanFilePathStr);
   1638   return (EFI_UNSUPPORTED);
   1639 }
   1640 /**
   1641   Free the linked list returned from ShellOpenFileMetaArg.
   1642 
   1643   if ListHead is NULL then ASSERT().
   1644 
   1645   @param ListHead               the pointer to free.
   1646 
   1647   @retval EFI_SUCCESS           the operation was sucessful.
   1648 **/
   1649 EFI_STATUS
   1650 EFIAPI
   1651 ShellCloseFileMetaArg (
   1652   IN OUT EFI_SHELL_FILE_INFO    **ListHead
   1653   )
   1654 {
   1655   LIST_ENTRY                    *Node;
   1656 
   1657   //
   1658   // ASSERT that ListHead is not NULL
   1659   //
   1660   ASSERT(ListHead != NULL);
   1661 
   1662   //
   1663   // Check for UEFI Shell 2.0 protocols
   1664   //
   1665   if (gEfiShellProtocol != NULL) {
   1666     return (gEfiShellProtocol->FreeFileList(ListHead));
   1667   } else if (mEfiShellEnvironment2 != NULL) {
   1668     //
   1669     // Since this is EFI Shell version we need to free our internally made copy
   1670     // of the list
   1671     //
   1672     for ( Node = GetFirstNode(&(*ListHead)->Link)
   1673         ; *ListHead != NULL && !IsListEmpty(&(*ListHead)->Link)
   1674         ; Node = GetFirstNode(&(*ListHead)->Link)) {
   1675       RemoveEntryList(Node);
   1676       ((EFI_FILE_PROTOCOL*)((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->Handle)->Close(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->Handle);
   1677       FreePool(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->FullName);
   1678       FreePool(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->FileName);
   1679       FreePool(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->Info);
   1680       FreePool((EFI_SHELL_FILE_INFO_NO_CONST*)Node);
   1681     }
   1682     SHELL_FREE_NON_NULL(*ListHead);
   1683     return EFI_SUCCESS;
   1684   }
   1685 
   1686   return (EFI_UNSUPPORTED);
   1687 }
   1688 
   1689 /**
   1690   Find a file by searching the CWD and then the path.
   1691 
   1692   If FileName is NULL then ASSERT.
   1693 
   1694   If the return value is not NULL then the memory must be caller freed.
   1695 
   1696   @param FileName               Filename string.
   1697 
   1698   @retval NULL                  the file was not found
   1699   @return !NULL                 the full path to the file.
   1700 **/
   1701 CHAR16 *
   1702 EFIAPI
   1703 ShellFindFilePath (
   1704   IN CONST CHAR16 *FileName
   1705   )
   1706 {
   1707   CONST CHAR16      *Path;
   1708   SHELL_FILE_HANDLE Handle;
   1709   EFI_STATUS        Status;
   1710   CHAR16            *RetVal;
   1711   CHAR16            *TestPath;
   1712   CONST CHAR16      *Walker;
   1713   UINTN             Size;
   1714   CHAR16            *TempChar;
   1715 
   1716   RetVal = NULL;
   1717 
   1718   //
   1719   // First make sure its not an absolute path.
   1720   //
   1721   Status = ShellOpenFileByName(FileName, &Handle, EFI_FILE_MODE_READ, 0);
   1722   if (!EFI_ERROR(Status)){
   1723     if (FileHandleIsDirectory(Handle) != EFI_SUCCESS) {
   1724       ASSERT(RetVal == NULL);
   1725       RetVal = StrnCatGrow(&RetVal, NULL, FileName, 0);
   1726       ShellCloseFile(&Handle);
   1727       return (RetVal);
   1728     } else {
   1729       ShellCloseFile(&Handle);
   1730     }
   1731   }
   1732 
   1733   Path = ShellGetEnvironmentVariable(L"cwd");
   1734   if (Path != NULL) {
   1735     Size = StrSize(Path) + sizeof(CHAR16);
   1736     Size += StrSize(FileName);
   1737     TestPath = AllocateZeroPool(Size);
   1738     if (TestPath == NULL) {
   1739       return (NULL);
   1740     }
   1741     StrCpyS(TestPath, Size/sizeof(CHAR16), Path);
   1742     StrCatS(TestPath, Size/sizeof(CHAR16), L"\\");
   1743     StrCatS(TestPath, Size/sizeof(CHAR16), FileName);
   1744     Status = ShellOpenFileByName(TestPath, &Handle, EFI_FILE_MODE_READ, 0);
   1745     if (!EFI_ERROR(Status)){
   1746       if (FileHandleIsDirectory(Handle) != EFI_SUCCESS) {
   1747         ASSERT(RetVal == NULL);
   1748         RetVal = StrnCatGrow(&RetVal, NULL, TestPath, 0);
   1749         ShellCloseFile(&Handle);
   1750         FreePool(TestPath);
   1751         return (RetVal);
   1752       } else {
   1753         ShellCloseFile(&Handle);
   1754       }
   1755     }
   1756     FreePool(TestPath);
   1757   }
   1758   Path = ShellGetEnvironmentVariable(L"path");
   1759   if (Path != NULL) {
   1760     Size = StrSize(Path)+sizeof(CHAR16);
   1761     Size += StrSize(FileName);
   1762     TestPath = AllocateZeroPool(Size);
   1763     if (TestPath == NULL) {
   1764       return (NULL);
   1765     }
   1766     Walker = (CHAR16*)Path;
   1767     do {
   1768       CopyMem(TestPath, Walker, StrSize(Walker));
   1769       if (TestPath != NULL) {
   1770         TempChar = StrStr(TestPath, L";");
   1771         if (TempChar != NULL) {
   1772           *TempChar = CHAR_NULL;
   1773         }
   1774         if (TestPath[StrLen(TestPath)-1] != L'\\') {
   1775           StrCatS(TestPath, Size/sizeof(CHAR16), L"\\");
   1776         }
   1777         if (FileName[0] == L'\\') {
   1778           FileName++;
   1779         }
   1780         StrCatS(TestPath, Size/sizeof(CHAR16), FileName);
   1781         if (StrStr(Walker, L";") != NULL) {
   1782           Walker = StrStr(Walker, L";") + 1;
   1783         } else {
   1784           Walker = NULL;
   1785         }
   1786         Status = ShellOpenFileByName(TestPath, &Handle, EFI_FILE_MODE_READ, 0);
   1787         if (!EFI_ERROR(Status)){
   1788           if (FileHandleIsDirectory(Handle) != EFI_SUCCESS) {
   1789             ASSERT(RetVal == NULL);
   1790             RetVal = StrnCatGrow(&RetVal, NULL, TestPath, 0);
   1791             ShellCloseFile(&Handle);
   1792             break;
   1793           } else {
   1794             ShellCloseFile(&Handle);
   1795           }
   1796         }
   1797       }
   1798     } while (Walker != NULL && Walker[0] != CHAR_NULL);
   1799     FreePool(TestPath);
   1800   }
   1801   return (RetVal);
   1802 }
   1803 
   1804 /**
   1805   Find a file by searching the CWD and then the path with a variable set of file
   1806   extensions.  If the file is not found it will append each extension in the list
   1807   in the order provided and return the first one that is successful.
   1808 
   1809   If FileName is NULL, then ASSERT.
   1810   If FileExtension is NULL, then behavior is identical to ShellFindFilePath.
   1811 
   1812   If the return value is not NULL then the memory must be caller freed.
   1813 
   1814   @param[in] FileName           Filename string.
   1815   @param[in] FileExtension      Semi-colon delimeted list of possible extensions.
   1816 
   1817   @retval NULL                  The file was not found.
   1818   @retval !NULL                 The path to the file.
   1819 **/
   1820 CHAR16 *
   1821 EFIAPI
   1822 ShellFindFilePathEx (
   1823   IN CONST CHAR16 *FileName,
   1824   IN CONST CHAR16 *FileExtension
   1825   )
   1826 {
   1827   CHAR16            *TestPath;
   1828   CHAR16            *RetVal;
   1829   CONST CHAR16      *ExtensionWalker;
   1830   UINTN             Size;
   1831   CHAR16            *TempChar;
   1832   CHAR16            *TempChar2;
   1833 
   1834   ASSERT(FileName != NULL);
   1835   if (FileExtension == NULL) {
   1836     return (ShellFindFilePath(FileName));
   1837   }
   1838   RetVal = ShellFindFilePath(FileName);
   1839   if (RetVal != NULL) {
   1840     return (RetVal);
   1841   }
   1842   Size =  StrSize(FileName);
   1843   Size += StrSize(FileExtension);
   1844   TestPath = AllocateZeroPool(Size);
   1845   if (TestPath == NULL) {
   1846     return (NULL);
   1847   }
   1848   for (ExtensionWalker = FileExtension, TempChar2 = (CHAR16*)FileExtension;  TempChar2 != NULL ; ExtensionWalker = TempChar2 + 1){
   1849     StrCpyS(TestPath, Size/sizeof(CHAR16), FileName);
   1850     if (ExtensionWalker != NULL) {
   1851       StrCatS(TestPath, Size/sizeof(CHAR16), ExtensionWalker);
   1852     }
   1853     TempChar = StrStr(TestPath, L";");
   1854     if (TempChar != NULL) {
   1855       *TempChar = CHAR_NULL;
   1856     }
   1857     RetVal = ShellFindFilePath(TestPath);
   1858     if (RetVal != NULL) {
   1859       break;
   1860     }
   1861     ASSERT(ExtensionWalker != NULL);
   1862     TempChar2 = StrStr(ExtensionWalker, L";");
   1863   }
   1864   FreePool(TestPath);
   1865   return (RetVal);
   1866 }
   1867 
   1868 typedef struct {
   1869   LIST_ENTRY     Link;
   1870   CHAR16         *Name;
   1871   SHELL_PARAM_TYPE      Type;
   1872   CHAR16         *Value;
   1873   UINTN          OriginalPosition;
   1874 } SHELL_PARAM_PACKAGE;
   1875 
   1876 /**
   1877   Checks the list of valid arguments and returns TRUE if the item was found.  If the
   1878   return value is TRUE then the type parameter is set also.
   1879 
   1880   if CheckList is NULL then ASSERT();
   1881   if Name is NULL then ASSERT();
   1882   if Type is NULL then ASSERT();
   1883 
   1884   @param Name                   pointer to Name of parameter found
   1885   @param CheckList              List to check against
   1886   @param Type                   pointer to type of parameter if it was found
   1887 
   1888   @retval TRUE                  the Parameter was found.  Type is valid.
   1889   @retval FALSE                 the Parameter was not found.  Type is not valid.
   1890 **/
   1891 BOOLEAN
   1892 InternalIsOnCheckList (
   1893   IN CONST CHAR16               *Name,
   1894   IN CONST SHELL_PARAM_ITEM     *CheckList,
   1895   OUT SHELL_PARAM_TYPE          *Type
   1896   )
   1897 {
   1898   SHELL_PARAM_ITEM              *TempListItem;
   1899   CHAR16                        *TempString;
   1900 
   1901   //
   1902   // ASSERT that all 3 pointer parameters aren't NULL
   1903   //
   1904   ASSERT(CheckList  != NULL);
   1905   ASSERT(Type       != NULL);
   1906   ASSERT(Name       != NULL);
   1907 
   1908   //
   1909   // question mark and page break mode are always supported
   1910   //
   1911   if ((StrCmp(Name, L"-?") == 0) ||
   1912       (StrCmp(Name, L"-b") == 0)
   1913      ) {
   1914      *Type = TypeFlag;
   1915      return (TRUE);
   1916   }
   1917 
   1918   //
   1919   // Enumerate through the list
   1920   //
   1921   for (TempListItem = (SHELL_PARAM_ITEM*)CheckList ; TempListItem->Name != NULL ; TempListItem++) {
   1922     //
   1923     // If the Type is TypeStart only check the first characters of the passed in param
   1924     // If it matches set the type and return TRUE
   1925     //
   1926     if (TempListItem->Type == TypeStart) {
   1927       if (StrnCmp(Name, TempListItem->Name, StrLen(TempListItem->Name)) == 0) {
   1928         *Type = TempListItem->Type;
   1929         return (TRUE);
   1930       }
   1931       TempString = NULL;
   1932       TempString = StrnCatGrow(&TempString, NULL, Name, StrLen(TempListItem->Name));
   1933       if (TempString != NULL) {
   1934         if (StringNoCaseCompare(&TempString, &TempListItem->Name) == 0) {
   1935           *Type = TempListItem->Type;
   1936           FreePool(TempString);
   1937           return (TRUE);
   1938         }
   1939         FreePool(TempString);
   1940       }
   1941     } else if (StringNoCaseCompare(&Name, &TempListItem->Name) == 0) {
   1942       *Type = TempListItem->Type;
   1943       return (TRUE);
   1944     }
   1945   }
   1946 
   1947   return (FALSE);
   1948 }
   1949 /**
   1950   Checks the string for indicators of "flag" status.  this is a leading '/', '-', or '+'
   1951 
   1952   @param[in] Name               pointer to Name of parameter found
   1953   @param[in] AlwaysAllowNumbers TRUE to allow numbers, FALSE to not.
   1954   @param[in] TimeNumbers        TRUE to allow numbers with ":", FALSE otherwise.
   1955 
   1956   @retval TRUE                  the Parameter is a flag.
   1957   @retval FALSE                 the Parameter not a flag.
   1958 **/
   1959 BOOLEAN
   1960 InternalIsFlag (
   1961   IN CONST CHAR16               *Name,
   1962   IN CONST BOOLEAN              AlwaysAllowNumbers,
   1963   IN CONST BOOLEAN              TimeNumbers
   1964   )
   1965 {
   1966   //
   1967   // ASSERT that Name isn't NULL
   1968   //
   1969   ASSERT(Name != NULL);
   1970 
   1971   //
   1972   // If we accept numbers then dont return TRUE. (they will be values)
   1973   //
   1974   if (((Name[0] == L'-' || Name[0] == L'+') && InternalShellIsHexOrDecimalNumber(Name+1, FALSE, FALSE, TimeNumbers)) && AlwaysAllowNumbers) {
   1975     return (FALSE);
   1976   }
   1977 
   1978   //
   1979   // If the Name has a /, +, or - as the first character return TRUE
   1980   //
   1981   if ((Name[0] == L'/') ||
   1982       (Name[0] == L'-') ||
   1983       (Name[0] == L'+')
   1984      ) {
   1985       return (TRUE);
   1986   }
   1987   return (FALSE);
   1988 }
   1989 
   1990 /**
   1991   Checks the command line arguments passed against the list of valid ones.
   1992 
   1993   If no initialization is required, then return RETURN_SUCCESS.
   1994 
   1995   @param[in] CheckList          pointer to list of parameters to check
   1996   @param[out] CheckPackage      pointer to pointer to list checked values
   1997   @param[out] ProblemParam      optional pointer to pointer to unicode string for
   1998                                 the paramater that caused failure.  If used then the
   1999                                 caller is responsible for freeing the memory.
   2000   @param[in] AutoPageBreak      will automatically set PageBreakEnabled for "b" parameter
   2001   @param[in] Argv               pointer to array of parameters
   2002   @param[in] Argc               Count of parameters in Argv
   2003   @param[in] AlwaysAllowNumbers TRUE to allow numbers always, FALSE otherwise.
   2004 
   2005   @retval EFI_SUCCESS           The operation completed sucessfully.
   2006   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed
   2007   @retval EFI_INVALID_PARAMETER A parameter was invalid
   2008   @retval EFI_VOLUME_CORRUPTED  the command line was corrupt.  an argument was
   2009                                 duplicated.  the duplicated command line argument
   2010                                 was returned in ProblemParam if provided.
   2011   @retval EFI_NOT_FOUND         a argument required a value that was missing.
   2012                                 the invalid command line argument was returned in
   2013                                 ProblemParam if provided.
   2014 **/
   2015 EFI_STATUS
   2016 InternalCommandLineParse (
   2017   IN CONST SHELL_PARAM_ITEM     *CheckList,
   2018   OUT LIST_ENTRY                **CheckPackage,
   2019   OUT CHAR16                    **ProblemParam OPTIONAL,
   2020   IN BOOLEAN                    AutoPageBreak,
   2021   IN CONST CHAR16               **Argv,
   2022   IN UINTN                      Argc,
   2023   IN BOOLEAN                    AlwaysAllowNumbers
   2024   )
   2025 {
   2026   UINTN                         LoopCounter;
   2027   SHELL_PARAM_TYPE              CurrentItemType;
   2028   SHELL_PARAM_PACKAGE           *CurrentItemPackage;
   2029   UINTN                         GetItemValue;
   2030   UINTN                         ValueSize;
   2031   UINTN                         Count;
   2032   CONST CHAR16                  *TempPointer;
   2033   UINTN                         CurrentValueSize;
   2034   CHAR16                        *NewValue;
   2035 
   2036   CurrentItemPackage = NULL;
   2037   GetItemValue = 0;
   2038   ValueSize = 0;
   2039   Count = 0;
   2040 
   2041   //
   2042   // If there is only 1 item we dont need to do anything
   2043   //
   2044   if (Argc < 1) {
   2045     *CheckPackage = NULL;
   2046     return (EFI_SUCCESS);
   2047   }
   2048 
   2049   //
   2050   // ASSERTs
   2051   //
   2052   ASSERT(CheckList  != NULL);
   2053   ASSERT(Argv       != NULL);
   2054 
   2055   //
   2056   // initialize the linked list
   2057   //
   2058   *CheckPackage = (LIST_ENTRY*)AllocateZeroPool(sizeof(LIST_ENTRY));
   2059   if (*CheckPackage == NULL) {
   2060     return (EFI_OUT_OF_RESOURCES);
   2061   }
   2062 
   2063   InitializeListHead(*CheckPackage);
   2064 
   2065   //
   2066   // loop through each of the arguments
   2067   //
   2068   for (LoopCounter = 0 ; LoopCounter < Argc ; ++LoopCounter) {
   2069     if (Argv[LoopCounter] == NULL) {
   2070       //
   2071       // do nothing for NULL argv
   2072       //
   2073     } else if (InternalIsOnCheckList(Argv[LoopCounter], CheckList, &CurrentItemType)) {
   2074       //
   2075       // We might have leftover if last parameter didnt have optional value
   2076       //
   2077       if (GetItemValue != 0) {
   2078         GetItemValue = 0;
   2079         InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);
   2080       }
   2081       //
   2082       // this is a flag
   2083       //
   2084       CurrentItemPackage = AllocateZeroPool(sizeof(SHELL_PARAM_PACKAGE));
   2085       if (CurrentItemPackage == NULL) {
   2086         ShellCommandLineFreeVarList(*CheckPackage);
   2087         *CheckPackage = NULL;
   2088         return (EFI_OUT_OF_RESOURCES);
   2089       }
   2090       CurrentItemPackage->Name  = AllocateCopyPool(StrSize(Argv[LoopCounter]), Argv[LoopCounter]);
   2091       if (CurrentItemPackage->Name == NULL) {
   2092         ShellCommandLineFreeVarList(*CheckPackage);
   2093         *CheckPackage = NULL;
   2094         return (EFI_OUT_OF_RESOURCES);
   2095       }
   2096       CurrentItemPackage->Type  = CurrentItemType;
   2097       CurrentItemPackage->OriginalPosition = (UINTN)(-1);
   2098       CurrentItemPackage->Value = NULL;
   2099 
   2100       //
   2101       // Does this flag require a value
   2102       //
   2103       switch (CurrentItemPackage->Type) {
   2104         //
   2105         // possibly trigger the next loop(s) to populate the value of this item
   2106         //
   2107         case TypeValue:
   2108         case TypeTimeValue:
   2109           GetItemValue = 1;
   2110           ValueSize = 0;
   2111           break;
   2112         case TypeDoubleValue:
   2113           GetItemValue = 2;
   2114           ValueSize = 0;
   2115           break;
   2116         case TypeMaxValue:
   2117           GetItemValue = (UINTN)(-1);
   2118           ValueSize = 0;
   2119           break;
   2120         default:
   2121           //
   2122           // this item has no value expected; we are done
   2123           //
   2124           InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);
   2125           ASSERT(GetItemValue == 0);
   2126           break;
   2127       }
   2128     } else if (GetItemValue != 0 && CurrentItemPackage != NULL && !InternalIsFlag(Argv[LoopCounter], AlwaysAllowNumbers, (BOOLEAN)(CurrentItemPackage->Type == TypeTimeValue))) {
   2129       //
   2130       // get the item VALUE for a previous flag
   2131       //
   2132       CurrentValueSize = ValueSize + StrSize(Argv[LoopCounter]) + sizeof(CHAR16);
   2133       NewValue = ReallocatePool(ValueSize, CurrentValueSize, CurrentItemPackage->Value);
   2134       if (NewValue == NULL) {
   2135         SHELL_FREE_NON_NULL (CurrentItemPackage->Value);
   2136         SHELL_FREE_NON_NULL (CurrentItemPackage);
   2137         ShellCommandLineFreeVarList (*CheckPackage);
   2138         *CheckPackage = NULL;
   2139         return EFI_OUT_OF_RESOURCES;
   2140       }
   2141       CurrentItemPackage->Value = NewValue;
   2142       if (ValueSize == 0) {
   2143         StrCpyS( CurrentItemPackage->Value,
   2144                   CurrentValueSize/sizeof(CHAR16),
   2145                   Argv[LoopCounter]
   2146                   );
   2147       } else {
   2148         StrCatS( CurrentItemPackage->Value,
   2149                   CurrentValueSize/sizeof(CHAR16),
   2150                   L" "
   2151                   );
   2152         StrCatS( CurrentItemPackage->Value,
   2153                   CurrentValueSize/sizeof(CHAR16),
   2154                   Argv[LoopCounter]
   2155                   );
   2156       }
   2157       ValueSize += StrSize(Argv[LoopCounter]) + sizeof(CHAR16);
   2158 
   2159       GetItemValue--;
   2160       if (GetItemValue == 0) {
   2161         InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);
   2162       }
   2163     } else if (!InternalIsFlag(Argv[LoopCounter], AlwaysAllowNumbers, FALSE)){
   2164       //
   2165       // add this one as a non-flag
   2166       //
   2167 
   2168       TempPointer = Argv[LoopCounter];
   2169       if ((*TempPointer == L'^' && *(TempPointer+1) == L'-')
   2170        || (*TempPointer == L'^' && *(TempPointer+1) == L'/')
   2171        || (*TempPointer == L'^' && *(TempPointer+1) == L'+')
   2172       ){
   2173         TempPointer++;
   2174       }
   2175       CurrentItemPackage = AllocateZeroPool(sizeof(SHELL_PARAM_PACKAGE));
   2176       if (CurrentItemPackage == NULL) {
   2177         ShellCommandLineFreeVarList(*CheckPackage);
   2178         *CheckPackage = NULL;
   2179         return (EFI_OUT_OF_RESOURCES);
   2180       }
   2181       CurrentItemPackage->Name  = NULL;
   2182       CurrentItemPackage->Type  = TypePosition;
   2183       CurrentItemPackage->Value = AllocateCopyPool(StrSize(TempPointer), TempPointer);
   2184       if (CurrentItemPackage->Value == NULL) {
   2185         ShellCommandLineFreeVarList(*CheckPackage);
   2186         *CheckPackage = NULL;
   2187         return (EFI_OUT_OF_RESOURCES);
   2188       }
   2189       CurrentItemPackage->OriginalPosition = Count++;
   2190       InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);
   2191     } else {
   2192       //
   2193       // this was a non-recognised flag... error!
   2194       //
   2195       if (ProblemParam != NULL) {
   2196         *ProblemParam = AllocateCopyPool(StrSize(Argv[LoopCounter]), Argv[LoopCounter]);
   2197       }
   2198       ShellCommandLineFreeVarList(*CheckPackage);
   2199       *CheckPackage = NULL;
   2200       return (EFI_VOLUME_CORRUPTED);
   2201     }
   2202   }
   2203   if (GetItemValue != 0) {
   2204     GetItemValue = 0;
   2205     InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);
   2206   }
   2207   //
   2208   // support for AutoPageBreak
   2209   //
   2210   if (AutoPageBreak && ShellCommandLineGetFlag(*CheckPackage, L"-b")) {
   2211     ShellSetPageBreakMode(TRUE);
   2212   }
   2213   return (EFI_SUCCESS);
   2214 }
   2215 
   2216 /**
   2217   Checks the command line arguments passed against the list of valid ones.
   2218   Optionally removes NULL values first.
   2219 
   2220   If no initialization is required, then return RETURN_SUCCESS.
   2221 
   2222   @param[in] CheckList          The pointer to list of parameters to check.
   2223   @param[out] CheckPackage      The package of checked values.
   2224   @param[out] ProblemParam      Optional pointer to pointer to unicode string for
   2225                                 the paramater that caused failure.
   2226   @param[in] AutoPageBreak      Will automatically set PageBreakEnabled.
   2227   @param[in] AlwaysAllowNumbers Will never fail for number based flags.
   2228 
   2229   @retval EFI_SUCCESS           The operation completed sucessfully.
   2230   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
   2231   @retval EFI_INVALID_PARAMETER A parameter was invalid.
   2232   @retval EFI_VOLUME_CORRUPTED  The command line was corrupt.
   2233   @retval EFI_DEVICE_ERROR      The commands contained 2 opposing arguments.  One
   2234                                 of the command line arguments was returned in
   2235                                 ProblemParam if provided.
   2236   @retval EFI_NOT_FOUND         A argument required a value that was missing.
   2237                                 The invalid command line argument was returned in
   2238                                 ProblemParam if provided.
   2239 **/
   2240 EFI_STATUS
   2241 EFIAPI
   2242 ShellCommandLineParseEx (
   2243   IN CONST SHELL_PARAM_ITEM     *CheckList,
   2244   OUT LIST_ENTRY                **CheckPackage,
   2245   OUT CHAR16                    **ProblemParam OPTIONAL,
   2246   IN BOOLEAN                    AutoPageBreak,
   2247   IN BOOLEAN                    AlwaysAllowNumbers
   2248   )
   2249 {
   2250   //
   2251   // ASSERT that CheckList and CheckPackage aren't NULL
   2252   //
   2253   ASSERT(CheckList    != NULL);
   2254   ASSERT(CheckPackage != NULL);
   2255 
   2256   //
   2257   // Check for UEFI Shell 2.0 protocols
   2258   //
   2259   if (gEfiShellParametersProtocol != NULL) {
   2260     return (InternalCommandLineParse(CheckList,
   2261                                      CheckPackage,
   2262                                      ProblemParam,
   2263                                      AutoPageBreak,
   2264                                      (CONST CHAR16**) gEfiShellParametersProtocol->Argv,
   2265                                      gEfiShellParametersProtocol->Argc,
   2266                                      AlwaysAllowNumbers));
   2267   }
   2268 
   2269   //
   2270   // ASSERT That EFI Shell is not required
   2271   //
   2272   ASSERT (mEfiShellInterface != NULL);
   2273   return (InternalCommandLineParse(CheckList,
   2274                                    CheckPackage,
   2275                                    ProblemParam,
   2276                                    AutoPageBreak,
   2277                                    (CONST CHAR16**) mEfiShellInterface->Argv,
   2278                                    mEfiShellInterface->Argc,
   2279                                    AlwaysAllowNumbers));
   2280 }
   2281 
   2282 /**
   2283   Frees shell variable list that was returned from ShellCommandLineParse.
   2284 
   2285   This function will free all the memory that was used for the CheckPackage
   2286   list of postprocessed shell arguments.
   2287 
   2288   this function has no return value.
   2289 
   2290   if CheckPackage is NULL, then return
   2291 
   2292   @param CheckPackage           the list to de-allocate
   2293   **/
   2294 VOID
   2295 EFIAPI
   2296 ShellCommandLineFreeVarList (
   2297   IN LIST_ENTRY                 *CheckPackage
   2298   )
   2299 {
   2300   LIST_ENTRY                    *Node;
   2301 
   2302   //
   2303   // check for CheckPackage == NULL
   2304   //
   2305   if (CheckPackage == NULL) {
   2306     return;
   2307   }
   2308 
   2309   //
   2310   // for each node in the list
   2311   //
   2312   for ( Node = GetFirstNode(CheckPackage)
   2313       ; !IsListEmpty(CheckPackage)
   2314       ; Node = GetFirstNode(CheckPackage)
   2315      ){
   2316     //
   2317     // Remove it from the list
   2318     //
   2319     RemoveEntryList(Node);
   2320 
   2321     //
   2322     // if it has a name free the name
   2323     //
   2324     if (((SHELL_PARAM_PACKAGE*)Node)->Name != NULL) {
   2325       FreePool(((SHELL_PARAM_PACKAGE*)Node)->Name);
   2326     }
   2327 
   2328     //
   2329     // if it has a value free the value
   2330     //
   2331     if (((SHELL_PARAM_PACKAGE*)Node)->Value != NULL) {
   2332       FreePool(((SHELL_PARAM_PACKAGE*)Node)->Value);
   2333     }
   2334 
   2335     //
   2336     // free the node structure
   2337     //
   2338     FreePool((SHELL_PARAM_PACKAGE*)Node);
   2339   }
   2340   //
   2341   // free the list head node
   2342   //
   2343   FreePool(CheckPackage);
   2344 }
   2345 /**
   2346   Checks for presence of a flag parameter
   2347 
   2348   flag arguments are in the form of "-<Key>" or "/<Key>", but do not have a value following the key
   2349 
   2350   if CheckPackage is NULL then return FALSE.
   2351   if KeyString is NULL then ASSERT()
   2352 
   2353   @param CheckPackage           The package of parsed command line arguments
   2354   @param KeyString              the Key of the command line argument to check for
   2355 
   2356   @retval TRUE                  the flag is on the command line
   2357   @retval FALSE                 the flag is not on the command line
   2358   **/
   2359 BOOLEAN
   2360 EFIAPI
   2361 ShellCommandLineGetFlag (
   2362   IN CONST LIST_ENTRY         * CONST CheckPackage,
   2363   IN CONST CHAR16             * CONST KeyString
   2364   )
   2365 {
   2366   LIST_ENTRY                    *Node;
   2367   CHAR16                        *TempString;
   2368 
   2369   //
   2370   // return FALSE for no package or KeyString is NULL
   2371   //
   2372   if (CheckPackage == NULL || KeyString == NULL) {
   2373     return (FALSE);
   2374   }
   2375 
   2376   //
   2377   // enumerate through the list of parametrs
   2378   //
   2379   for ( Node = GetFirstNode(CheckPackage)
   2380       ; !IsNull (CheckPackage, Node)
   2381       ; Node = GetNextNode(CheckPackage, Node)
   2382       ){
   2383     //
   2384     // If the Name matches, return TRUE (and there may be NULL name)
   2385     //
   2386     if (((SHELL_PARAM_PACKAGE*)Node)->Name != NULL) {
   2387       //
   2388       // If Type is TypeStart then only compare the begining of the strings
   2389       //
   2390       if (((SHELL_PARAM_PACKAGE*)Node)->Type == TypeStart) {
   2391         if (StrnCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name, StrLen(KeyString)) == 0) {
   2392           return (TRUE);
   2393         }
   2394         TempString = NULL;
   2395         TempString = StrnCatGrow(&TempString, NULL, KeyString, StrLen(((SHELL_PARAM_PACKAGE*)Node)->Name));
   2396         if (TempString != NULL) {
   2397           if (StringNoCaseCompare(&KeyString, &((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) {
   2398             FreePool(TempString);
   2399             return (TRUE);
   2400           }
   2401           FreePool(TempString);
   2402         }
   2403       } else if (StringNoCaseCompare(&KeyString, &((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) {
   2404         return (TRUE);
   2405       }
   2406     }
   2407   }
   2408   return (FALSE);
   2409 }
   2410 /**
   2411   Returns value from command line argument.
   2412 
   2413   Value parameters are in the form of "-<Key> value" or "/<Key> value".
   2414 
   2415   If CheckPackage is NULL, then return NULL.
   2416 
   2417   @param[in] CheckPackage       The package of parsed command line arguments.
   2418   @param[in] KeyString          The Key of the command line argument to check for.
   2419 
   2420   @retval NULL                  The flag is not on the command line.
   2421   @retval !=NULL                The pointer to unicode string of the value.
   2422 **/
   2423 CONST CHAR16*
   2424 EFIAPI
   2425 ShellCommandLineGetValue (
   2426   IN CONST LIST_ENTRY           *CheckPackage,
   2427   IN CHAR16                     *KeyString
   2428   )
   2429 {
   2430   LIST_ENTRY                    *Node;
   2431   CHAR16                        *TempString;
   2432 
   2433   //
   2434   // return NULL for no package or KeyString is NULL
   2435   //
   2436   if (CheckPackage == NULL || KeyString == NULL) {
   2437     return (NULL);
   2438   }
   2439 
   2440   //
   2441   // enumerate through the list of parametrs
   2442   //
   2443   for ( Node = GetFirstNode(CheckPackage)
   2444       ; !IsNull (CheckPackage, Node)
   2445       ; Node = GetNextNode(CheckPackage, Node)
   2446       ){
   2447     //
   2448     // If the Name matches, return TRUE (and there may be NULL name)
   2449     //
   2450     if (((SHELL_PARAM_PACKAGE*)Node)->Name != NULL) {
   2451       //
   2452       // If Type is TypeStart then only compare the begining of the strings
   2453       //
   2454       if (((SHELL_PARAM_PACKAGE*)Node)->Type == TypeStart) {
   2455         if (StrnCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name, StrLen(KeyString)) == 0) {
   2456           return (((SHELL_PARAM_PACKAGE*)Node)->Name + StrLen(KeyString));
   2457         }
   2458         TempString = NULL;
   2459         TempString = StrnCatGrow(&TempString, NULL, KeyString, StrLen(((SHELL_PARAM_PACKAGE*)Node)->Name));
   2460         if (TempString != NULL) {
   2461           if (StringNoCaseCompare(&KeyString, &((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) {
   2462             FreePool(TempString);
   2463             return (((SHELL_PARAM_PACKAGE*)Node)->Name + StrLen(KeyString));
   2464           }
   2465           FreePool(TempString);
   2466         }
   2467       } else if (StringNoCaseCompare(&KeyString, &((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) {
   2468         return (((SHELL_PARAM_PACKAGE*)Node)->Value);
   2469       }
   2470     }
   2471   }
   2472   return (NULL);
   2473 }
   2474 
   2475 /**
   2476   Returns raw value from command line argument.
   2477 
   2478   Raw value parameters are in the form of "value" in a specific position in the list.
   2479 
   2480   If CheckPackage is NULL, then return NULL.
   2481 
   2482   @param[in] CheckPackage       The package of parsed command line arguments.
   2483   @param[in] Position           The position of the value.
   2484 
   2485   @retval NULL                  The flag is not on the command line.
   2486   @retval !=NULL                The pointer to unicode string of the value.
   2487   **/
   2488 CONST CHAR16*
   2489 EFIAPI
   2490 ShellCommandLineGetRawValue (
   2491   IN CONST LIST_ENTRY           * CONST CheckPackage,
   2492   IN UINTN                      Position
   2493   )
   2494 {
   2495   LIST_ENTRY                    *Node;
   2496 
   2497   //
   2498   // check for CheckPackage == NULL
   2499   //
   2500   if (CheckPackage == NULL) {
   2501     return (NULL);
   2502   }
   2503 
   2504   //
   2505   // enumerate through the list of parametrs
   2506   //
   2507   for ( Node = GetFirstNode(CheckPackage)
   2508       ; !IsNull (CheckPackage, Node)
   2509       ; Node = GetNextNode(CheckPackage, Node)
   2510      ){
   2511     //
   2512     // If the position matches, return the value
   2513     //
   2514     if (((SHELL_PARAM_PACKAGE*)Node)->OriginalPosition == Position) {
   2515       return (((SHELL_PARAM_PACKAGE*)Node)->Value);
   2516     }
   2517   }
   2518   return (NULL);
   2519 }
   2520 
   2521 /**
   2522   returns the number of command line value parameters that were parsed.
   2523 
   2524   this will not include flags.
   2525 
   2526   @param[in] CheckPackage       The package of parsed command line arguments.
   2527 
   2528   @retval (UINTN)-1     No parsing has ocurred
   2529   @return other         The number of value parameters found
   2530 **/
   2531 UINTN
   2532 EFIAPI
   2533 ShellCommandLineGetCount(
   2534   IN CONST LIST_ENTRY              *CheckPackage
   2535   )
   2536 {
   2537   LIST_ENTRY  *Node1;
   2538   UINTN       Count;
   2539 
   2540   if (CheckPackage == NULL) {
   2541     return (0);
   2542   }
   2543   for ( Node1 = GetFirstNode(CheckPackage), Count = 0
   2544       ; !IsNull (CheckPackage, Node1)
   2545       ; Node1 = GetNextNode(CheckPackage, Node1)
   2546      ){
   2547     if (((SHELL_PARAM_PACKAGE*)Node1)->Name == NULL) {
   2548       Count++;
   2549     }
   2550   }
   2551   return (Count);
   2552 }
   2553 
   2554 /**
   2555   Determines if a parameter is duplicated.
   2556 
   2557   If Param is not NULL then it will point to a callee allocated string buffer
   2558   with the parameter value if a duplicate is found.
   2559 
   2560   If CheckPackage is NULL, then ASSERT.
   2561 
   2562   @param[in] CheckPackage       The package of parsed command line arguments.
   2563   @param[out] Param             Upon finding one, a pointer to the duplicated parameter.
   2564 
   2565   @retval EFI_SUCCESS           No parameters were duplicated.
   2566   @retval EFI_DEVICE_ERROR      A duplicate was found.
   2567   **/
   2568 EFI_STATUS
   2569 EFIAPI
   2570 ShellCommandLineCheckDuplicate (
   2571   IN CONST LIST_ENTRY              *CheckPackage,
   2572   OUT CHAR16                       **Param
   2573   )
   2574 {
   2575   LIST_ENTRY                    *Node1;
   2576   LIST_ENTRY                    *Node2;
   2577 
   2578   ASSERT(CheckPackage != NULL);
   2579 
   2580   for ( Node1 = GetFirstNode(CheckPackage)
   2581       ; !IsNull (CheckPackage, Node1)
   2582       ; Node1 = GetNextNode(CheckPackage, Node1)
   2583      ){
   2584     for ( Node2 = GetNextNode(CheckPackage, Node1)
   2585         ; !IsNull (CheckPackage, Node2)
   2586         ; Node2 = GetNextNode(CheckPackage, Node2)
   2587        ){
   2588       if ((((SHELL_PARAM_PACKAGE*)Node1)->Name != NULL) && (((SHELL_PARAM_PACKAGE*)Node2)->Name != NULL) && StrCmp(((SHELL_PARAM_PACKAGE*)Node1)->Name, ((SHELL_PARAM_PACKAGE*)Node2)->Name) == 0) {
   2589         if (Param != NULL) {
   2590           *Param = NULL;
   2591           *Param = StrnCatGrow(Param, NULL, ((SHELL_PARAM_PACKAGE*)Node1)->Name, 0);
   2592         }
   2593         return (EFI_DEVICE_ERROR);
   2594       }
   2595     }
   2596   }
   2597   return (EFI_SUCCESS);
   2598 }
   2599 
   2600 /**
   2601   This is a find and replace function.  Upon successful return the NewString is a copy of
   2602   SourceString with each instance of FindTarget replaced with ReplaceWith.
   2603 
   2604   If SourceString and NewString overlap the behavior is undefined.
   2605 
   2606   If the string would grow bigger than NewSize it will halt and return error.
   2607 
   2608   @param[in] SourceString              The string with source buffer.
   2609   @param[in, out] NewString            The string with resultant buffer.
   2610   @param[in] NewSize                   The size in bytes of NewString.
   2611   @param[in] FindTarget                The string to look for.
   2612   @param[in] ReplaceWith               The string to replace FindTarget with.
   2613   @param[in] SkipPreCarrot             If TRUE will skip a FindTarget that has a '^'
   2614                                        immediately before it.
   2615   @param[in] ParameterReplacing        If TRUE will add "" around items with spaces.
   2616 
   2617   @retval EFI_INVALID_PARAMETER       SourceString was NULL.
   2618   @retval EFI_INVALID_PARAMETER       NewString was NULL.
   2619   @retval EFI_INVALID_PARAMETER       FindTarget was NULL.
   2620   @retval EFI_INVALID_PARAMETER       ReplaceWith was NULL.
   2621   @retval EFI_INVALID_PARAMETER       FindTarget had length < 1.
   2622   @retval EFI_INVALID_PARAMETER       SourceString had length < 1.
   2623   @retval EFI_BUFFER_TOO_SMALL        NewSize was less than the minimum size to hold
   2624                                       the new string (truncation occurred).
   2625   @retval EFI_SUCCESS                 The string was successfully copied with replacement.
   2626 **/
   2627 EFI_STATUS
   2628 EFIAPI
   2629 ShellCopySearchAndReplace(
   2630   IN CHAR16 CONST                     *SourceString,
   2631   IN OUT CHAR16                       *NewString,
   2632   IN UINTN                            NewSize,
   2633   IN CONST CHAR16                     *FindTarget,
   2634   IN CONST CHAR16                     *ReplaceWith,
   2635   IN CONST BOOLEAN                    SkipPreCarrot,
   2636   IN CONST BOOLEAN                    ParameterReplacing
   2637   )
   2638 {
   2639   UINTN Size;
   2640   CHAR16 *Replace;
   2641 
   2642   if ( (SourceString == NULL)
   2643     || (NewString    == NULL)
   2644     || (FindTarget   == NULL)
   2645     || (ReplaceWith  == NULL)
   2646     || (StrLen(FindTarget) < 1)
   2647     || (StrLen(SourceString) < 1)
   2648    ){
   2649     return (EFI_INVALID_PARAMETER);
   2650   }
   2651   Replace = NULL;
   2652   if (StrStr(ReplaceWith, L" ") == NULL || !ParameterReplacing) {
   2653     Replace = StrnCatGrow(&Replace, NULL, ReplaceWith, 0);
   2654   } else {
   2655     Replace = AllocateZeroPool(StrSize(ReplaceWith) + 2*sizeof(CHAR16));
   2656     if (Replace != NULL) {
   2657       UnicodeSPrint(Replace, StrSize(ReplaceWith) + 2*sizeof(CHAR16), L"\"%s\"", ReplaceWith);
   2658     }
   2659   }
   2660   if (Replace == NULL) {
   2661     return (EFI_OUT_OF_RESOURCES);
   2662   }
   2663   NewString = ZeroMem(NewString, NewSize);
   2664   while (*SourceString != CHAR_NULL) {
   2665     //
   2666     // if we find the FindTarget and either Skip == FALSE or Skip  and we
   2667     // dont have a carrot do a replace...
   2668     //
   2669     if (StrnCmp(SourceString, FindTarget, StrLen(FindTarget)) == 0
   2670       && ((SkipPreCarrot && *(SourceString-1) != L'^') || !SkipPreCarrot)
   2671      ){
   2672       SourceString += StrLen(FindTarget);
   2673       Size = StrSize(NewString);
   2674       if ((Size + (StrLen(Replace)*sizeof(CHAR16))) > NewSize) {
   2675         FreePool(Replace);
   2676         return (EFI_BUFFER_TOO_SMALL);
   2677       }
   2678       StrCatS(NewString, NewSize/sizeof(CHAR16), Replace);
   2679     } else {
   2680       Size = StrSize(NewString);
   2681       if (Size + sizeof(CHAR16) > NewSize) {
   2682         FreePool(Replace);
   2683         return (EFI_BUFFER_TOO_SMALL);
   2684       }
   2685       StrnCatS(NewString, NewSize/sizeof(CHAR16), SourceString, 1);
   2686       SourceString++;
   2687     }
   2688   }
   2689   FreePool(Replace);
   2690   return (EFI_SUCCESS);
   2691 }
   2692 
   2693 /**
   2694   Internal worker function to output a string.
   2695 
   2696   This function will output a string to the correct StdOut.
   2697 
   2698   @param[in] String       The string to print out.
   2699 
   2700   @retval EFI_SUCCESS     The operation was sucessful.
   2701   @retval !EFI_SUCCESS    The operation failed.
   2702 **/
   2703 EFI_STATUS
   2704 InternalPrintTo (
   2705   IN CONST CHAR16 *String
   2706   )
   2707 {
   2708   UINTN Size;
   2709   Size = StrSize(String) - sizeof(CHAR16);
   2710   if (Size == 0) {
   2711     return (EFI_SUCCESS);
   2712   }
   2713   if (gEfiShellParametersProtocol != NULL) {
   2714     return (gEfiShellProtocol->WriteFile(gEfiShellParametersProtocol->StdOut, &Size, (VOID*)String));
   2715   }
   2716   if (mEfiShellInterface          != NULL) {
   2717     if (mEfiShellInterface->RedirArgc == 0) {
   2718     //
   2719     // Divide in half for old shell.  Must be string length not size.
   2720       //
   2721       Size /=2;  // Divide in half only when no redirection.
   2722     }
   2723     return (mEfiShellInterface->StdOut->Write(mEfiShellInterface->StdOut,          &Size, (VOID*)String));
   2724   }
   2725   ASSERT(FALSE);
   2726   return (EFI_UNSUPPORTED);
   2727 }
   2728 
   2729 /**
   2730   Print at a specific location on the screen.
   2731 
   2732   This function will move the cursor to a given screen location and print the specified string
   2733 
   2734   If -1 is specified for either the Row or Col the current screen location for BOTH
   2735   will be used.
   2736 
   2737   if either Row or Col is out of range for the current console, then ASSERT
   2738   if Format is NULL, then ASSERT
   2739 
   2740   In addition to the standard %-based flags as supported by UefiLib Print() this supports
   2741   the following additional flags:
   2742     %N       -   Set output attribute to normal
   2743     %H       -   Set output attribute to highlight
   2744     %E       -   Set output attribute to error
   2745     %B       -   Set output attribute to blue color
   2746     %V       -   Set output attribute to green color
   2747 
   2748   Note: The background color is controlled by the shell command cls.
   2749 
   2750   @param[in] Col        the column to print at
   2751   @param[in] Row        the row to print at
   2752   @param[in] Format     the format string
   2753   @param[in] Marker     the marker for the variable argument list
   2754 
   2755   @return EFI_SUCCESS           The operation was successful.
   2756   @return EFI_DEVICE_ERROR      The console device reported an error.
   2757 **/
   2758 EFI_STATUS
   2759 InternalShellPrintWorker(
   2760   IN INT32                Col OPTIONAL,
   2761   IN INT32                Row OPTIONAL,
   2762   IN CONST CHAR16         *Format,
   2763   IN VA_LIST              Marker
   2764   )
   2765 {
   2766   EFI_STATUS        Status;
   2767   CHAR16            *ResumeLocation;
   2768   CHAR16            *FormatWalker;
   2769   UINTN             OriginalAttribute;
   2770   CHAR16            *mPostReplaceFormat;
   2771   CHAR16            *mPostReplaceFormat2;
   2772 
   2773   mPostReplaceFormat = AllocateZeroPool (PcdGet16 (PcdShellPrintBufferSize));
   2774   mPostReplaceFormat2 = AllocateZeroPool (PcdGet16 (PcdShellPrintBufferSize));
   2775 
   2776   if (mPostReplaceFormat == NULL || mPostReplaceFormat2 == NULL) {
   2777     SHELL_FREE_NON_NULL(mPostReplaceFormat);
   2778     SHELL_FREE_NON_NULL(mPostReplaceFormat2);
   2779     return (EFI_OUT_OF_RESOURCES);
   2780   }
   2781 
   2782   Status            = EFI_SUCCESS;
   2783   OriginalAttribute = gST->ConOut->Mode->Attribute;
   2784 
   2785   //
   2786   // Back and forth each time fixing up 1 of our flags...
   2787   //
   2788   Status = ShellCopySearchAndReplace(Format,             mPostReplaceFormat,  PcdGet16 (PcdShellPrintBufferSize), L"%N", L"%%N", FALSE, FALSE);
   2789   ASSERT_EFI_ERROR(Status);
   2790   Status = ShellCopySearchAndReplace(mPostReplaceFormat,  mPostReplaceFormat2, PcdGet16 (PcdShellPrintBufferSize), L"%E", L"%%E", FALSE, FALSE);
   2791   ASSERT_EFI_ERROR(Status);
   2792   Status = ShellCopySearchAndReplace(mPostReplaceFormat2, mPostReplaceFormat,  PcdGet16 (PcdShellPrintBufferSize), L"%H", L"%%H", FALSE, FALSE);
   2793   ASSERT_EFI_ERROR(Status);
   2794   Status = ShellCopySearchAndReplace(mPostReplaceFormat,  mPostReplaceFormat2, PcdGet16 (PcdShellPrintBufferSize), L"%B", L"%%B", FALSE, FALSE);
   2795   ASSERT_EFI_ERROR(Status);
   2796   Status = ShellCopySearchAndReplace(mPostReplaceFormat2, mPostReplaceFormat,  PcdGet16 (PcdShellPrintBufferSize), L"%V", L"%%V", FALSE, FALSE);
   2797   ASSERT_EFI_ERROR(Status);
   2798 
   2799   //
   2800   // Use the last buffer from replacing to print from...
   2801   //
   2802   UnicodeVSPrint (mPostReplaceFormat2, PcdGet16 (PcdShellPrintBufferSize), mPostReplaceFormat, Marker);
   2803 
   2804   if (Col != -1 && Row != -1) {
   2805     Status = gST->ConOut->SetCursorPosition(gST->ConOut, Col, Row);
   2806   }
   2807 
   2808   FormatWalker = mPostReplaceFormat2;
   2809   while (*FormatWalker != CHAR_NULL) {
   2810     //
   2811     // Find the next attribute change request
   2812     //
   2813     ResumeLocation = StrStr(FormatWalker, L"%");
   2814     if (ResumeLocation != NULL) {
   2815       *ResumeLocation = CHAR_NULL;
   2816     }
   2817     //
   2818     // print the current FormatWalker string
   2819     //
   2820     if (StrLen(FormatWalker)>0) {
   2821       Status = InternalPrintTo(FormatWalker);
   2822       if (EFI_ERROR(Status)) {
   2823         break;
   2824       }
   2825     }
   2826 
   2827     //
   2828     // update the attribute
   2829     //
   2830     if (ResumeLocation != NULL) {
   2831       if (*(ResumeLocation-1) == L'^') {
   2832         //
   2833         // Move cursor back 1 position to overwrite the ^
   2834         //
   2835         gST->ConOut->SetCursorPosition(gST->ConOut, gST->ConOut->Mode->CursorColumn - 1, gST->ConOut->Mode->CursorRow);
   2836 
   2837         //
   2838         // Print a simple '%' symbol
   2839         //
   2840         Status = InternalPrintTo(L"%");
   2841         ResumeLocation = ResumeLocation - 1;
   2842       } else {
   2843         switch (*(ResumeLocation+1)) {
   2844           case (L'N'):
   2845             gST->ConOut->SetAttribute(gST->ConOut, OriginalAttribute);
   2846             break;
   2847           case (L'E'):
   2848             gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_YELLOW, ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4)));
   2849             break;
   2850           case (L'H'):
   2851             gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_WHITE, ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4)));
   2852             break;
   2853           case (L'B'):
   2854             gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_BLUE, ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4)));
   2855             break;
   2856           case (L'V'):
   2857             gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_GREEN, ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4)));
   2858             break;
   2859           default:
   2860             //
   2861             // Print a simple '%' symbol
   2862             //
   2863             Status = InternalPrintTo(L"%");
   2864             if (EFI_ERROR(Status)) {
   2865               break;
   2866             }
   2867             ResumeLocation = ResumeLocation - 1;
   2868             break;
   2869         }
   2870       }
   2871     } else {
   2872       //
   2873       // reset to normal now...
   2874       //
   2875       break;
   2876     }
   2877 
   2878     //
   2879     // update FormatWalker to Resume + 2 (skip the % and the indicator)
   2880     //
   2881     FormatWalker = ResumeLocation + 2;
   2882   }
   2883 
   2884   gST->ConOut->SetAttribute(gST->ConOut, OriginalAttribute);
   2885 
   2886   SHELL_FREE_NON_NULL(mPostReplaceFormat);
   2887   SHELL_FREE_NON_NULL(mPostReplaceFormat2);
   2888   return (Status);
   2889 }
   2890 
   2891 /**
   2892   Print at a specific location on the screen.
   2893 
   2894   This function will move the cursor to a given screen location and print the specified string.
   2895 
   2896   If -1 is specified for either the Row or Col the current screen location for BOTH
   2897   will be used.
   2898 
   2899   If either Row or Col is out of range for the current console, then ASSERT.
   2900   If Format is NULL, then ASSERT.
   2901 
   2902   In addition to the standard %-based flags as supported by UefiLib Print() this supports
   2903   the following additional flags:
   2904     %N       -   Set output attribute to normal
   2905     %H       -   Set output attribute to highlight
   2906     %E       -   Set output attribute to error
   2907     %B       -   Set output attribute to blue color
   2908     %V       -   Set output attribute to green color
   2909 
   2910   Note: The background color is controlled by the shell command cls.
   2911 
   2912   @param[in] Col        the column to print at
   2913   @param[in] Row        the row to print at
   2914   @param[in] Format     the format string
   2915   @param[in] ...        The variable argument list.
   2916 
   2917   @return EFI_SUCCESS           The printing was successful.
   2918   @return EFI_DEVICE_ERROR      The console device reported an error.
   2919 **/
   2920 EFI_STATUS
   2921 EFIAPI
   2922 ShellPrintEx(
   2923   IN INT32                Col OPTIONAL,
   2924   IN INT32                Row OPTIONAL,
   2925   IN CONST CHAR16         *Format,
   2926   ...
   2927   )
   2928 {
   2929   VA_LIST           Marker;
   2930   EFI_STATUS        RetVal;
   2931   if (Format == NULL) {
   2932     return (EFI_INVALID_PARAMETER);
   2933   }
   2934   VA_START (Marker, Format);
   2935   RetVal = InternalShellPrintWorker(Col, Row, Format, Marker);
   2936   VA_END(Marker);
   2937   return(RetVal);
   2938 }
   2939 
   2940 /**
   2941   Print at a specific location on the screen.
   2942 
   2943   This function will move the cursor to a given screen location and print the specified string.
   2944 
   2945   If -1 is specified for either the Row or Col the current screen location for BOTH
   2946   will be used.
   2947 
   2948   If either Row or Col is out of range for the current console, then ASSERT.
   2949   If Format is NULL, then ASSERT.
   2950 
   2951   In addition to the standard %-based flags as supported by UefiLib Print() this supports
   2952   the following additional flags:
   2953     %N       -   Set output attribute to normal.
   2954     %H       -   Set output attribute to highlight.
   2955     %E       -   Set output attribute to error.
   2956     %B       -   Set output attribute to blue color.
   2957     %V       -   Set output attribute to green color.
   2958 
   2959   Note: The background color is controlled by the shell command cls.
   2960 
   2961   @param[in] Col                The column to print at.
   2962   @param[in] Row                The row to print at.
   2963   @param[in] Language           The language of the string to retrieve.  If this parameter
   2964                                 is NULL, then the current platform language is used.
   2965   @param[in] HiiFormatStringId  The format string Id for getting from Hii.
   2966   @param[in] HiiFormatHandle    The format string Handle for getting from Hii.
   2967   @param[in] ...                The variable argument list.
   2968 
   2969   @return EFI_SUCCESS           The printing was successful.
   2970   @return EFI_DEVICE_ERROR      The console device reported an error.
   2971 **/
   2972 EFI_STATUS
   2973 EFIAPI
   2974 ShellPrintHiiEx(
   2975   IN INT32                Col OPTIONAL,
   2976   IN INT32                Row OPTIONAL,
   2977   IN CONST CHAR8          *Language OPTIONAL,
   2978   IN CONST EFI_STRING_ID  HiiFormatStringId,
   2979   IN CONST EFI_HANDLE     HiiFormatHandle,
   2980   ...
   2981   )
   2982 {
   2983   VA_LIST           Marker;
   2984   CHAR16            *HiiFormatString;
   2985   EFI_STATUS        RetVal;
   2986 
   2987   RetVal = EFI_DEVICE_ERROR;
   2988 
   2989   VA_START (Marker, HiiFormatHandle);
   2990   HiiFormatString = HiiGetString(HiiFormatHandle, HiiFormatStringId, Language);
   2991   if (HiiFormatString != NULL) {
   2992     RetVal = InternalShellPrintWorker (Col, Row, HiiFormatString, Marker);
   2993     SHELL_FREE_NON_NULL (HiiFormatString);
   2994   }
   2995   VA_END(Marker);
   2996 
   2997   return (RetVal);
   2998 }
   2999 
   3000 /**
   3001   Function to determine if a given filename represents a file or a directory.
   3002 
   3003   @param[in] DirName      Path to directory to test.
   3004 
   3005   @retval EFI_SUCCESS             The Path represents a directory
   3006   @retval EFI_NOT_FOUND           The Path does not represent a directory
   3007   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
   3008   @return                         The path failed to open
   3009 **/
   3010 EFI_STATUS
   3011 EFIAPI
   3012 ShellIsDirectory(
   3013   IN CONST CHAR16 *DirName
   3014   )
   3015 {
   3016   EFI_STATUS        Status;
   3017   SHELL_FILE_HANDLE Handle;
   3018   CHAR16            *TempLocation;
   3019   CHAR16            *TempLocation2;
   3020 
   3021   ASSERT(DirName != NULL);
   3022 
   3023   Handle        = NULL;
   3024   TempLocation  = NULL;
   3025 
   3026   Status = ShellOpenFileByName(DirName, &Handle, EFI_FILE_MODE_READ, 0);
   3027   if (EFI_ERROR(Status)) {
   3028     //
   3029     // try good logic first.
   3030     //
   3031     if (gEfiShellProtocol != NULL) {
   3032       TempLocation  = StrnCatGrow(&TempLocation, NULL, DirName, 0);
   3033       if (TempLocation == NULL) {
   3034         ShellCloseFile(&Handle);
   3035         return (EFI_OUT_OF_RESOURCES);
   3036       }
   3037       TempLocation2 = StrStr(TempLocation, L":");
   3038       if (TempLocation2 != NULL && StrLen(StrStr(TempLocation, L":")) == 2) {
   3039         *(TempLocation2+1) = CHAR_NULL;
   3040       }
   3041       if (gEfiShellProtocol->GetDevicePathFromMap(TempLocation) != NULL) {
   3042         FreePool(TempLocation);
   3043         return (EFI_SUCCESS);
   3044       }
   3045       FreePool(TempLocation);
   3046     } else {
   3047       //
   3048       // probably a map name?!?!!?
   3049       //
   3050       TempLocation = StrStr(DirName, L"\\");
   3051       if (TempLocation != NULL && *(TempLocation+1) == CHAR_NULL) {
   3052         return (EFI_SUCCESS);
   3053       }
   3054     }
   3055     return (Status);
   3056   }
   3057 
   3058   if (FileHandleIsDirectory(Handle) == EFI_SUCCESS) {
   3059     ShellCloseFile(&Handle);
   3060     return (EFI_SUCCESS);
   3061   }
   3062   ShellCloseFile(&Handle);
   3063   return (EFI_NOT_FOUND);
   3064 }
   3065 
   3066 /**
   3067   Function to determine if a given filename represents a file.
   3068 
   3069   @param[in] Name         Path to file to test.
   3070 
   3071   @retval EFI_SUCCESS     The Path represents a file.
   3072   @retval EFI_NOT_FOUND   The Path does not represent a file.
   3073   @retval other           The path failed to open.
   3074 **/
   3075 EFI_STATUS
   3076 EFIAPI
   3077 ShellIsFile(
   3078   IN CONST CHAR16 *Name
   3079   )
   3080 {
   3081   EFI_STATUS        Status;
   3082   SHELL_FILE_HANDLE            Handle;
   3083 
   3084   ASSERT(Name != NULL);
   3085 
   3086   Handle = NULL;
   3087 
   3088   Status = ShellOpenFileByName(Name, &Handle, EFI_FILE_MODE_READ, 0);
   3089   if (EFI_ERROR(Status)) {
   3090     return (Status);
   3091   }
   3092 
   3093   if (FileHandleIsDirectory(Handle) != EFI_SUCCESS) {
   3094     ShellCloseFile(&Handle);
   3095     return (EFI_SUCCESS);
   3096   }
   3097   ShellCloseFile(&Handle);
   3098   return (EFI_NOT_FOUND);
   3099 }
   3100 
   3101 /**
   3102   Function to determine if a given filename represents a file.
   3103 
   3104   This will search the CWD and then the Path.
   3105 
   3106   If Name is NULL, then ASSERT.
   3107 
   3108   @param[in] Name         Path to file to test.
   3109 
   3110   @retval EFI_SUCCESS     The Path represents a file.
   3111   @retval EFI_NOT_FOUND   The Path does not represent a file.
   3112   @retval other           The path failed to open.
   3113 **/
   3114 EFI_STATUS
   3115 EFIAPI
   3116 ShellIsFileInPath(
   3117   IN CONST CHAR16 *Name
   3118   )
   3119 {
   3120   CHAR16      *NewName;
   3121   EFI_STATUS  Status;
   3122 
   3123   if (!EFI_ERROR(ShellIsFile(Name))) {
   3124     return (EFI_SUCCESS);
   3125   }
   3126 
   3127   NewName = ShellFindFilePath(Name);
   3128   if (NewName == NULL) {
   3129     return (EFI_NOT_FOUND);
   3130   }
   3131   Status = ShellIsFile(NewName);
   3132   FreePool(NewName);
   3133   return (Status);
   3134 }
   3135 
   3136 /**
   3137   Function return the number converted from a hex representation of a number.
   3138 
   3139   Note: this function cannot be used when (UINTN)(-1), (0xFFFFFFFF) may be a valid
   3140   result.  Use ShellConvertStringToUint64 instead.
   3141 
   3142   @param[in] String   String representation of a number.
   3143 
   3144   @return             The unsigned integer result of the conversion.
   3145   @retval (UINTN)(-1) An error occured.
   3146 **/
   3147 UINTN
   3148 EFIAPI
   3149 ShellHexStrToUintn(
   3150   IN CONST CHAR16 *String
   3151   )
   3152 {
   3153   UINT64        RetVal;
   3154 
   3155   if (!EFI_ERROR(ShellConvertStringToUint64(String, &RetVal, TRUE, TRUE))) {
   3156     return ((UINTN)RetVal);
   3157   }
   3158 
   3159   return ((UINTN)(-1));
   3160 }
   3161 
   3162 /**
   3163   Function to determine whether a string is decimal or hex representation of a number
   3164   and return the number converted from the string.  Spaces are always skipped.
   3165 
   3166   @param[in] String   String representation of a number
   3167 
   3168   @return             the number
   3169   @retval (UINTN)(-1) An error ocurred.
   3170 **/
   3171 UINTN
   3172 EFIAPI
   3173 ShellStrToUintn(
   3174   IN CONST CHAR16 *String
   3175   )
   3176 {
   3177   UINT64        RetVal;
   3178   BOOLEAN       Hex;
   3179 
   3180   Hex = FALSE;
   3181 
   3182   if (!InternalShellIsHexOrDecimalNumber(String, Hex, TRUE, FALSE)) {
   3183     Hex = TRUE;
   3184   }
   3185 
   3186   if (!EFI_ERROR(ShellConvertStringToUint64(String, &RetVal, Hex, TRUE))) {
   3187     return ((UINTN)RetVal);
   3188   }
   3189   return ((UINTN)(-1));
   3190 }
   3191 
   3192 /**
   3193   Safely append with automatic string resizing given length of Destination and
   3194   desired length of copy from Source.
   3195 
   3196   append the first D characters of Source to the end of Destination, where D is
   3197   the lesser of Count and the StrLen() of Source. If appending those D characters
   3198   will fit within Destination (whose Size is given as CurrentSize) and
   3199   still leave room for a NULL terminator, then those characters are appended,
   3200   starting at the original terminating NULL of Destination, and a new terminating
   3201   NULL is appended.
   3202 
   3203   If appending D characters onto Destination will result in a overflow of the size
   3204   given in CurrentSize the string will be grown such that the copy can be performed
   3205   and CurrentSize will be updated to the new size.
   3206 
   3207   If Source is NULL, there is nothing to append, just return the current buffer in
   3208   Destination.
   3209 
   3210   if Destination is NULL, then ASSERT()
   3211   if Destination's current length (including NULL terminator) is already more then
   3212   CurrentSize, then ASSERT()
   3213 
   3214   @param[in, out] Destination   The String to append onto
   3215   @param[in, out] CurrentSize   on call the number of bytes in Destination.  On
   3216                                 return possibly the new size (still in bytes).  if NULL
   3217                                 then allocate whatever is needed.
   3218   @param[in]      Source        The String to append from
   3219   @param[in]      Count         Maximum number of characters to append.  if 0 then
   3220                                 all are appended.
   3221 
   3222   @return Destination           return the resultant string.
   3223 **/
   3224 CHAR16*
   3225 EFIAPI
   3226 StrnCatGrow (
   3227   IN OUT CHAR16           **Destination,
   3228   IN OUT UINTN            *CurrentSize,
   3229   IN     CONST CHAR16     *Source,
   3230   IN     UINTN            Count
   3231   )
   3232 {
   3233   UINTN DestinationStartSize;
   3234   UINTN NewSize;
   3235 
   3236   //
   3237   // ASSERTs
   3238   //
   3239   ASSERT(Destination != NULL);
   3240 
   3241   //
   3242   // If there's nothing to do then just return Destination
   3243   //
   3244   if (Source == NULL) {
   3245     return (*Destination);
   3246   }
   3247 
   3248   //
   3249   // allow for un-initialized pointers, based on size being 0
   3250   //
   3251   if (CurrentSize != NULL && *CurrentSize == 0) {
   3252     *Destination = NULL;
   3253   }
   3254 
   3255   //
   3256   // allow for NULL pointers address as Destination
   3257   //
   3258   if (*Destination != NULL) {
   3259     ASSERT(CurrentSize != 0);
   3260     DestinationStartSize = StrSize(*Destination);
   3261     ASSERT(DestinationStartSize <= *CurrentSize);
   3262   } else {
   3263     DestinationStartSize = 0;
   3264 //    ASSERT(*CurrentSize == 0);
   3265   }
   3266 
   3267   //
   3268   // Append all of Source?
   3269   //
   3270   if (Count == 0) {
   3271     Count = StrLen(Source);
   3272   }
   3273 
   3274   //
   3275   // Test and grow if required
   3276   //
   3277   if (CurrentSize != NULL) {
   3278     NewSize = *CurrentSize;
   3279     if (NewSize < DestinationStartSize + (Count * sizeof(CHAR16))) {
   3280       while (NewSize < (DestinationStartSize + (Count*sizeof(CHAR16)))) {
   3281         NewSize += 2 * Count * sizeof(CHAR16);
   3282       }
   3283       *Destination = ReallocatePool(*CurrentSize, NewSize, *Destination);
   3284       *CurrentSize = NewSize;
   3285     }
   3286   } else {
   3287     NewSize = (Count+1)*sizeof(CHAR16);
   3288     *Destination = AllocateZeroPool(NewSize);
   3289   }
   3290 
   3291   //
   3292   // Now use standard StrnCat on a big enough buffer
   3293   //
   3294   if (*Destination == NULL) {
   3295     return (NULL);
   3296   }
   3297 
   3298   StrnCatS(*Destination, NewSize/sizeof(CHAR16), Source, Count);
   3299   return *Destination;
   3300 }
   3301 
   3302 /**
   3303   Prompt the user and return the resultant answer to the requestor.
   3304 
   3305   This function will display the requested question on the shell prompt and then
   3306   wait for an appropriate answer to be input from the console.
   3307 
   3308   if the SHELL_PROMPT_REQUEST_TYPE is SHELL_PROMPT_REQUEST_TYPE_YESNO, ShellPromptResponseTypeQuitContinue
   3309   or SHELL_PROMPT_REQUEST_TYPE_YESNOCANCEL then *Response is of type SHELL_PROMPT_RESPONSE.
   3310 
   3311   if the SHELL_PROMPT_REQUEST_TYPE is ShellPromptResponseTypeFreeform then *Response is of type
   3312   CHAR16*.
   3313 
   3314   In either case *Response must be callee freed if Response was not NULL;
   3315 
   3316   @param Type                     What type of question is asked.  This is used to filter the input
   3317                                   to prevent invalid answers to question.
   3318   @param Prompt                   Pointer to string prompt to use to request input.
   3319   @param Response                 Pointer to Response which will be populated upon return.
   3320 
   3321   @retval EFI_SUCCESS             The operation was sucessful.
   3322   @retval EFI_UNSUPPORTED         The operation is not supported as requested.
   3323   @retval EFI_INVALID_PARAMETER   A parameter was invalid.
   3324   @return other                   The operation failed.
   3325 **/
   3326 EFI_STATUS
   3327 EFIAPI
   3328 ShellPromptForResponse (
   3329   IN SHELL_PROMPT_REQUEST_TYPE   Type,
   3330   IN CHAR16         *Prompt OPTIONAL,
   3331   IN OUT VOID       **Response OPTIONAL
   3332   )
   3333 {
   3334   EFI_STATUS        Status;
   3335   EFI_INPUT_KEY     Key;
   3336   UINTN             EventIndex;
   3337   SHELL_PROMPT_RESPONSE          *Resp;
   3338   UINTN             Size;
   3339   CHAR16            *Buffer;
   3340 
   3341   Status  = EFI_UNSUPPORTED;
   3342   Resp    = NULL;
   3343   Buffer  = NULL;
   3344   Size    = 0;
   3345   if (Type != ShellPromptResponseTypeFreeform) {
   3346     Resp = (SHELL_PROMPT_RESPONSE*)AllocateZeroPool(sizeof(SHELL_PROMPT_RESPONSE));
   3347     if (Resp == NULL) {
   3348       return (EFI_OUT_OF_RESOURCES);
   3349     }
   3350   }
   3351 
   3352   switch(Type) {
   3353     case ShellPromptResponseTypeQuitContinue:
   3354       if (Prompt != NULL) {
   3355         ShellPrintEx(-1, -1, L"%s", Prompt);
   3356       }
   3357       //
   3358       // wait for valid response
   3359       //
   3360       gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
   3361       Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
   3362       if (EFI_ERROR(Status)) {
   3363         break;
   3364       }
   3365       ShellPrintEx(-1, -1, L"%c", Key.UnicodeChar);
   3366       if (Key.UnicodeChar == L'Q' || Key.UnicodeChar ==L'q') {
   3367         *Resp = ShellPromptResponseQuit;
   3368       } else {
   3369         *Resp = ShellPromptResponseContinue;
   3370       }
   3371       break;
   3372     case ShellPromptResponseTypeYesNoCancel:
   3373        if (Prompt != NULL) {
   3374         ShellPrintEx(-1, -1, L"%s", Prompt);
   3375       }
   3376       //
   3377       // wait for valid response
   3378       //
   3379       *Resp = ShellPromptResponseMax;
   3380       while (*Resp == ShellPromptResponseMax) {
   3381         if (ShellGetExecutionBreakFlag()) {
   3382           Status = EFI_ABORTED;
   3383           break;
   3384         }
   3385         gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
   3386         Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
   3387         if (EFI_ERROR(Status)) {
   3388           break;
   3389         }
   3390         ShellPrintEx(-1, -1, L"%c", Key.UnicodeChar);
   3391         switch (Key.UnicodeChar) {
   3392           case L'Y':
   3393           case L'y':
   3394             *Resp = ShellPromptResponseYes;
   3395             break;
   3396           case L'N':
   3397           case L'n':
   3398             *Resp = ShellPromptResponseNo;
   3399             break;
   3400           case L'C':
   3401           case L'c':
   3402             *Resp = ShellPromptResponseCancel;
   3403             break;
   3404         }
   3405       }
   3406       break;
   3407       case ShellPromptResponseTypeYesNoAllCancel:
   3408        if (Prompt != NULL) {
   3409         ShellPrintEx(-1, -1, L"%s", Prompt);
   3410       }
   3411       //
   3412       // wait for valid response
   3413       //
   3414       *Resp = ShellPromptResponseMax;
   3415       while (*Resp == ShellPromptResponseMax) {
   3416         if (ShellGetExecutionBreakFlag()) {
   3417           Status = EFI_ABORTED;
   3418           break;
   3419         }
   3420         gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
   3421         Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
   3422         if (EFI_ERROR(Status)) {
   3423           break;
   3424         }
   3425 
   3426         if (Key.UnicodeChar <= 127 && Key.UnicodeChar >= 32) {
   3427           ShellPrintEx (-1, -1, L"%c", Key.UnicodeChar);
   3428         }
   3429 
   3430         switch (Key.UnicodeChar) {
   3431           case L'Y':
   3432           case L'y':
   3433             *Resp = ShellPromptResponseYes;
   3434             break;
   3435           case L'N':
   3436           case L'n':
   3437             *Resp = ShellPromptResponseNo;
   3438             break;
   3439           case L'A':
   3440           case L'a':
   3441             *Resp = ShellPromptResponseAll;
   3442             break;
   3443           case L'C':
   3444           case L'c':
   3445             *Resp = ShellPromptResponseCancel;
   3446             break;
   3447         }
   3448       }
   3449       break;
   3450     case ShellPromptResponseTypeEnterContinue:
   3451     case ShellPromptResponseTypeAnyKeyContinue:
   3452       if (Prompt != NULL) {
   3453         ShellPrintEx(-1, -1, L"%s", Prompt);
   3454       }
   3455       //
   3456       // wait for valid response
   3457       //
   3458       *Resp = ShellPromptResponseMax;
   3459       while (*Resp == ShellPromptResponseMax) {
   3460         if (ShellGetExecutionBreakFlag()) {
   3461           Status = EFI_ABORTED;
   3462           break;
   3463         }
   3464         gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
   3465         if (Type == ShellPromptResponseTypeEnterContinue) {
   3466           Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
   3467           if (EFI_ERROR(Status)) {
   3468             break;
   3469           }
   3470           ShellPrintEx(-1, -1, L"%c", Key.UnicodeChar);
   3471           if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
   3472             *Resp = ShellPromptResponseContinue;
   3473             break;
   3474           }
   3475         }
   3476         if (Type == ShellPromptResponseTypeAnyKeyContinue) {
   3477           Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
   3478           ASSERT_EFI_ERROR(Status);
   3479           *Resp = ShellPromptResponseContinue;
   3480           break;
   3481         }
   3482       }
   3483       break;
   3484     case ShellPromptResponseTypeYesNo:
   3485        if (Prompt != NULL) {
   3486         ShellPrintEx(-1, -1, L"%s", Prompt);
   3487       }
   3488       //
   3489       // wait for valid response
   3490       //
   3491       *Resp = ShellPromptResponseMax;
   3492       while (*Resp == ShellPromptResponseMax) {
   3493         if (ShellGetExecutionBreakFlag()) {
   3494           Status = EFI_ABORTED;
   3495           break;
   3496         }
   3497         gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
   3498         Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
   3499         if (EFI_ERROR(Status)) {
   3500           break;
   3501         }
   3502         ShellPrintEx(-1, -1, L"%c", Key.UnicodeChar);
   3503         switch (Key.UnicodeChar) {
   3504           case L'Y':
   3505           case L'y':
   3506             *Resp = ShellPromptResponseYes;
   3507             break;
   3508           case L'N':
   3509           case L'n':
   3510             *Resp = ShellPromptResponseNo;
   3511             break;
   3512         }
   3513       }
   3514       break;
   3515     case ShellPromptResponseTypeFreeform:
   3516       if (Prompt != NULL) {
   3517         ShellPrintEx(-1, -1, L"%s", Prompt);
   3518       }
   3519       while(1) {
   3520         if (ShellGetExecutionBreakFlag()) {
   3521           Status = EFI_ABORTED;
   3522           break;
   3523         }
   3524         gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
   3525         Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
   3526         if (EFI_ERROR(Status)) {
   3527           break;
   3528         }
   3529         ShellPrintEx(-1, -1, L"%c", Key.UnicodeChar);
   3530         if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
   3531           break;
   3532         }
   3533         ASSERT((Buffer == NULL && Size == 0) || (Buffer != NULL));
   3534         StrnCatGrow(&Buffer, &Size, &Key.UnicodeChar, 1);
   3535       }
   3536       break;
   3537     //
   3538     // This is the location to add new prompt types.
   3539     // If your new type loops remember to add ExecutionBreak support.
   3540     //
   3541     default:
   3542       ASSERT(FALSE);
   3543   }
   3544 
   3545   if (Response != NULL) {
   3546     if (Resp != NULL) {
   3547       *Response = Resp;
   3548     } else if (Buffer != NULL) {
   3549       *Response = Buffer;
   3550     }
   3551   } else {
   3552     if (Resp != NULL) {
   3553       FreePool(Resp);
   3554     }
   3555     if (Buffer != NULL) {
   3556       FreePool(Buffer);
   3557     }
   3558   }
   3559 
   3560   ShellPrintEx(-1, -1, L"\r\n");
   3561   return (Status);
   3562 }
   3563 
   3564 /**
   3565   Prompt the user and return the resultant answer to the requestor.
   3566 
   3567   This function is the same as ShellPromptForResponse, except that the prompt is
   3568   automatically pulled from HII.
   3569 
   3570   @param Type     What type of question is asked.  This is used to filter the input
   3571                   to prevent invalid answers to question.
   3572   @param[in] HiiFormatStringId  The format string Id for getting from Hii.
   3573   @param[in] HiiFormatHandle    The format string Handle for getting from Hii.
   3574   @param Response               Pointer to Response which will be populated upon return.
   3575 
   3576   @retval EFI_SUCCESS the operation was sucessful.
   3577   @return other       the operation failed.
   3578 
   3579   @sa ShellPromptForResponse
   3580 **/
   3581 EFI_STATUS
   3582 EFIAPI
   3583 ShellPromptForResponseHii (
   3584   IN SHELL_PROMPT_REQUEST_TYPE         Type,
   3585   IN CONST EFI_STRING_ID  HiiFormatStringId,
   3586   IN CONST EFI_HANDLE     HiiFormatHandle,
   3587   IN OUT VOID             **Response
   3588   )
   3589 {
   3590   CHAR16      *Prompt;
   3591   EFI_STATUS  Status;
   3592 
   3593   Prompt = HiiGetString(HiiFormatHandle, HiiFormatStringId, NULL);
   3594   Status = ShellPromptForResponse(Type, Prompt, Response);
   3595   FreePool(Prompt);
   3596   return (Status);
   3597 }
   3598 
   3599 /**
   3600   Function to determin if an entire string is a valid number.
   3601 
   3602   If Hex it must be preceeded with a 0x or has ForceHex, set TRUE.
   3603 
   3604   @param[in] String       The string to evaluate.
   3605   @param[in] ForceHex     TRUE - always assume hex.
   3606   @param[in] StopAtSpace  TRUE to halt upon finding a space, FALSE to keep going.
   3607   @param[in] TimeNumbers        TRUE to allow numbers with ":", FALSE otherwise.
   3608 
   3609   @retval TRUE        It is all numeric (dec/hex) characters.
   3610   @retval FALSE       There is a non-numeric character.
   3611 **/
   3612 BOOLEAN
   3613 InternalShellIsHexOrDecimalNumber (
   3614   IN CONST CHAR16   *String,
   3615   IN CONST BOOLEAN  ForceHex,
   3616   IN CONST BOOLEAN  StopAtSpace,
   3617   IN CONST BOOLEAN  TimeNumbers
   3618   )
   3619 {
   3620   BOOLEAN Hex;
   3621 
   3622   //
   3623   // chop off a single negative sign
   3624   //
   3625   if (String != NULL && *String == L'-') {
   3626     String++;
   3627   }
   3628 
   3629   if (String == NULL) {
   3630     return (FALSE);
   3631   }
   3632 
   3633   //
   3634   // chop leading zeroes
   3635   //
   3636   while(String != NULL && *String == L'0'){
   3637     String++;
   3638   }
   3639   //
   3640   // allow '0x' or '0X', but not 'x' or 'X'
   3641   //
   3642   if (String != NULL && (*String == L'x' || *String == L'X')) {
   3643     if (*(String-1) != L'0') {
   3644       //
   3645       // we got an x without a preceeding 0
   3646       //
   3647       return (FALSE);
   3648     }
   3649     String++;
   3650     Hex = TRUE;
   3651   } else if (ForceHex) {
   3652     Hex = TRUE;
   3653   } else {
   3654     Hex = FALSE;
   3655   }
   3656 
   3657   //
   3658   // loop through the remaining characters and use the lib function
   3659   //
   3660   for ( ; String != NULL && *String != CHAR_NULL && !(StopAtSpace && *String == L' ') ; String++){
   3661     if (TimeNumbers && (String[0] == L':')) {
   3662       continue;
   3663     }
   3664     if (Hex) {
   3665       if (!ShellIsHexaDecimalDigitCharacter(*String)) {
   3666         return (FALSE);
   3667       }
   3668     } else {
   3669       if (!ShellIsDecimalDigitCharacter(*String)) {
   3670         return (FALSE);
   3671       }
   3672     }
   3673   }
   3674 
   3675   return (TRUE);
   3676 }
   3677 
   3678 /**
   3679   Function to determine if a given filename exists.
   3680 
   3681   @param[in] Name         Path to test.
   3682 
   3683   @retval EFI_SUCCESS     The Path represents a file.
   3684   @retval EFI_NOT_FOUND   The Path does not represent a file.
   3685   @retval other           The path failed to open.
   3686 **/
   3687 EFI_STATUS
   3688 EFIAPI
   3689 ShellFileExists(
   3690   IN CONST CHAR16 *Name
   3691   )
   3692 {
   3693   EFI_STATUS          Status;
   3694   EFI_SHELL_FILE_INFO *List;
   3695 
   3696   ASSERT(Name != NULL);
   3697 
   3698   List = NULL;
   3699   Status = ShellOpenFileMetaArg((CHAR16*)Name, EFI_FILE_MODE_READ, &List);
   3700   if (EFI_ERROR(Status)) {
   3701     return (Status);
   3702   }
   3703 
   3704   ShellCloseFileMetaArg(&List);
   3705 
   3706   return (EFI_SUCCESS);
   3707 }
   3708 
   3709 /**
   3710   Convert a Unicode character to upper case only if
   3711   it maps to a valid small-case ASCII character.
   3712 
   3713   This internal function only deal with Unicode character
   3714   which maps to a valid small-case ASCII character, i.e.
   3715   L'a' to L'z'. For other Unicode character, the input character
   3716   is returned directly.
   3717 
   3718   @param  Char  The character to convert.
   3719 
   3720   @retval LowerCharacter   If the Char is with range L'a' to L'z'.
   3721   @retval Unchanged        Otherwise.
   3722 
   3723 **/
   3724 CHAR16
   3725 InternalShellCharToUpper (
   3726   IN      CHAR16                    Char
   3727   )
   3728 {
   3729   if (Char >= L'a' && Char <= L'z') {
   3730     return (CHAR16) (Char - (L'a' - L'A'));
   3731   }
   3732 
   3733   return Char;
   3734 }
   3735 
   3736 /**
   3737   Convert a Unicode character to numerical value.
   3738 
   3739   This internal function only deal with Unicode character
   3740   which maps to a valid hexadecimal ASII character, i.e.
   3741   L'0' to L'9', L'a' to L'f' or L'A' to L'F'. For other
   3742   Unicode character, the value returned does not make sense.
   3743 
   3744   @param  Char  The character to convert.
   3745 
   3746   @return The numerical value converted.
   3747 
   3748 **/
   3749 UINTN
   3750 InternalShellHexCharToUintn (
   3751   IN      CHAR16                    Char
   3752   )
   3753 {
   3754   if (ShellIsDecimalDigitCharacter (Char)) {
   3755     return Char - L'0';
   3756   }
   3757 
   3758   return (UINTN) (10 + InternalShellCharToUpper (Char) - L'A');
   3759 }
   3760 
   3761 /**
   3762   Convert a Null-terminated Unicode hexadecimal string to a value of type UINT64.
   3763 
   3764   This function returns a value of type UINT64 by interpreting the contents
   3765   of the Unicode string specified by String as a hexadecimal number.
   3766   The format of the input Unicode string String is:
   3767 
   3768                   [spaces][zeros][x][hexadecimal digits].
   3769 
   3770   The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F].
   3771   The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix.
   3772   If "x" appears in the input string, it must be prefixed with at least one 0.
   3773   The function will ignore the pad space, which includes spaces or tab characters,
   3774   before [zeros], [x] or [hexadecimal digit]. The running zero before [x] or
   3775   [hexadecimal digit] will be ignored. Then, the decoding starts after [x] or the
   3776   first valid hexadecimal digit. Then, the function stops at the first character that is
   3777   a not a valid hexadecimal character or NULL, whichever one comes first.
   3778 
   3779   If String has only pad spaces, then zero is returned.
   3780   If String has no leading pad spaces, leading zeros or valid hexadecimal digits,
   3781   then zero is returned.
   3782 
   3783   @param[in]  String      A pointer to a Null-terminated Unicode string.
   3784   @param[out] Value       Upon a successful return the value of the conversion.
   3785   @param[in] StopAtSpace  FALSE to skip spaces.
   3786 
   3787   @retval EFI_SUCCESS             The conversion was successful.
   3788   @retval EFI_INVALID_PARAMETER   A parameter was NULL or invalid.
   3789   @retval EFI_DEVICE_ERROR        An overflow occured.
   3790 **/
   3791 EFI_STATUS
   3792 InternalShellStrHexToUint64 (
   3793   IN CONST CHAR16   *String,
   3794      OUT   UINT64   *Value,
   3795   IN CONST BOOLEAN  StopAtSpace
   3796   )
   3797 {
   3798   UINT64    Result;
   3799 
   3800   if (String == NULL || StrSize(String) == 0 || Value == NULL) {
   3801     return (EFI_INVALID_PARAMETER);
   3802   }
   3803 
   3804   //
   3805   // Ignore the pad spaces (space or tab)
   3806   //
   3807   while ((*String == L' ') || (*String == L'\t')) {
   3808     String++;
   3809   }
   3810 
   3811   //
   3812   // Ignore leading Zeros after the spaces
   3813   //
   3814   while (*String == L'0') {
   3815     String++;
   3816   }
   3817 
   3818   if (InternalShellCharToUpper (*String) == L'X') {
   3819     if (*(String - 1) != L'0') {
   3820       return 0;
   3821     }
   3822     //
   3823     // Skip the 'X'
   3824     //
   3825     String++;
   3826   }
   3827 
   3828   Result = 0;
   3829 
   3830   //
   3831   // there is a space where there should't be
   3832   //
   3833   if (*String == L' ') {
   3834     return (EFI_INVALID_PARAMETER);
   3835   }
   3836 
   3837   while (ShellIsHexaDecimalDigitCharacter (*String)) {
   3838     //
   3839     // If the Hex Number represented by String overflows according
   3840     // to the range defined by UINT64, then return EFI_DEVICE_ERROR.
   3841     //
   3842     if (!(Result <= (RShiftU64((((UINT64) ~0) - InternalShellHexCharToUintn (*String)), 4)))) {
   3843 //    if (!(Result <= ((((UINT64) ~0) - InternalShellHexCharToUintn (*String)) >> 4))) {
   3844       return (EFI_DEVICE_ERROR);
   3845     }
   3846 
   3847     Result = (LShiftU64(Result, 4));
   3848     Result += InternalShellHexCharToUintn (*String);
   3849     String++;
   3850 
   3851     //
   3852     // stop at spaces if requested
   3853     //
   3854     if (StopAtSpace && *String == L' ') {
   3855       break;
   3856     }
   3857   }
   3858 
   3859   *Value = Result;
   3860   return (EFI_SUCCESS);
   3861 }
   3862 
   3863 /**
   3864   Convert a Null-terminated Unicode decimal string to a value of
   3865   type UINT64.
   3866 
   3867   This function returns a value of type UINT64 by interpreting the contents
   3868   of the Unicode string specified by String as a decimal number. The format
   3869   of the input Unicode string String is:
   3870 
   3871                   [spaces] [decimal digits].
   3872 
   3873   The valid decimal digit character is in the range [0-9]. The
   3874   function will ignore the pad space, which includes spaces or
   3875   tab characters, before [decimal digits]. The running zero in the
   3876   beginning of [decimal digits] will be ignored. Then, the function
   3877   stops at the first character that is a not a valid decimal character
   3878   or a Null-terminator, whichever one comes first.
   3879 
   3880   If String has only pad spaces, then 0 is returned.
   3881   If String has no pad spaces or valid decimal digits,
   3882   then 0 is returned.
   3883 
   3884   @param[in]  String      A pointer to a Null-terminated Unicode string.
   3885   @param[out] Value       Upon a successful return the value of the conversion.
   3886   @param[in] StopAtSpace  FALSE to skip spaces.
   3887 
   3888   @retval EFI_SUCCESS             The conversion was successful.
   3889   @retval EFI_INVALID_PARAMETER   A parameter was NULL or invalid.
   3890   @retval EFI_DEVICE_ERROR        An overflow occured.
   3891 **/
   3892 EFI_STATUS
   3893 InternalShellStrDecimalToUint64 (
   3894   IN CONST CHAR16 *String,
   3895      OUT   UINT64 *Value,
   3896   IN CONST BOOLEAN  StopAtSpace
   3897   )
   3898 {
   3899   UINT64     Result;
   3900 
   3901   if (String == NULL || StrSize (String) == 0 || Value == NULL) {
   3902     return (EFI_INVALID_PARAMETER);
   3903   }
   3904 
   3905   //
   3906   // Ignore the pad spaces (space or tab)
   3907   //
   3908   while ((*String == L' ') || (*String == L'\t')) {
   3909     String++;
   3910   }
   3911 
   3912   //
   3913   // Ignore leading Zeros after the spaces
   3914   //
   3915   while (*String == L'0') {
   3916     String++;
   3917   }
   3918 
   3919   Result = 0;
   3920 
   3921   //
   3922   // Stop upon space if requested
   3923   // (if the whole value was 0)
   3924   //
   3925   if (StopAtSpace && *String == L' ') {
   3926     *Value = Result;
   3927     return (EFI_SUCCESS);
   3928   }
   3929 
   3930   while (ShellIsDecimalDigitCharacter (*String)) {
   3931     //
   3932     // If the number represented by String overflows according
   3933     // to the range defined by UINT64, then return EFI_DEVICE_ERROR.
   3934     //
   3935 
   3936     if (!(Result <= (DivU64x32((((UINT64) ~0) - (*String - L'0')),10)))) {
   3937       return (EFI_DEVICE_ERROR);
   3938     }
   3939 
   3940     Result = MultU64x32(Result, 10) + (*String - L'0');
   3941     String++;
   3942 
   3943     //
   3944     // Stop at spaces if requested
   3945     //
   3946     if (StopAtSpace && *String == L' ') {
   3947       break;
   3948     }
   3949   }
   3950 
   3951   *Value = Result;
   3952 
   3953   return (EFI_SUCCESS);
   3954 }
   3955 
   3956 /**
   3957   Function to verify and convert a string to its numerical value.
   3958 
   3959   If Hex it must be preceeded with a 0x, 0X, or has ForceHex set TRUE.
   3960 
   3961   @param[in] String       The string to evaluate.
   3962   @param[out] Value       Upon a successful return the value of the conversion.
   3963   @param[in] ForceHex     TRUE - always assume hex.
   3964   @param[in] StopAtSpace  FALSE to skip spaces.
   3965 
   3966   @retval EFI_SUCCESS             The conversion was successful.
   3967   @retval EFI_INVALID_PARAMETER   String contained an invalid character.
   3968   @retval EFI_NOT_FOUND           String was a number, but Value was NULL.
   3969 **/
   3970 EFI_STATUS
   3971 EFIAPI
   3972 ShellConvertStringToUint64(
   3973   IN CONST CHAR16   *String,
   3974      OUT   UINT64   *Value,
   3975   IN CONST BOOLEAN  ForceHex,
   3976   IN CONST BOOLEAN  StopAtSpace
   3977   )
   3978 {
   3979   UINT64        RetVal;
   3980   CONST CHAR16  *Walker;
   3981   EFI_STATUS    Status;
   3982   BOOLEAN       Hex;
   3983 
   3984   Hex = ForceHex;
   3985 
   3986   if (!InternalShellIsHexOrDecimalNumber(String, Hex, StopAtSpace, FALSE)) {
   3987     if (!Hex) {
   3988       Hex = TRUE;
   3989       if (!InternalShellIsHexOrDecimalNumber(String, Hex, StopAtSpace, FALSE)) {
   3990         return (EFI_INVALID_PARAMETER);
   3991       }
   3992     } else {
   3993       return (EFI_INVALID_PARAMETER);
   3994     }
   3995   }
   3996 
   3997   //
   3998   // Chop off leading spaces
   3999   //
   4000   for (Walker = String; Walker != NULL && *Walker != CHAR_NULL && *Walker == L' '; Walker++);
   4001 
   4002   //
   4003   // make sure we have something left that is numeric.
   4004   //
   4005   if (Walker == NULL || *Walker == CHAR_NULL || !InternalShellIsHexOrDecimalNumber(Walker, Hex, StopAtSpace, FALSE)) {
   4006     return (EFI_INVALID_PARAMETER);
   4007   }
   4008 
   4009   //
   4010   // do the conversion.
   4011   //
   4012   if (Hex || StrnCmp(Walker, L"0x", 2) == 0 || StrnCmp(Walker, L"0X", 2) == 0){
   4013     Status = InternalShellStrHexToUint64(Walker, &RetVal, StopAtSpace);
   4014   } else {
   4015     Status = InternalShellStrDecimalToUint64(Walker, &RetVal, StopAtSpace);
   4016   }
   4017 
   4018   if (Value == NULL && !EFI_ERROR(Status)) {
   4019     return (EFI_NOT_FOUND);
   4020   }
   4021 
   4022   if (Value != NULL) {
   4023     *Value = RetVal;
   4024   }
   4025 
   4026   return (Status);
   4027 }
   4028 
   4029 /**
   4030   Function to determin if an entire string is a valid number.
   4031 
   4032   If Hex it must be preceeded with a 0x or has ForceHex, set TRUE.
   4033 
   4034   @param[in] String       The string to evaluate.
   4035   @param[in] ForceHex     TRUE - always assume hex.
   4036   @param[in] StopAtSpace  TRUE to halt upon finding a space, FALSE to keep going.
   4037 
   4038   @retval TRUE        It is all numeric (dec/hex) characters.
   4039   @retval FALSE       There is a non-numeric character.
   4040 **/
   4041 BOOLEAN
   4042 EFIAPI
   4043 ShellIsHexOrDecimalNumber (
   4044   IN CONST CHAR16   *String,
   4045   IN CONST BOOLEAN  ForceHex,
   4046   IN CONST BOOLEAN  StopAtSpace
   4047   )
   4048 {
   4049   if (ShellConvertStringToUint64(String, NULL, ForceHex, StopAtSpace) == EFI_NOT_FOUND) {
   4050     return (TRUE);
   4051   }
   4052   return (FALSE);
   4053 }
   4054 
   4055 /**
   4056   Function to read a single line from a SHELL_FILE_HANDLE. The \n is not included in the returned
   4057   buffer.  The returned buffer must be callee freed.
   4058 
   4059   If the position upon start is 0, then the Ascii Boolean will be set.  This should be
   4060   maintained and not changed for all operations with the same file.
   4061 
   4062   @param[in]       Handle        SHELL_FILE_HANDLE to read from.
   4063   @param[in, out]  Ascii         Boolean value for indicating whether the file is
   4064                                  Ascii (TRUE) or UCS2 (FALSE).
   4065 
   4066   @return                        The line of text from the file.
   4067   @retval NULL                   There was not enough memory available.
   4068 
   4069   @sa ShellFileHandleReadLine
   4070 **/
   4071 CHAR16*
   4072 EFIAPI
   4073 ShellFileHandleReturnLine(
   4074   IN SHELL_FILE_HANDLE            Handle,
   4075   IN OUT BOOLEAN                *Ascii
   4076   )
   4077 {
   4078   CHAR16          *RetVal;
   4079   UINTN           Size;
   4080   EFI_STATUS      Status;
   4081 
   4082   Size = 0;
   4083   RetVal = NULL;
   4084 
   4085   Status = ShellFileHandleReadLine(Handle, RetVal, &Size, FALSE, Ascii);
   4086   if (Status == EFI_BUFFER_TOO_SMALL) {
   4087     RetVal = AllocateZeroPool(Size);
   4088     if (RetVal == NULL) {
   4089       return (NULL);
   4090     }
   4091     Status = ShellFileHandleReadLine(Handle, RetVal, &Size, FALSE, Ascii);
   4092 
   4093   }
   4094   if (Status == EFI_END_OF_FILE && RetVal != NULL && *RetVal != CHAR_NULL) {
   4095     Status = EFI_SUCCESS;
   4096   }
   4097   if (EFI_ERROR(Status) && (RetVal != NULL)) {
   4098     FreePool(RetVal);
   4099     RetVal = NULL;
   4100   }
   4101   return (RetVal);
   4102 }
   4103 
   4104 /**
   4105   Function to read a single line (up to but not including the \n) from a SHELL_FILE_HANDLE.
   4106 
   4107   If the position upon start is 0, then the Ascii Boolean will be set.  This should be
   4108   maintained and not changed for all operations with the same file.
   4109 
   4110   NOTE: LINES THAT ARE RETURNED BY THIS FUNCTION ARE UCS2, EVEN IF THE FILE BEING READ
   4111         IS IN ASCII FORMAT.
   4112 
   4113   @param[in]       Handle        SHELL_FILE_HANDLE to read from.
   4114   @param[in, out]  Buffer        The pointer to buffer to read into. If this function
   4115                                  returns EFI_SUCCESS, then on output Buffer will
   4116                                  contain a UCS2 string, even if the file being
   4117                                  read is ASCII.
   4118   @param[in, out]  Size          On input, pointer to number of bytes in Buffer.
   4119                                  On output, unchanged unless Buffer is too small
   4120                                  to contain the next line of the file. In that
   4121                                  case Size is set to the number of bytes needed
   4122                                  to hold the next line of the file (as a UCS2
   4123                                  string, even if it is an ASCII file).
   4124   @param[in]       Truncate      If the buffer is large enough, this has no effect.
   4125                                  If the buffer is is too small and Truncate is TRUE,
   4126                                  the line will be truncated.
   4127                                  If the buffer is is too small and Truncate is FALSE,
   4128                                  then no read will occur.
   4129 
   4130   @param[in, out]  Ascii         Boolean value for indicating whether the file is
   4131                                  Ascii (TRUE) or UCS2 (FALSE).
   4132 
   4133   @retval EFI_SUCCESS           The operation was successful.  The line is stored in
   4134                                 Buffer.
   4135   @retval EFI_END_OF_FILE       There are no more lines in the file.
   4136   @retval EFI_INVALID_PARAMETER Handle was NULL.
   4137   @retval EFI_INVALID_PARAMETER Size was NULL.
   4138   @retval EFI_BUFFER_TOO_SMALL  Size was not large enough to store the line.
   4139                                 Size was updated to the minimum space required.
   4140 **/
   4141 EFI_STATUS
   4142 EFIAPI
   4143 ShellFileHandleReadLine(
   4144   IN SHELL_FILE_HANDLE          Handle,
   4145   IN OUT CHAR16                 *Buffer,
   4146   IN OUT UINTN                  *Size,
   4147   IN BOOLEAN                    Truncate,
   4148   IN OUT BOOLEAN                *Ascii
   4149   )
   4150 {
   4151   EFI_STATUS  Status;
   4152   CHAR16      CharBuffer;
   4153   UINTN       CharSize;
   4154   UINTN       CountSoFar;
   4155   UINT64      OriginalFilePosition;
   4156 
   4157 
   4158   if (Handle == NULL
   4159     ||Size   == NULL
   4160    ){
   4161     return (EFI_INVALID_PARAMETER);
   4162   }
   4163   if (Buffer == NULL) {
   4164     ASSERT(*Size == 0);
   4165   } else {
   4166     *Buffer = CHAR_NULL;
   4167   }
   4168   gEfiShellProtocol->GetFilePosition(Handle, &OriginalFilePosition);
   4169   if (OriginalFilePosition == 0) {
   4170     CharSize = sizeof(CHAR16);
   4171     Status = gEfiShellProtocol->ReadFile(Handle, &CharSize, &CharBuffer);
   4172     ASSERT_EFI_ERROR(Status);
   4173     if (CharBuffer == gUnicodeFileTag) {
   4174       *Ascii = FALSE;
   4175     } else {
   4176       *Ascii = TRUE;
   4177       gEfiShellProtocol->SetFilePosition(Handle, OriginalFilePosition);
   4178     }
   4179   }
   4180 
   4181   if (*Ascii) {
   4182     CharSize = sizeof(CHAR8);
   4183   } else {
   4184     CharSize = sizeof(CHAR16);
   4185   }
   4186   for (CountSoFar = 0;;CountSoFar++){
   4187     CharBuffer = 0;
   4188     Status = gEfiShellProtocol->ReadFile(Handle, &CharSize, &CharBuffer);
   4189     if (  EFI_ERROR(Status)
   4190        || CharSize == 0
   4191        || (CharBuffer == L'\n' && !(*Ascii))
   4192        || (CharBuffer ==  '\n' && *Ascii)
   4193      ){
   4194       if (CharSize == 0) {
   4195         Status = EFI_END_OF_FILE;
   4196       }
   4197       break;
   4198     }
   4199     //
   4200     // if we have space save it...
   4201     //
   4202     if ((CountSoFar+1)*sizeof(CHAR16) < *Size){
   4203       ASSERT(Buffer != NULL);
   4204       ((CHAR16*)Buffer)[CountSoFar] = CharBuffer;
   4205       ((CHAR16*)Buffer)[CountSoFar+1] = CHAR_NULL;
   4206     }
   4207   }
   4208 
   4209   //
   4210   // if we ran out of space tell when...
   4211   //
   4212   if ((CountSoFar+1)*sizeof(CHAR16) > *Size){
   4213     *Size = (CountSoFar+1)*sizeof(CHAR16);
   4214     if (!Truncate) {
   4215       gEfiShellProtocol->SetFilePosition(Handle, OriginalFilePosition);
   4216     } else {
   4217       DEBUG((DEBUG_WARN, "The line was truncated in ShellFileHandleReadLine"));
   4218     }
   4219     return (EFI_BUFFER_TOO_SMALL);
   4220   }
   4221   while(Buffer[StrLen(Buffer)-1] == L'\r') {
   4222     Buffer[StrLen(Buffer)-1] = CHAR_NULL;
   4223   }
   4224 
   4225   return (Status);
   4226 }
   4227 
   4228 /**
   4229   Function to print help file / man page content in the spec from the UEFI Shell protocol GetHelpText function.
   4230 
   4231   @param[in] CommandToGetHelpOn  Pointer to a string containing the command name of help file to be printed.
   4232   @param[in] SectionToGetHelpOn  Pointer to the section specifier(s).
   4233   @param[in] PrintCommandText    If TRUE, prints the command followed by the help content, otherwise prints
   4234                                  the help content only.
   4235   @retval EFI_DEVICE_ERROR       The help data format was incorrect.
   4236   @retval EFI_NOT_FOUND          The help data could not be found.
   4237   @retval EFI_SUCCESS            The operation was successful.
   4238 **/
   4239 EFI_STATUS
   4240 EFIAPI
   4241 ShellPrintHelp (
   4242   IN CONST CHAR16     *CommandToGetHelpOn,
   4243   IN CONST CHAR16     *SectionToGetHelpOn,
   4244   IN BOOLEAN          PrintCommandText
   4245   )
   4246 {
   4247 	EFI_STATUS          Status;
   4248 	CHAR16              *OutText;
   4249 
   4250 	OutText = NULL;
   4251 
   4252   //
   4253   // Get the string to print based
   4254   //
   4255 	Status = gEfiShellProtocol->GetHelpText (CommandToGetHelpOn, SectionToGetHelpOn, &OutText);
   4256 
   4257   //
   4258   // make sure we got a valid string
   4259   //
   4260   if (EFI_ERROR(Status)){
   4261     return Status;
   4262 	}
   4263   if (OutText == NULL || StrLen(OutText) == 0) {
   4264     return EFI_NOT_FOUND;
   4265 	}
   4266 
   4267   //
   4268   // Chop off trailing stuff we dont need
   4269   //
   4270   while (OutText[StrLen(OutText)-1] == L'\r' || OutText[StrLen(OutText)-1] == L'\n' || OutText[StrLen(OutText)-1] == L' ') {
   4271     OutText[StrLen(OutText)-1] = CHAR_NULL;
   4272   }
   4273 
   4274   //
   4275   // Print this out to the console
   4276   //
   4277   if (PrintCommandText) {
   4278     ShellPrintEx(-1, -1, L"%H%-14s%N- %s\r\n", CommandToGetHelpOn, OutText);
   4279   } else {
   4280     ShellPrintEx(-1, -1, L"%N%s\r\n", OutText);
   4281   }
   4282 
   4283   SHELL_FREE_NON_NULL(OutText);
   4284 
   4285 	return EFI_SUCCESS;
   4286 }
   4287 
   4288 /**
   4289   Function to delete a file by name
   4290 
   4291   @param[in]       FileName       Pointer to file name to delete.
   4292 
   4293   @retval EFI_SUCCESS             the file was deleted sucessfully
   4294   @retval EFI_WARN_DELETE_FAILURE the handle was closed, but the file was not
   4295                                   deleted
   4296   @retval EFI_INVALID_PARAMETER   One of the parameters has an invalid value.
   4297   @retval EFI_NOT_FOUND           The specified file could not be found on the
   4298                                   device or the file system could not be found
   4299                                   on the device.
   4300   @retval EFI_NO_MEDIA            The device has no medium.
   4301   @retval EFI_MEDIA_CHANGED       The device has a different medium in it or the
   4302                                   medium is no longer supported.
   4303   @retval EFI_DEVICE_ERROR        The device reported an error.
   4304   @retval EFI_VOLUME_CORRUPTED    The file system structures are corrupted.
   4305   @retval EFI_WRITE_PROTECTED     The file or medium is write protected.
   4306   @retval EFI_ACCESS_DENIED       The file was opened read only.
   4307   @retval EFI_OUT_OF_RESOURCES    Not enough resources were available to open the
   4308                                   file.
   4309   @retval other                   The file failed to open
   4310 **/
   4311 EFI_STATUS
   4312 EFIAPI
   4313 ShellDeleteFileByName(
   4314   IN CONST CHAR16               *FileName
   4315   )
   4316 {
   4317   EFI_STATUS                Status;
   4318   SHELL_FILE_HANDLE         FileHandle;
   4319 
   4320   Status = ShellFileExists(FileName);
   4321 
   4322   if (Status == EFI_SUCCESS){
   4323     Status = ShellOpenFileByName(FileName, &FileHandle, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0x0);
   4324     if (Status == EFI_SUCCESS){
   4325       Status = ShellDeleteFile(&FileHandle);
   4326     }
   4327   }
   4328 
   4329   return(Status);
   4330 
   4331 }
   4332 
   4333 /**
   4334   Cleans off all the quotes in the string.
   4335 
   4336   @param[in]     OriginalString   pointer to the string to be cleaned.
   4337   @param[out]   CleanString      The new string with all quotes removed.
   4338                                                   Memory allocated in the function and free
   4339                                                   by caller.
   4340 
   4341   @retval EFI_SUCCESS   The operation was successful.
   4342 **/
   4343 EFI_STATUS
   4344 InternalShellStripQuotes (
   4345   IN  CONST CHAR16     *OriginalString,
   4346   OUT CHAR16           **CleanString
   4347   )
   4348 {
   4349   CHAR16            *Walker;
   4350 
   4351   if (OriginalString == NULL || CleanString == NULL) {
   4352     return EFI_INVALID_PARAMETER;
   4353   }
   4354 
   4355   *CleanString = AllocateCopyPool (StrSize (OriginalString), OriginalString);
   4356   if (*CleanString == NULL) {
   4357     return EFI_OUT_OF_RESOURCES;
   4358   }
   4359 
   4360   for (Walker = *CleanString; Walker != NULL && *Walker != CHAR_NULL ; Walker++) {
   4361     if (*Walker == L'\"') {
   4362       CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0]));
   4363     }
   4364   }
   4365 
   4366   return EFI_SUCCESS;
   4367 }
   4368 
   4369