]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ShellPkg/Library/UefiShellLib/UefiShellLib.c
updating comments mostly. also added some new lib functions.
[mirror_edk2.git] / ShellPkg / Library / UefiShellLib / UefiShellLib.c
index 3a56903789a1693b0c57a6215148e83337a6d8b7..e1aaa9db80c818b6ff4b10fb493b2ce91a4d9aae 100644 (file)
@@ -1,36 +1,17 @@
 /** @file\r
   Provides interface to shell functionality for shell commands and applications.\r
 \r
-Copyright (c) 2006 - 2009, Intel Corporation\r
-All rights reserved. This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution.  The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
+  Copyright (c) 2006 - 2010, Intel Corporation<BR>\r
+  All rights reserved. This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
 \r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
 \r
 **/\r
 \r
-#include <Uefi.h>\r
-#include <Library/ShellLib.h>\r
-#include <Library/UefiBootServicesTableLib.h>\r
-#include <Library/BaseLib.h>\r
-#include <Library/BaseMemoryLib.h>\r
-#include <Library/DebugLib.h>\r
-#include <Library/MemoryAllocationLib.h>\r
-#include <Library/DevicePathLib.h>\r
-#include <Library/PcdLib.h>\r
-#include <Library/FileHandleLib.h>\r
-#include <Library/PrintLib.h>\r
-#include <Library/UefiLib.h>\r
-\r
-#include <Protocol/EfiShellEnvironment2.h>\r
-#include <Protocol/EfiShellInterface.h>\r
-#include <Protocol/EfiShell.h>\r
-#include <Protocol/EfiShellParameters.h>\r
-#include <Protocol/SimpleFileSystem.h>\r
-\r
 #include "UefiShellLib.h"\r
 \r
 #define MAX_FILE_NAME_LEN 522 // (20 * (6+5+2))+1) unicode characters from EFI FAT spec (doubled for bytes)\r
@@ -52,6 +33,31 @@ STATIC EFI_SHELL_PROTOCOL            *mEfiShellProtocol;
 STATIC EFI_SHELL_PARAMETERS_PROTOCOL *mEfiShellParametersProtocol;\r
 STATIC EFI_HANDLE                    mEfiShellEnvironment2Handle;\r
 STATIC FILE_HANDLE_FUNCTION_MAP      FileFunctionMap;\r
+STATIC UINTN                         mTotalParameterCount;\r
+STATIC CHAR16                        *mPostReplaceFormat;\r
+STATIC CHAR16                        *mPostReplaceFormat2;\r
+\r
+/**\r
+  Check if a Unicode character is a hexadecimal character.\r
+\r
+  This internal function checks if a Unicode character is a \r
+  decimal character.  The valid hexadecimal character is \r
+  L'0' to L'9', L'a' to L'f', or L'A' to L'F'.\r
+\r
+\r
+  @param  Char  The character to check against.\r
+\r
+  @retval TRUE  If the Char is a hexadecmial character.\r
+  @retval FALSE If the Char is not a hexadecmial character.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+ShellLibIsHexaDecimalDigitCharacter (\r
+  IN      CHAR16                    Char\r
+  ) {\r
+  return (BOOLEAN) ((Char >= L'0' && Char <= L'9') || (Char >= L'A' && Char <= L'F') || (Char >= L'a' && Char <= L'f'));\r
+}\r
 \r
 /**\r
   helper function to find ShellEnvironment2 for constructor\r
@@ -60,8 +66,7 @@ EFI_STATUS
 EFIAPI\r
 ShellFindSE2 (\r
   IN EFI_HANDLE        ImageHandle\r
-  )\r
-{\r
+  ) {\r
   EFI_STATUS  Status;\r
   EFI_HANDLE  *Buffer;\r
   UINTN       BufferSize;\r
@@ -79,9 +84,7 @@ ShellFindSE2 (
   //\r
   // look for the mEfiShellEnvironment2 protocol at a higher level\r
   //\r
-  if (EFI_ERROR (Status) || !(CompareGuid (&mEfiShellEnvironment2->SESGuid, &gEfiShellEnvironment2ExtGuid) != FALSE &&\r
-     (mEfiShellEnvironment2->MajorVersion > EFI_SHELL_MAJOR_VER ||\r
-     (mEfiShellEnvironment2->MajorVersion == EFI_SHELL_MAJOR_VER && mEfiShellEnvironment2->MinorVersion >= EFI_SHELL_MINOR_VER)))) {\r
+  if (EFI_ERROR (Status) || !(CompareGuid (&mEfiShellEnvironment2->SESGuid, &gEfiShellEnvironment2ExtGuid) != FALSE)){\r
     //\r
     // figure out how big of a buffer we need.\r
     //\r
@@ -91,15 +94,19 @@ ShellFindSE2 (
                                 &BufferSize,\r
                                 Buffer\r
                                 );\r
-    ASSERT(Status == EFI_BUFFER_TOO_SMALL);\r
-    Buffer = (EFI_HANDLE*)AllocatePool(BufferSize);\r
-    ASSERT(Buffer != NULL);\r
-    Status = gBS->LocateHandle (ByProtocol,\r
-                                &gEfiShellEnvironment2Guid,\r
-                                NULL, // ignored for ByProtocol\r
-                                &BufferSize,\r
-                                Buffer\r
-                                );\r
+    //\r
+    // maybe it's not there???\r
+    //\r
+    if (Status == EFI_BUFFER_TOO_SMALL) {\r
+      Buffer = (EFI_HANDLE*)AllocatePool(BufferSize);\r
+      ASSERT(Buffer != NULL);\r
+      Status = gBS->LocateHandle (ByProtocol,\r
+                                  &gEfiShellEnvironment2Guid,\r
+                                  NULL, // ignored for ByProtocol\r
+                                  &BufferSize,\r
+                                  Buffer\r
+                                  );\r
+    }\r
     if (!EFI_ERROR (Status)) {\r
       //\r
       // now parse the list of returned handles\r
@@ -113,9 +120,7 @@ ShellFindSE2 (
                                    NULL,\r
                                    EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
                                    );\r
-         if (CompareGuid (&mEfiShellEnvironment2->SESGuid, &gEfiShellEnvironment2ExtGuid) != FALSE &&\r
-          (mEfiShellEnvironment2->MajorVersion > EFI_SHELL_MAJOR_VER ||\r
-          (mEfiShellEnvironment2->MajorVersion == EFI_SHELL_MAJOR_VER && mEfiShellEnvironment2->MinorVersion >= EFI_SHELL_MINOR_VER))) {\r
+         if (CompareGuid (&mEfiShellEnvironment2->SESGuid, &gEfiShellEnvironment2ExtGuid) != FALSE) {\r
           mEfiShellEnvironment2Handle = Buffer[HandleIndex];\r
           Status = EFI_SUCCESS;\r
           break;\r
@@ -134,9 +139,20 @@ EFIAPI
 ShellLibConstructorWorker (\r
   IN EFI_HANDLE        ImageHandle,\r
   IN EFI_SYSTEM_TABLE  *SystemTable\r
-){\r
+  ) {\r
   EFI_STATUS Status;\r
 \r
+  ASSERT(PcdGet16 (PcdShellPrintBufferSize) < PcdGet32 (PcdMaximumUnicodeStringLength));\r
+  mPostReplaceFormat = AllocateZeroPool (PcdGet16 (PcdShellPrintBufferSize));\r
+  ASSERT (mPostReplaceFormat != NULL);\r
+  mPostReplaceFormat2 = AllocateZeroPool (PcdGet16 (PcdShellPrintBufferSize));\r
+  ASSERT (mPostReplaceFormat2 != NULL);\r
+\r
+  //\r
+  // Set the parameter count to an invalid number\r
+  //\r
+  mTotalParameterCount = (UINTN)(-1);\r
+\r
   //\r
   // UEFI 2.0 shell interfaces (used preferentially)\r
   //\r
@@ -230,8 +246,7 @@ EFIAPI
 ShellLibConstructor (\r
   IN EFI_HANDLE        ImageHandle,\r
   IN EFI_SYSTEM_TABLE  *SystemTable\r
-  )\r
-{\r
+  ) {\r
 \r
 \r
   mEfiShellEnvironment2       = NULL;\r
@@ -239,6 +254,8 @@ ShellLibConstructor (
   mEfiShellParametersProtocol = NULL;\r
   mEfiShellInterface          = NULL;\r
   mEfiShellEnvironment2Handle = NULL;\r
+  mPostReplaceFormat          = NULL;\r
+  mPostReplaceFormat2         = NULL;\r
 \r
   //\r
   // verify that auto initialize is not set false\r
@@ -258,7 +275,7 @@ EFIAPI
 ShellLibDestructor (\r
   IN EFI_HANDLE        ImageHandle,\r
   IN EFI_SYSTEM_TABLE  *SystemTable\r
-  ){\r
+  ) {\r
   if (mEfiShellEnvironment2 != NULL) {\r
     gBS->CloseProtocol(mEfiShellEnvironment2Handle==NULL?ImageHandle:mEfiShellEnvironment2Handle,\r
                        &gEfiShellEnvironment2Guid,\r
@@ -288,6 +305,16 @@ ShellLibDestructor (
     mEfiShellParametersProtocol = NULL;\r
   }\r
   mEfiShellEnvironment2Handle = NULL;\r
+\r
+  if (mPostReplaceFormat != NULL) {\r
+    FreePool(mPostReplaceFormat);\r
+  }\r
+  if (mPostReplaceFormat2 != NULL) {\r
+    FreePool(mPostReplaceFormat2);\r
+  }\r
+  mPostReplaceFormat          = NULL;\r
+  mPostReplaceFormat2         = NULL;\r
+\r
   return (EFI_SUCCESS);\r
 }\r
 \r
@@ -344,8 +371,7 @@ EFI_FILE_INFO*
 EFIAPI\r
 ShellGetFileInfo (\r
   IN EFI_FILE_HANDLE            FileHandle\r
-  )\r
-{\r
+  ) {\r
   return (FileFunctionMap.GetFileInfo(FileHandle));\r
 }\r
 \r
@@ -372,8 +398,7 @@ EFIAPI
 ShellSetFileInfo (\r
   IN EFI_FILE_HANDLE           FileHandle,\r
   IN EFI_FILE_INFO              *FileInfo\r
-  )\r
-{\r
+  ) {\r
   return (FileFunctionMap.SetFileInfo(FileHandle, FileInfo));\r
 }  \r
   \r
@@ -415,8 +440,7 @@ ShellOpenFileByDevicePath(
   OUT EFI_FILE_HANDLE                  *FileHandle,\r
   IN UINT64                            OpenMode,\r
   IN UINT64                            Attributes\r
-  )\r
-{\r
+  ) {\r
   CHAR16      *FileName;\r
   EFI_STATUS  Status;\r
   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *EfiSimpleFileSystemProtocol;\r
@@ -562,12 +586,11 @@ ShellOpenFileByDevicePath(
 EFI_STATUS\r
 EFIAPI\r
 ShellOpenFileByName(\r
-  IN CHAR16                              *FileName,\r
+  IN CONST CHAR16                          *FileName,\r
   OUT EFI_FILE_HANDLE           *FileHandle,\r
   IN UINT64                     OpenMode,\r
   IN UINT64                            Attributes\r
-  )\r
-{\r
+  ) {\r
   EFI_HANDLE                    DeviceHandle;\r
   EFI_DEVICE_PATH_PROTOCOL      *FilePath;\r
   EFI_STATUS                    Status;\r
@@ -585,11 +608,12 @@ ShellOpenFileByName(
     Status = mEfiShellProtocol->OpenFileByName(FileName,\r
                                                FileHandle,\r
                                                OpenMode);\r
-    if (!EFI_ERROR(Status)){\r
-      FileInfo = FileHandleGetInfo(*FileHandle);\r
+    if (!EFI_ERROR(Status) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)){\r
+      FileInfo = FileFunctionMap.GetFileInfo(*FileHandle);\r
       ASSERT(FileInfo != NULL);\r
       FileInfo->Attribute = Attributes;\r
-      Status = FileHandleSetInfo(*FileHandle, FileInfo);\r
+      Status = FileFunctionMap.SetFileInfo(*FileHandle, FileInfo);\r
+      FreePool(FileInfo);\r
     }\r
     return (Status);\r
   } \r
@@ -599,7 +623,7 @@ ShellOpenFileByName(
   // since this will use EFI method again that will open it.\r
   //\r
   ASSERT(mEfiShellEnvironment2 != NULL);\r
-  FilePath = mEfiShellEnvironment2->NameToPath (FileName);\r
+  FilePath = mEfiShellEnvironment2->NameToPath ((CHAR16*)FileName);\r
   if (FileDevicePath != NULL) {\r
     return (ShellOpenFileByDevicePath(&FilePath,\r
                                       &DeviceHandle,\r
@@ -640,18 +664,24 @@ ShellOpenFileByName(
 EFI_STATUS\r
 EFIAPI\r
 ShellCreateDirectory(\r
-  IN CHAR16                   *DirectoryName,\r
+  IN CONST CHAR16             *DirectoryName,\r
   OUT EFI_FILE_HANDLE         *FileHandle\r
-  )\r
-{\r
-  //\r
-  // this is a pass thru to the open file function with sepcific open mode and attributes\r
-  //\r
-  return (ShellOpenFileByName(DirectoryName,\r
-                              FileHandle,\r
-                              EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE,\r
-                              EFI_FILE_DIRECTORY\r
-                              ));\r
+  ) {\r
+  if (mEfiShellProtocol != NULL) {\r
+    //\r
+    // Use UEFI Shell 2.0 method\r
+    //\r
+    return (mEfiShellProtocol->CreateFile(DirectoryName,\r
+                          EFI_FILE_DIRECTORY,\r
+                          FileHandle\r
+                          ));\r
+  } else {\r
+    return (ShellOpenFileByName(DirectoryName,\r
+                                FileHandle,\r
+                                EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE,\r
+                                EFI_FILE_DIRECTORY\r
+                                ));\r
+  }\r
 }\r
 \r
 /**\r
@@ -689,8 +719,7 @@ ShellReadFile(
   IN EFI_FILE_HANDLE            FileHandle,\r
   IN OUT UINTN                  *BufferSize,\r
   OUT VOID                      *Buffer\r
-  )\r
-{\r
+  ) {\r
   return (FileFunctionMap.ReadFile(FileHandle, BufferSize, Buffer));\r
 }\r
 \r
@@ -725,8 +754,7 @@ ShellWriteFile(
   IN EFI_FILE_HANDLE            FileHandle,\r
   IN OUT UINTN                  *BufferSize,\r
   IN VOID                       *Buffer\r
-  )\r
-{\r
+  ) {\r
   return (FileFunctionMap.WriteFile(FileHandle, BufferSize, Buffer));\r
 }\r
 \r
@@ -745,8 +773,7 @@ EFI_STATUS
 EFIAPI\r
 ShellCloseFile (\r
   IN EFI_FILE_HANDLE            *FileHandle\r
-  )\r
-{\r
+  ) {\r
   return (FileFunctionMap.CloseFile(*FileHandle));\r
 }\r
 \r
@@ -768,8 +795,7 @@ EFI_STATUS
 EFIAPI\r
 ShellDeleteFile (\r
   IN EFI_FILE_HANDLE           *FileHandle\r
-  )\r
-{\r
+  ) {\r
   return (FileFunctionMap.DeleteFile(*FileHandle));\r
 }\r
 \r
@@ -797,8 +823,7 @@ EFIAPI
 ShellSetFilePosition (\r
   IN EFI_FILE_HANDLE           FileHandle,\r
   IN UINT64            Position\r
-  )\r
-{\r
+  ) {\r
   return (FileFunctionMap.SetFilePosition(FileHandle, Position));\r
 }\r
 \r
@@ -822,8 +847,7 @@ EFIAPI
 ShellGetFilePosition (\r
   IN EFI_FILE_HANDLE            FileHandle,\r
   OUT UINT64                    *Position\r
-  )\r
-{\r
+  ) {\r
   return (FileFunctionMap.GetFilePosition(FileHandle, Position));\r
 }\r
 /**\r
@@ -844,8 +868,7 @@ EFI_STATUS
 EFIAPI\r
 ShellFlushFile (\r
   IN EFI_FILE_HANDLE            FileHandle\r
-  )\r
-{\r
+  ) {\r
   return (FileFunctionMap.FlushFile(FileHandle));\r
 }\r
 \r
@@ -872,8 +895,7 @@ EFIAPI
 ShellFindFirstFile (\r
   IN EFI_FILE_HANDLE            DirHandle,\r
   OUT EFI_FILE_INFO             **Buffer\r
-  )\r
-{\r
+  ) {\r
   //\r
   // pass to file handle lib\r
   //\r
@@ -904,8 +926,7 @@ ShellFindNextFile(
   IN EFI_FILE_HANDLE             DirHandle,\r
   OUT EFI_FILE_INFO              *Buffer,\r
   OUT BOOLEAN                    *NoFile\r
-  )\r
-{\r
+  ) {\r
   //\r
   // pass to file handle lib\r
   //\r
@@ -931,8 +952,7 @@ EFIAPI
 ShellGetFileSize (\r
   IN EFI_FILE_HANDLE            FileHandle,\r
   OUT UINT64                    *Size\r
-  )\r
-{\r
+  ) {\r
   return (FileFunctionMap.GetFileSize(FileHandle, Size));\r
 }\r
 /**\r
@@ -1220,7 +1240,7 @@ typedef struct {
   the ShellCloseFileMetaArg function.\r
 \r
   @param[in] FileList           the EFI shell list type\r
-  @param[in][out] ListHead      the list to add to\r
+  @param[in,out] ListHead      the list to add to\r
 \r
   @retval the resultant head of the double linked new format list;\r
 **/\r
@@ -1229,7 +1249,8 @@ EFIAPI
 InternalShellConvertFileListType (\r
   IN LIST_ENTRY                 *FileList,\r
   IN OUT LIST_ENTRY             *ListHead\r
-  ){\r
+  )\r
+{\r
   SHELL_FILE_ARG                *OldInfo;\r
   LIST_ENTRY                    *Link;\r
   EFI_SHELL_FILE_INFO_NO_CONST  *NewInfo;\r
@@ -1306,7 +1327,8 @@ InternalShellConvertFileListType (
   and will process '?' and '*' as such.  the list must be freed with a call to \r
   ShellCloseFileMetaArg().\r
 \r
-  If you are NOT appending to an existing list *ListHead must be NULL.\r
+  If you are NOT appending to an existing list *ListHead must be NULL.  If \r
+  *ListHead is NULL then it must be callee freed.\r
 \r
   @param Arg                    pointer to path string\r
   @param OpenMode               mode to open files with\r
@@ -1341,9 +1363,22 @@ ShellOpenFileMetaArg (
   // Check for UEFI Shell 2.0 protocols\r
   //\r
   if (mEfiShellProtocol != NULL) {\r
-    return (mEfiShellProtocol->OpenFileList(Arg, \r
+    if (*ListHead == NULL) {\r
+      *ListHead = (EFI_SHELL_FILE_INFO*)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));\r
+      if (*ListHead == NULL) {\r
+        return (EFI_OUT_OF_RESOURCES);\r
+      }\r
+      InitializeListHead(&((*ListHead)->Link));\r
+    }    \r
+    Status = mEfiShellProtocol->OpenFileList(Arg, \r
                                            OpenMode, \r
-                                           ListHead));\r
+                                           ListHead);\r
+    if (EFI_ERROR(Status)) {\r
+      mEfiShellProtocol->RemoveDupInFileList(ListHead);\r
+    } else {\r
+      Status = mEfiShellProtocol->RemoveDupInFileList(ListHead);\r
+    }\r
+    return (Status);\r
   } \r
 \r
   //\r
@@ -1430,6 +1465,135 @@ ShellCloseFileMetaArg (
   }\r
 }\r
 \r
+/**\r
+  Find a file by searching the CWD and then the path.\r
+\r
+  If FileName is NULL then ASSERT.\r
+\r
+  If the return value is not NULL then the memory must be caller freed.\r
+\r
+  @param FileName               Filename string.\r
+\r
+  @retval NULL                  the file was not found\r
+  @return !NULL                 the full path to the file.\r
+**/\r
+CHAR16 *\r
+EFIAPI\r
+ShellFindFilePath (\r
+  IN CONST CHAR16 *FileName\r
+  )\r
+{\r
+  CONST CHAR16      *Path;\r
+  EFI_FILE_HANDLE   Handle;\r
+  EFI_STATUS        Status;\r
+  CHAR16            *RetVal;\r
+  CHAR16            *TestPath;\r
+  CONST CHAR16      *Walker;\r
+  UINTN             Size;\r
+\r
+  RetVal = NULL;\r
+\r
+  Path = ShellGetEnvironmentVariable(L"cwd");\r
+  if (Path != NULL) {\r
+    Size = StrSize(Path);\r
+    Size += StrSize(FileName);\r
+    TestPath = AllocateZeroPool(Size);\r
+    StrCpy(TestPath, Path);\r
+    StrCat(TestPath, FileName);\r
+    Status = ShellOpenFileByName(TestPath, &Handle, EFI_FILE_MODE_READ, 0);\r
+    if (!EFI_ERROR(Status)){\r
+      RetVal = StrnCatGrow(&RetVal, NULL, TestPath, 0);\r
+      ShellCloseFile(&Handle);\r
+      FreePool(TestPath);\r
+      return (RetVal);\r
+    }\r
+    FreePool(TestPath);\r
+  }\r
+  Path = ShellGetEnvironmentVariable(L"path");\r
+  if (Path != NULL) {\r
+    Size = StrSize(Path);\r
+    Size += StrSize(FileName);\r
+    TestPath = AllocateZeroPool(Size);\r
+    Walker = (CHAR16*)Path; \r
+    do {\r
+      CopyMem(TestPath, Walker, StrSize(Walker));\r
+      if (StrStr(TestPath, L";") != NULL) {\r
+        *(StrStr(TestPath, L";")) = CHAR_NULL;\r
+      }\r
+      StrCat(TestPath, FileName);\r
+      if (StrStr(Walker, L";") != NULL) {\r
+        Walker = StrStr(Walker, L";") + 1;\r
+      } else {\r
+        Walker = NULL;\r
+      }\r
+      Status = ShellOpenFileByName(TestPath, &Handle, EFI_FILE_MODE_READ, 0);\r
+      if (!EFI_ERROR(Status)){\r
+        RetVal = StrnCatGrow(&RetVal, NULL, TestPath, 0);\r
+        ShellCloseFile(&Handle);\r
+        break;\r
+      }\r
+    } while (Walker != NULL && Walker[0] != CHAR_NULL);\r
+    FreePool(TestPath);\r
+  }\r
+  return (RetVal);\r
+}\r
+\r
+/**\r
+  Find a file by searching the CWD and then the path with a variable set of file \r
+  extensions.  If the file is not found it will append each extension in the list \r
+  in the order provided and return the first one that is successful.\r
+\r
+  If FileName is NULL, then ASSERT.\r
+  If FileExtension is NULL, then behavior is identical to ShellFindFilePath.\r
+\r
+  If the return value is not NULL then the memory must be caller freed.\r
+\r
+  @param[in] FileName           Filename string.\r
+  @param[in] FileExtension      Semi-colon delimeted list of possible extensions.\r
+\r
+  @retval NULL                  The file was not found.\r
+  @retval !NULL                 The path to the file.\r
+**/\r
+CHAR16 *\r
+EFIAPI\r
+ShellFindFilePathEx (\r
+  IN CONST CHAR16 *FileName,\r
+  IN CONST CHAR16 *FileExtension\r
+  )\r
+{\r
+  CHAR16            *TestPath;\r
+  CHAR16            *RetVal;\r
+  CONST CHAR16      *ExtensionWalker;\r
+  ASSERT(FileName != NULL);\r
+  if (FileExtension == NULL) {\r
+    return (ShellFindFilePath(FileName));\r
+  }\r
+  RetVal = ShellFindFilePath(FileName);\r
+  if (RetVal != NULL) {\r
+    return (RetVal);\r
+  }\r
+  TestPath = AllocateZeroPool(StrSize(FileName) + StrSize(FileExtension));\r
+  for (ExtensionWalker = FileExtension ;  ; ExtensionWalker = StrStr(ExtensionWalker, L";") + 1 ){\r
+    StrCpy(TestPath, FileName);\r
+    StrCat(TestPath, ExtensionWalker);\r
+    if (StrStr(TestPath, L";") != NULL) {\r
+      *(StrStr(TestPath, L";")) = CHAR_NULL;\r
+    }\r
+    RetVal = ShellFindFilePath(TestPath);\r
+    if (RetVal != NULL) {\r
+      break;\r
+    }\r
+    //\r
+    // Must be after first loop...\r
+    //\r
+    if (StrStr(ExtensionWalker, L";") == NULL) {\r
+      break;\r
+    }\r
+  }\r
+  FreePool(TestPath);\r
+  return (RetVal);\r
+}\r
+\r
 typedef struct {\r
   LIST_ENTRY     Link;\r
   CHAR16         *Name;\r
@@ -1459,8 +1623,7 @@ InternalIsOnCheckList (
   IN CONST CHAR16               *Name,\r
   IN CONST SHELL_PARAM_ITEM     *CheckList,\r
   OUT ParamType                 *Type\r
-  )\r
-{\r
+  ) {\r
   SHELL_PARAM_ITEM              *TempListItem;\r
 \r
   //\r
@@ -1484,13 +1647,18 @@ InternalIsOnCheckList (
   //\r
   for (TempListItem = (SHELL_PARAM_ITEM*)CheckList ; TempListItem->Name != NULL ; TempListItem++) {\r
     //\r
-    // If the Name matches set the type and return TRUE\r
+    // If the Type is TypeStart only check the first characters of the passed in param\r
+    // If it matches set the type and return TRUE\r
     //\r
-    if (StrCmp(Name, TempListItem->Name) == 0) {\r
+    if (TempListItem->Type == TypeStart && StrnCmp(Name, TempListItem->Name, StrLen(TempListItem->Name)) == 0) {\r
+      *Type = TempListItem->Type;\r
+      return (TRUE);\r
+    } else if (StrCmp(Name, TempListItem->Name) == 0) {\r
       *Type = TempListItem->Type;\r
       return (TRUE);\r
     }\r
   }\r
+\r
   return (FALSE);\r
 }\r
 /**\r
@@ -1504,7 +1672,8 @@ InternalIsOnCheckList (
 BOOLEAN\r
 EFIAPI\r
 InternalIsFlag (\r
-  IN CONST CHAR16               *Name\r
+  IN CONST CHAR16               *Name,\r
+  IN BOOLEAN                    AlwaysAllowNumbers\r
   )\r
 {\r
   //\r
@@ -1512,6 +1681,13 @@ InternalIsFlag (
   //\r
   ASSERT(Name != NULL);\r
 \r
+  //\r
+  // If we accept numbers then dont return TRUE. (they will be values)\r
+  //\r
+  if (((Name[0] == L'-' || Name[0] == L'+') && ShellLibIsHexaDecimalDigitCharacter(Name[1])) && AlwaysAllowNumbers != FALSE) {\r
+    return (FALSE);\r
+  }\r
+\r
   //\r
   // If the Name has a / or - as the first character return TRUE\r
   //\r
@@ -1548,6 +1724,7 @@ InternalIsFlag (
                                 the invalid command line argument was returned in\r
                                 ProblemParam if provided.\r
 **/\r
+STATIC\r
 EFI_STATUS\r
 EFIAPI\r
 InternalCommandLineParse (\r
@@ -1556,25 +1733,19 @@ InternalCommandLineParse (
   OUT CHAR16                    **ProblemParam OPTIONAL,\r
   IN BOOLEAN                    AutoPageBreak,\r
   IN CONST CHAR16               **Argv,\r
-  IN UINTN                      Argc\r
-  )\r
-{\r
+  IN UINTN                      Argc,\r
+  IN BOOLEAN                    AlwaysAllowNumbers\r
+  ) {\r
   UINTN                         LoopCounter;\r
-  UINTN                         Count;\r
   ParamType                     CurrentItemType;\r
   SHELL_PARAM_PACKAGE           *CurrentItemPackage;\r
-  BOOLEAN                       GetItemValue;\r
+  UINTN                         GetItemValue;\r
+  UINTN                         ValueSize;\r
 \r
   CurrentItemPackage = NULL;\r
-\r
-  //\r
-  // ASSERTs\r
-  //\r
-  ASSERT(CheckList  != NULL);\r
-  ASSERT(Argv       != NULL);\r
-\r
-  Count = 0;\r
-  GetItemValue = FALSE;\r
+  mTotalParameterCount = 0;\r
+  GetItemValue = 0;\r
+  ValueSize = 0;\r
 \r
   //\r
   // If there is only 1 item we dont need to do anything\r
@@ -1584,6 +1755,12 @@ InternalCommandLineParse (
     return (EFI_SUCCESS);\r
   }\r
 \r
+  //\r
+  // ASSERTs\r
+  //\r
+  ASSERT(CheckList  != NULL);\r
+  ASSERT(Argv       != NULL);\r
+\r
   //\r
   // initialize the linked list\r
   //\r
@@ -1598,7 +1775,14 @@ InternalCommandLineParse (
       //\r
       // do nothing for NULL argv\r
       //\r
-    } else if (InternalIsOnCheckList(Argv[LoopCounter], CheckList, &CurrentItemType) == TRUE) {\r
+    } else if (InternalIsOnCheckList(Argv[LoopCounter], CheckList, &CurrentItemType) != FALSE) {\r
+      //\r
+      // We might have leftover if last parameter didnt have optional value\r
+      //\r
+      if (GetItemValue != 0) {\r
+        GetItemValue = 0;\r
+        InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);\r
+      }\r
       //\r
       // this is a flag\r
       //\r
@@ -1614,28 +1798,49 @@ InternalCommandLineParse (
       //\r
       // Does this flag require a value\r
       //\r
-      if (CurrentItemPackage->Type == TypeValue) {\r
-        //\r
-        // trigger the next loop to populate the value of this item\r
-        //\r
-        GetItemValue = TRUE; \r
-      } else {\r
+      switch (CurrentItemPackage->Type) {\r
         //\r
-        // this item has no value expected; we are done\r
-        //\r
-        InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);\r
+        // possibly trigger the next loop(s) to populate the value of this item\r
+        //        \r
+        case TypeValue:\r
+          GetItemValue = 1; \r
+          ValueSize = 0;\r
+          break;\r
+        case TypeDoubleValue:\r
+          GetItemValue = 2;\r
+          ValueSize = 0;\r
+          break;\r
+        case TypeMaxValue:\r
+          GetItemValue = (UINTN)(-1);\r
+          ValueSize = 0;\r
+          break;\r
+        default:\r
+          //\r
+          // this item has no value expected; we are done\r
+          //\r
+          InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);\r
+          ASSERT(GetItemValue == 0);\r
+          break;\r
       }\r
-    } else if (GetItemValue == TRUE && InternalIsFlag(Argv[LoopCounter]) == FALSE) {\r
+    } else if (GetItemValue != 0 && InternalIsFlag(Argv[LoopCounter], AlwaysAllowNumbers) == FALSE) {\r
       ASSERT(CurrentItemPackage != NULL);\r
       //\r
-      // get the item VALUE for the previous flag\r
+      // get the item VALUE for a previous flag\r
       //\r
-      GetItemValue = FALSE;\r
-      CurrentItemPackage->Value = AllocateZeroPool(StrSize(Argv[LoopCounter]));\r
+      CurrentItemPackage->Value = ReallocatePool(ValueSize, ValueSize + StrSize(Argv[LoopCounter]) + sizeof(CHAR16), CurrentItemPackage->Value);\r
       ASSERT(CurrentItemPackage->Value != NULL);\r
-      StrCpy(CurrentItemPackage->Value, Argv[LoopCounter]);\r
-      InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);\r
-    } else if (InternalIsFlag(Argv[LoopCounter]) == FALSE) {\r
+      if (ValueSize == 0) {\r
+        StrCpy(CurrentItemPackage->Value, Argv[LoopCounter]);\r
+      } else {\r
+        StrCat(CurrentItemPackage->Value, L" ");\r
+        StrCat(CurrentItemPackage->Value, Argv[LoopCounter]);\r
+      }\r
+      ValueSize += StrSize(Argv[LoopCounter]) + sizeof(CHAR16);\r
+      GetItemValue--;\r
+      if (GetItemValue == 0) {\r
+        InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);\r
+      }\r
+    } else if (InternalIsFlag(Argv[LoopCounter], AlwaysAllowNumbers) == FALSE) {\r
       //\r
       // add this one as a non-flag\r
       //\r
@@ -1646,7 +1851,7 @@ InternalCommandLineParse (
       CurrentItemPackage->Value = AllocatePool(StrSize(Argv[LoopCounter]));\r
       ASSERT(CurrentItemPackage->Value != NULL);\r
       StrCpy(CurrentItemPackage->Value, Argv[LoopCounter]);\r
-      CurrentItemPackage->OriginalPosition = Count++;\r
+      CurrentItemPackage->OriginalPosition = mTotalParameterCount++;\r
       InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);\r
     } else if (ProblemParam) {\r
       //\r
@@ -1664,6 +1869,10 @@ InternalCommandLineParse (
       return (EFI_VOLUME_CORRUPTED);\r
     }\r
   }\r
+  if (GetItemValue != 0) {\r
+    GetItemValue = 0;\r
+    InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);\r
+  }\r
   //\r
   // support for AutoPageBreak\r
   //\r
@@ -1700,13 +1909,13 @@ InternalCommandLineParse (
 **/\r
 EFI_STATUS\r
 EFIAPI\r
-ShellCommandLineParse (\r
+ShellCommandLineParseEx (\r
   IN CONST SHELL_PARAM_ITEM     *CheckList,\r
   OUT LIST_ENTRY                **CheckPackage,\r
   OUT CHAR16                    **ProblemParam OPTIONAL,\r
-  IN BOOLEAN                    AutoPageBreak\r
-  )\r
-{\r
+  IN BOOLEAN                    AutoPageBreak,\r
+  IN BOOLEAN                    AlwaysAllowNumbers\r
+  ) {\r
   // \r
   // ASSERT that CheckList and CheckPackage aren't NULL\r
   //\r
@@ -1722,7 +1931,8 @@ ShellCommandLineParse (
                                      ProblemParam, \r
                                      AutoPageBreak, \r
                                      (CONST CHAR16**) mEfiShellParametersProtocol->Argv,\r
-                                     mEfiShellParametersProtocol->Argc ));\r
+                                     mEfiShellParametersProtocol->Argc,\r
+                                     AlwaysAllowNumbers));\r
   }\r
 \r
   // \r
@@ -1734,7 +1944,8 @@ ShellCommandLineParse (
                                    ProblemParam, \r
                                    AutoPageBreak, \r
                                    (CONST CHAR16**) mEfiShellInterface->Argv,\r
-                                   mEfiShellInterface->Argc ));\r
+                                   mEfiShellInterface->Argc,\r
+                                   AlwaysAllowNumbers));\r
 }\r
 \r
 /**\r
@@ -1753,8 +1964,7 @@ VOID
 EFIAPI\r
 ShellCommandLineFreeVarList (\r
   IN LIST_ENTRY                 *CheckPackage\r
-  )\r
-{\r
+  ) {\r
   LIST_ENTRY                    *Node;\r
 \r
   //\r
@@ -1767,7 +1977,10 @@ ShellCommandLineFreeVarList (
   //\r
   // for each node in the list\r
   //\r
-  for (Node = GetFirstNode(CheckPackage); Node != CheckPackage ; Node = GetFirstNode(CheckPackage)) {\r
+  for ( Node = GetFirstNode(CheckPackage)\r
+      ; IsListEmpty(CheckPackage) == FALSE\r
+      ; Node = GetFirstNode(CheckPackage)\r
+      ){\r
     //\r
     // Remove it from the list\r
     //\r
@@ -1816,8 +2029,7 @@ EFIAPI
 ShellCommandLineGetFlag (\r
   IN CONST LIST_ENTRY           *CheckPackage,\r
   IN CHAR16                     *KeyString\r
-  )\r
-{\r
+  ) {\r
   LIST_ENTRY                    *Node;\r
 \r
   //\r
@@ -1835,12 +2047,22 @@ ShellCommandLineGetFlag (
   //\r
   // enumerate through the list of parametrs\r
   //\r
-  for (Node = GetFirstNode(CheckPackage) ; !IsNull (CheckPackage, Node) ; Node = GetNextNode(CheckPackage, Node) ) {\r
+  for ( Node = GetFirstNode(CheckPackage) \r
+      ; !IsNull (CheckPackage, Node) \r
+      ; Node = GetNextNode(CheckPackage, Node) \r
+      ){\r
     //\r
     // If the Name matches, return TRUE (and there may be NULL name)\r
     //\r
     if (((SHELL_PARAM_PACKAGE*)Node)->Name != NULL) {\r
-      if (StrCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) {\r
+      //\r
+      // If Type is TypeStart then only compare the begining of the strings\r
+      //\r
+      if ( ((SHELL_PARAM_PACKAGE*)Node)->Type == TypeStart \r
+        && StrnCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name, StrLen(KeyString)) == 0\r
+        ){\r
+        return (TRUE);\r
+      } else if (StrCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) {\r
         return (TRUE);\r
       }\r
     }\r
@@ -1865,8 +2087,7 @@ EFIAPI
 ShellCommandLineGetValue (\r
   IN CONST LIST_ENTRY           *CheckPackage,\r
   IN CHAR16                     *KeyString\r
-  )\r
-{\r
+  ) {\r
   LIST_ENTRY                    *Node;\r
 \r
   //\r
@@ -1879,12 +2100,28 @@ ShellCommandLineGetValue (
   //\r
   // enumerate through the list of parametrs\r
   //\r
-  for (Node = GetFirstNode(CheckPackage) ; !IsNull (CheckPackage, Node) ; Node = GetNextNode(CheckPackage, Node) ) {\r
+  for ( Node = GetFirstNode(CheckPackage) \r
+      ; !IsNull (CheckPackage, Node) \r
+      ; Node = GetNextNode(CheckPackage, Node) \r
+      ){\r
     //\r
     // If the Name matches, return the value (name can be NULL)\r
     //\r
     if (((SHELL_PARAM_PACKAGE*)Node)->Name != NULL) {\r
-      if (StrCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) {\r
+      //\r
+      // If Type is TypeStart then only compare the begining of the strings\r
+      //\r
+      if ( ((SHELL_PARAM_PACKAGE*)Node)->Type == TypeStart \r
+        && StrnCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name, StrLen(KeyString)) == 0\r
+        ){\r
+        //\r
+        // return the string part after the flag\r
+        //\r
+        return (((SHELL_PARAM_PACKAGE*)Node)->Name + StrLen(KeyString));\r
+      } else if (StrCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) {\r
+        //\r
+        // return the value\r
+        //\r
         return (((SHELL_PARAM_PACKAGE*)Node)->Value);\r
       }\r
     }\r
@@ -1909,8 +2146,7 @@ EFIAPI
 ShellCommandLineGetRawValue (\r
   IN CONST LIST_ENTRY           *CheckPackage,\r
   IN UINT32                     Position\r
-  )\r
-{\r
+  ) {\r
   LIST_ENTRY                    *Node;\r
 \r
   //\r
@@ -1923,7 +2159,10 @@ ShellCommandLineGetRawValue (
   //\r
   // enumerate through the list of parametrs\r
   //\r
-  for (Node = GetFirstNode(CheckPackage) ; !IsNull (CheckPackage, Node) ; Node = GetNextNode(CheckPackage, Node) ) {\r
+  for ( Node = GetFirstNode(CheckPackage) \r
+      ; !IsNull (CheckPackage, Node) \r
+      ; Node = GetNextNode(CheckPackage, Node) \r
+      ){\r
     //\r
     // If the position matches, return the value\r
     //\r
@@ -1933,14 +2172,80 @@ ShellCommandLineGetRawValue (
   }\r
   return (NULL);\r
 }\r
+\r
+/**\r
+  returns the number of command line value parameters that were parsed.  \r
+  \r
+  this will not include flags.\r
+\r
+  @retval (UINTN)-1     No parsing has ocurred\r
+  @return other         The number of value parameters found\r
+**/\r
+UINTN\r
+EFIAPI\r
+ShellCommandLineGetCount(\r
+  VOID\r
+  )\r
+{\r
+  return (mTotalParameterCount);\r
+}\r
+\r
+/**\r
+  Determins if a parameter is duplicated.\r
+\r
+  If Param is not NULL then it will point to a callee allocated string buffer \r
+  with the parameter value if a duplicate is found.\r
+\r
+  If CheckPackage is NULL, then ASSERT.\r
+\r
+  @param[in] CheckPackage       The package of parsed command line arguments.\r
+  @param[out] Param             Upon finding one, a pointer to the duplicated parameter.\r
+\r
+  @retval EFI_SUCCESS           No parameters were duplicated.\r
+  @retval EFI_DEVICE_ERROR      A duplicate was found.\r
+  **/\r
+EFI_STATUS\r
+EFIAPI\r
+ShellCommandLineCheckDuplicate (\r
+  IN CONST LIST_ENTRY              *CheckPackage,\r
+  OUT CHAR16                       **Param\r
+  )\r
+{\r
+  LIST_ENTRY                    *Node1;\r
+  LIST_ENTRY                    *Node2;\r
+  \r
+  ASSERT(CheckPackage != NULL);\r
+\r
+  for ( Node1 = GetFirstNode(CheckPackage) \r
+      ; !IsNull (CheckPackage, Node1) \r
+      ; Node1 = GetNextNode(CheckPackage, Node1) \r
+      ){\r
+    for ( Node2 = GetNextNode(CheckPackage, Node1) \r
+        ; !IsNull (CheckPackage, Node2) \r
+        ; Node2 = GetNextNode(CheckPackage, Node2) \r
+        ){\r
+      if (StrCmp(((SHELL_PARAM_PACKAGE*)Node1)->Name, ((SHELL_PARAM_PACKAGE*)Node2)->Name) == 0) {\r
+        if (Param != NULL) {\r
+          *Param = NULL;\r
+          *Param = StrnCatGrow(Param, NULL, ((SHELL_PARAM_PACKAGE*)Node1)->Name, 0);\r
+        }\r
+        return (EFI_DEVICE_ERROR);\r
+      }\r
+    }\r
+  }\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
 /**\r
-  This is a find and replace function.  it will return the NewString as a copy of \r
+  This is a find and replace function.  Upon successful return the NewString is a copy of \r
   SourceString with each instance of FindTarget replaced with ReplaceWith.\r
 \r
+  If SourceString and NewString overlap the behavior is undefined.\r
+\r
   If the string would grow bigger than NewSize it will halt and return error.\r
 \r
   @param[in] SourceString             String with source buffer\r
-  @param[in][out] NewString           String with resultant buffer\r
+  @param[in,out] NewString           String with resultant buffer\r
   @param[in] NewSize                  Size in bytes of NewString\r
   @param[in] FindTarget               String to look for\r
   @param[in[ ReplaceWith              String to replace FindTarget with\r
@@ -1958,13 +2263,15 @@ ShellCommandLineGetRawValue (
 \r
 EFI_STATUS\r
 EFIAPI\r
-CopyReplace(\r
+ShellLibCopySearchAndReplace(\r
   IN CHAR16 CONST                     *SourceString,\r
   IN CHAR16                           *NewString,\r
   IN UINTN                            NewSize,\r
   IN CONST CHAR16                     *FindTarget,\r
   IN CONST CHAR16                     *ReplaceWith\r
-  ){\r
+  ) \r
+{\r
+  UINTN Size;\r
   if ( (SourceString == NULL)\r
     || (NewString    == NULL)\r
     || (FindTarget   == NULL)\r
@@ -1974,16 +2281,18 @@ CopyReplace(
     ){\r
     return (EFI_INVALID_PARAMETER);\r
   }\r
-  NewString = SetMem16(NewString, NewSize, L'\0');\r
-  while (*SourceString != L'\0') {\r
+  NewString = SetMem16(NewString, NewSize, CHAR_NULL);\r
+  while (*SourceString != CHAR_NULL) {\r
     if (StrnCmp(SourceString, FindTarget, StrLen(FindTarget)) == 0) {\r
       SourceString += StrLen(FindTarget);\r
-      if (StrSize(NewString) + (StrLen(ReplaceWith)*sizeof(CHAR16)) > NewSize) {\r
+      Size = StrSize(NewString);\r
+      if ((Size + (StrLen(ReplaceWith)*sizeof(CHAR16))) > NewSize) {\r
         return (EFI_BUFFER_TOO_SMALL);\r
       }\r
       StrCat(NewString, ReplaceWith);\r
     } else {\r
-      if (StrSize(NewString) + sizeof(CHAR16) > NewSize) {\r
+      Size = StrSize(NewString);\r
+      if (Size + sizeof(CHAR16) > NewSize) {\r
         return (EFI_BUFFER_TOO_SMALL);\r
       }\r
       StrnCat(NewString, SourceString, 1);\r
@@ -1993,6 +2302,38 @@ CopyReplace(
   return (EFI_SUCCESS);\r
 }\r
 \r
+/**\r
+  Internal worker function to output a string.\r
+\r
+  This function will output a string to the correct StdOut.\r
+\r
+  @param[in] String       The string to print out.\r
+\r
+  @retval EFI_SUCCESS     The operation was sucessful.\r
+  @retval !EFI_SUCCESS    The operation failed.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InternalPrintTo (\r
+  IN CONST CHAR16 *String\r
+  )\r
+{\r
+  UINTN Size;\r
+  Size = StrSize(String) - sizeof(CHAR16);\r
+  if (mEfiShellParametersProtocol != NULL) {\r
+    return (mEfiShellParametersProtocol->StdOut->Write(mEfiShellParametersProtocol->StdOut, &Size, (VOID*)String));\r
+  }\r
+  if (mEfiShellInterface          != NULL) {\r
+    //\r
+    // Divide in half for old shell.  Must be string length not size.\r
+    //\r
+    Size /= 2;\r
+    return (         mEfiShellInterface->StdOut->Write(mEfiShellInterface->StdOut,          &Size, (VOID*)String));\r
+  }\r
+  ASSERT(FALSE);\r
+  return (EFI_UNSUPPORTED);\r
+}\r
+\r
 /**\r
   Print at a specific location on the screen.\r
 \r
@@ -2017,57 +2358,44 @@ CopyReplace(
   @param[in] Row        the row to print at\r
   @param[in] Col        the column to print at\r
   @param[in] Format     the format string\r
+  @param[in] Marker     the marker for the variable argument list\r
 \r
   @return the number of characters printed to the screen\r
 **/\r
 \r
 UINTN\r
 EFIAPI\r
-ShellPrintEx(\r
+InternalShellPrintWorker(\r
   IN INT32                Col OPTIONAL,\r
   IN INT32                Row OPTIONAL,\r
   IN CONST CHAR16         *Format,\r
-  ...\r
-  ){\r
-  VA_LIST           Marker;\r
-  UINTN             BufferSize;\r
-  CHAR16            *PostReplaceFormat;\r
-  CHAR16            *PostReplaceFormat2;\r
+  VA_LIST                 Marker\r
+  ) \r
+{\r
   UINTN             Return;\r
-\r
   EFI_STATUS        Status;\r
   UINTN             NormalAttribute;\r
   CHAR16            *ResumeLocation;\r
   CHAR16            *FormatWalker;\r
-\r
-  VA_START (Marker, Format);\r
   \r
-  BufferSize = (PcdGet32 (PcdUefiLibMaxPrintBufferSize) + 1) * sizeof (CHAR16);\r
-  PostReplaceFormat = AllocateZeroPool (BufferSize);\r
-  ASSERT (PostReplaceFormat != NULL);\r
-  PostReplaceFormat2 = AllocateZeroPool (BufferSize);\r
-  ASSERT (PostReplaceFormat2 != NULL);\r
-\r
   //\r
   // Back and forth each time fixing up 1 of our flags...\r
   //\r
-  Status = CopyReplace(Format,             PostReplaceFormat,  BufferSize, L"%N", L"%%N");\r
+  Status = ShellLibCopySearchAndReplace(Format,             mPostReplaceFormat,  PcdGet16 (PcdShellPrintBufferSize), L"%N", L"%%N");\r
   ASSERT_EFI_ERROR(Status);\r
-  Status = CopyReplace(PostReplaceFormat,  PostReplaceFormat2, BufferSize, L"%E", L"%%E");\r
+  Status = ShellLibCopySearchAndReplace(mPostReplaceFormat,  mPostReplaceFormat2, PcdGet16 (PcdShellPrintBufferSize), L"%E", L"%%E");\r
   ASSERT_EFI_ERROR(Status);\r
-  Status = CopyReplace(PostReplaceFormat2, PostReplaceFormat,  BufferSize, L"%H", L"%%H");\r
+  Status = ShellLibCopySearchAndReplace(mPostReplaceFormat2, mPostReplaceFormat,  PcdGet16 (PcdShellPrintBufferSize), L"%H", L"%%H");\r
   ASSERT_EFI_ERROR(Status);\r
-  Status = CopyReplace(PostReplaceFormat,  PostReplaceFormat2, BufferSize, L"%B", L"%%B");\r
+  Status = ShellLibCopySearchAndReplace(mPostReplaceFormat,  mPostReplaceFormat2, PcdGet16 (PcdShellPrintBufferSize), L"%B", L"%%B");\r
   ASSERT_EFI_ERROR(Status);\r
-  Status = CopyReplace(PostReplaceFormat2, PostReplaceFormat,  BufferSize, L"%V", L"%%V");\r
+  Status = ShellLibCopySearchAndReplace(mPostReplaceFormat2, mPostReplaceFormat,  PcdGet16 (PcdShellPrintBufferSize), L"%V", L"%%V");\r
   ASSERT_EFI_ERROR(Status);\r
 \r
   //\r
   // Use the last buffer from replacing to print from...\r
   //\r
-  Return = UnicodeVSPrint (PostReplaceFormat2, BufferSize, PostReplaceFormat, Marker);\r
-\r
-  FreePool(PostReplaceFormat);\r
+  Return = UnicodeVSPrint (mPostReplaceFormat2, PcdGet16 (PcdShellPrintBufferSize), mPostReplaceFormat, Marker);\r
 \r
   if (Col != -1 && Row != -1) {\r
     Status = gST->ConOut->SetCursorPosition(gST->ConOut, Col, Row);\r
@@ -2075,19 +2403,19 @@ ShellPrintEx(
   }\r
 \r
   NormalAttribute = gST->ConOut->Mode->Attribute;\r
-  FormatWalker = PostReplaceFormat2;\r
-  while (*FormatWalker != L'\0') {\r
+  FormatWalker = mPostReplaceFormat2;\r
+  while (*FormatWalker != CHAR_NULL) {\r
     //\r
     // Find the next attribute change request\r
     //\r
     ResumeLocation = StrStr(FormatWalker, L"%");\r
     if (ResumeLocation != NULL) {\r
-      *ResumeLocation = L'\0';\r
+      *ResumeLocation = CHAR_NULL;\r
     }\r
     //\r
     // print the current FormatWalker string\r
     //\r
-    Status = gST->ConOut->OutputString(gST->ConOut, FormatWalker);\r
+    Status = InternalPrintTo(FormatWalker);\r
     ASSERT_EFI_ERROR(Status);\r
     //\r
     // update the attribute\r
@@ -2110,7 +2438,12 @@ ShellPrintEx(
           gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_GREEN, ((NormalAttribute&(BIT4|BIT5|BIT6))>>4)));\r
           break;\r
         default:\r
-          ASSERT(FALSE);\r
+          //\r
+          // Print a simple '%' symbol\r
+          //\r
+          Status = InternalPrintTo(L"%");\r
+          ASSERT_EFI_ERROR(Status);\r
+          ResumeLocation = ResumeLocation - 1;\r
           break;\r
       }\r
     } else {\r
@@ -2127,7 +2460,331 @@ ShellPrintEx(
     FormatWalker = ResumeLocation + 2;\r
   }\r
 \r
-  FreePool(PostReplaceFormat2);\r
-\r
   return (Return);\r
-}
\ No newline at end of file
+}\r
+\r
+/**\r
+  Print at a specific location on the screen.\r
+\r
+  This function will move the cursor to a given screen location and print the specified string.\r
+  \r
+  If -1 is specified for either the Row or Col the current screen location for BOTH \r
+  will be used.\r
+\r
+  If either Row or Col is out of range for the current console, then ASSERT.\r
+  If Format is NULL, then ASSERT.\r
+\r
+  In addition to the standard %-based flags as supported by UefiLib Print() this supports \r
+  the following additional flags:\r
+    %N       -   Set output attribute to normal\r
+    %H       -   Set output attribute to highlight\r
+    %E       -   Set output attribute to error\r
+    %B       -   Set output attribute to blue color\r
+    %V       -   Set output attribute to green color\r
+\r
+  Note: The background color is controlled by the shell command cls.\r
+\r
+  @param[in] Row        the row to print at\r
+  @param[in] Col        the column to print at\r
+  @param[in] Format     the format string\r
+\r
+  @return the number of characters printed to the screen\r
+**/\r
+\r
+UINTN\r
+EFIAPI\r
+ShellPrintEx(\r
+  IN INT32                Col OPTIONAL,\r
+  IN INT32                Row OPTIONAL,\r
+  IN CONST CHAR16         *Format,\r
+  ...\r
+  ) \r
+{\r
+  VA_LIST           Marker;\r
+  EFI_STATUS        Status;\r
+  VA_START (Marker, Format);\r
+  Status = InternalShellPrintWorker(Col, Row, Format, Marker);\r
+  VA_END(Marker);\r
+  return(Status);\r
+}\r
+\r
+/**\r
+  Print at a specific location on the screen.\r
+\r
+  This function will move the cursor to a given screen location and print the specified string.\r
+  \r
+  If -1 is specified for either the Row or Col the current screen location for BOTH \r
+  will be used.\r
+\r
+  If either Row or Col is out of range for the current console, then ASSERT.\r
+  If Format is NULL, then ASSERT.\r
+\r
+  In addition to the standard %-based flags as supported by UefiLib Print() this supports \r
+  the following additional flags:\r
+    %N       -   Set output attribute to normal\r
+    %H       -   Set output attribute to highlight\r
+    %E       -   Set output attribute to error\r
+    %B       -   Set output attribute to blue color\r
+    %V       -   Set output attribute to green color\r
+\r
+  Note: The background color is controlled by the shell command cls.\r
+\r
+  @param[in] Row                the row to print at\r
+  @param[in] Col                the column to print at\r
+  @param[in] HiiFormatStringId  the format string Id for getting from Hii\r
+  @param[in] HiiFormatHandle    the format string Handle for getting from Hii\r
+\r
+  @return the number of characters printed to the screen\r
+**/\r
+UINTN\r
+EFIAPI\r
+ShellPrintHiiEx(\r
+  IN INT32                Col OPTIONAL,\r
+  IN INT32                Row OPTIONAL,\r
+  IN CONST EFI_STRING_ID  HiiFormatStringId,\r
+  IN CONST EFI_HANDLE     HiiFormatHandle,\r
+  ...\r
+  )\r
+{\r
+  VA_LIST           Marker;\r
+  CHAR16            *HiiFormatString;\r
+  UINTN             RetVal;\r
+\r
+  VA_START (Marker, HiiFormatHandle);\r
+  HiiFormatString = HiiGetString(HiiFormatHandle, HiiFormatStringId, NULL);\r
+  ASSERT(HiiFormatString != NULL);\r
+\r
+  RetVal = InternalShellPrintWorker(Col, Row, HiiFormatString, Marker);\r
+\r
+  FreePool(HiiFormatString);\r
+  VA_END(Marker);\r
+\r
+  return (RetVal);\r
+}\r
+\r
+/**\r
+  Function to determine if a given filename represents a file or a directory.\r
+\r
+  @param[in] DirName      Path to directory to test.\r
+\r
+  @retval EFI_SUCCESS     The Path represents a directory\r
+  @retval EFI_NOT_FOUND   The Path does not represent a directory\r
+  @return other           The path failed to open\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ShellIsDirectory(\r
+  IN CONST CHAR16 *DirName\r
+  )\r
+{\r
+  EFI_STATUS        Status;\r
+  EFI_FILE_HANDLE   Handle;\r
+\r
+  ASSERT(DirName != NULL);\r
+\r
+  Handle = NULL;\r
+\r
+  Status = ShellOpenFileByName(DirName, &Handle, EFI_FILE_MODE_READ, 0);\r
+  if (EFI_ERROR(Status)) {\r
+    return (Status);\r
+  }\r
+\r
+  if (FileHandleIsDirectory(Handle) == EFI_SUCCESS) {\r
+    ShellCloseFile(&Handle);\r
+    return (EFI_SUCCESS);\r
+  }\r
+  ShellCloseFile(&Handle);\r
+  return (EFI_NOT_FOUND);\r
+}\r
+\r
+/**\r
+  Function to determine if a given filename represents a file.\r
+\r
+  @param[in] Name         Path to file to test.\r
+\r
+  @retval EFI_SUCCESS     The Path represents a file.\r
+  @retval EFI_NOT_FOUND   The Path does not represent a file.\r
+  @retval other           The path failed to open.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ShellIsFile(\r
+  IN CONST CHAR16 *Name\r
+  )\r
+{\r
+  EFI_STATUS        Status;\r
+  EFI_FILE_HANDLE   Handle;\r
+\r
+  ASSERT(Name != NULL);\r
+\r
+  Handle = NULL;\r
+\r
+  Status = ShellOpenFileByName(Name, &Handle, EFI_FILE_MODE_READ, 0);\r
+  if (EFI_ERROR(Status)) {\r
+    return (Status);\r
+  }\r
+\r
+  if (FileHandleIsDirectory(Handle) != EFI_SUCCESS) {\r
+    ShellCloseFile(&Handle);\r
+    return (EFI_SUCCESS);\r
+  }\r
+  ShellCloseFile(&Handle);\r
+  return (EFI_NOT_FOUND);\r
+}\r
+\r
+/**\r
+  Function to determine if a given filename represents a file.\r
+\r
+  This will search the CWD and then the Path.\r
+\r
+  If Name is NULL, then ASSERT.\r
+\r
+  @param[in] Name         Path to file to test.\r
+\r
+  @retval EFI_SUCCESS     The Path represents a file.\r
+  @retval EFI_NOT_FOUND   The Path does not represent a file.\r
+  @retval other           The path failed to open.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ShellIsFileInPath(\r
+  IN CONST CHAR16 *Name\r
+  ) {\r
+  CHAR16      *NewName;\r
+  EFI_STATUS  Status;\r
+\r
+  if (!EFI_ERROR(ShellIsFile(Name))) {\r
+    return (TRUE);\r
+  }\r
+\r
+  NewName = ShellFindFilePath(Name);\r
+  if (NewName == NULL) {\r
+    return (EFI_NOT_FOUND);\r
+  }\r
+  Status = ShellIsFile(NewName);\r
+  FreePool(NewName);\r
+  return (Status);\r
+}\r
+/**\r
+  Function to determine whether a string is decimal or hex representation of a number \r
+  and return the number converted from the string.\r
+\r
+  @param[in] String   String representation of a number\r
+\r
+  @retval all         the number\r
+**/\r
+UINTN\r
+EFIAPI\r
+ShellStrToUintn(\r
+  IN CONST CHAR16 *String\r
+  )\r
+{\r
+  CONST CHAR16  *Walker;\r
+  for (Walker = String; Walker != NULL && *Walker != CHAR_NULL && *Walker == L' '; Walker++);\r
+  if (StrnCmp(Walker, L"0x", 2) == 0 || StrnCmp(Walker, L"0X", 2) == 0){\r
+    return (StrHexToUintn(Walker));\r
+  }\r
+  return (StrDecimalToUintn(Walker));\r
+}\r
+\r
+/**\r
+  Safely append with automatic string resizing given length of Destination and\r
+  desired length of copy from Source.\r
+\r
+  append the first D characters of Source to the end of Destination, where D is\r
+  the lesser of Count and the StrLen() of Source. If appending those D characters\r
+  will fit within Destination (whose Size is given as CurrentSize) and\r
+  still leave room for a null terminator, then those characters are appended,\r
+  starting at the original terminating null of Destination, and a new terminating\r
+  null is appended.\r
+\r
+  If appending D characters onto Destination will result in a overflow of the size\r
+  given in CurrentSize the string will be grown such that the copy can be performed\r
+  and CurrentSize will be updated to the new size.\r
+\r
+  If Source is NULL, there is nothing to append, just return the current buffer in\r
+  Destination.\r
+\r
+  if Destination is NULL, then ASSERT()\r
+  if Destination's current length (including NULL terminator) is already more then\r
+  CurrentSize, then ASSERT()\r
+\r
+  @param[in,out] Destination   The String to append onto\r
+  @param[in,out] CurrentSize   on call the number of bytes in Destination.  On\r
+                                return possibly the new size (still in bytes).  if NULL\r
+                                then allocate whatever is needed.\r
+  @param[in]      Source        The String to append from\r
+  @param[in]      Count         Maximum number of characters to append.  if 0 then\r
+                                all are appended.\r
+\r
+  @return Destination           return the resultant string.\r
+**/\r
+CHAR16*\r
+EFIAPI\r
+StrnCatGrow (\r
+  IN OUT CHAR16           **Destination,\r
+  IN OUT UINTN            *CurrentSize,\r
+  IN     CONST CHAR16     *Source,\r
+  IN     UINTN            Count\r
+  )\r
+{\r
+  UINTN DestinationStartSize;\r
+  UINTN NewSize;\r
+\r
+  //\r
+  // ASSERTs\r
+  //\r
+  ASSERT(Destination != NULL);\r
+\r
+  //\r
+  // If there's nothing to do then just return Destination\r
+  //\r
+  if (Source == NULL) {\r
+    return (*Destination);\r
+  }\r
+\r
+  //\r
+  // allow for un-initialized pointers, based on size being 0\r
+  //\r
+  if (CurrentSize != NULL && *CurrentSize == 0) {\r
+    *Destination = NULL;\r
+  }\r
+\r
+  //\r
+  // allow for NULL pointers address as Destination\r
+  //\r
+  if (*Destination != NULL) {\r
+    ASSERT(CurrentSize != 0);\r
+    DestinationStartSize = StrSize(*Destination);\r
+    ASSERT(DestinationStartSize <= *CurrentSize);\r
+  } else {\r
+    DestinationStartSize = 0;\r
+//    ASSERT(*CurrentSize == 0);\r
+  }\r
+\r
+  //\r
+  // Append all of Source?\r
+  //\r
+  if (Count == 0) {\r
+    Count = StrLen(Source);\r
+  }\r
+\r
+  //\r
+  // Test and grow if required\r
+  //\r
+  if (CurrentSize != NULL) {\r
+    NewSize = *CurrentSize;\r
+    while (NewSize < (DestinationStartSize + (Count*sizeof(CHAR16)))) {\r
+      NewSize += 2 * Count * sizeof(CHAR16);\r
+    }\r
+    *Destination = ReallocatePool(*CurrentSize, NewSize, *Destination);\r
+    *CurrentSize = NewSize;\r
+  } else {\r
+    *Destination = AllocateZeroPool((Count+1)*sizeof(CHAR16));\r
+  }\r
+\r
+  //\r
+  // Now use standard StrnCat on a big enough buffer\r
+  //\r
+  return StrnCat(*Destination, Source, Count);\r
+}\r