]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ShellPkg/Library/UefiShellLib/UefiShellLib.c
fix build breaks. and allow for new lists to be created.
[mirror_edk2.git] / ShellPkg / Library / UefiShellLib / UefiShellLib.c
index 1ddff9a2a0130478a08370dc9cd05807f8b08abc..54b6a43e038053e5f72c77fafe33d9ed03398d46 100644 (file)
@@ -22,13 +22,16 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #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 "BaseShellLib.h"\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
 #define FIND_XXXXX_FILE_BUFFER_SIZE (SIZE_OF_EFI_FILE_INFO + MAX_FILE_NAME_LEN)\r
@@ -76,9 +79,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
@@ -110,9 +111,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
@@ -237,7 +236,6 @@ ShellLibConstructor (
   mEfiShellInterface          = NULL;\r
   mEfiShellEnvironment2Handle = NULL;\r
 \r
-  ///@todo make a worker constructor so initialize function works\r
   //\r
   // verify that auto initialize is not set false\r
   // \r
@@ -256,8 +254,7 @@ EFIAPI
 ShellLibDestructor (\r
   IN EFI_HANDLE        ImageHandle,\r
   IN EFI_SYSTEM_TABLE  *SystemTable\r
-  )\r
-{\r
+  ){\r
   if (mEfiShellEnvironment2 != NULL) {\r
     gBS->CloseProtocol(mEfiShellEnvironment2Handle==NULL?ImageHandle:mEfiShellEnvironment2Handle,\r
                        &gEfiShellEnvironment2Guid,\r
@@ -455,7 +452,7 @@ ShellOpenFileByDevicePath(
   }\r
   Status = gBS->OpenProtocol(*DeviceHandle,\r
                              &gEfiSimpleFileSystemProtocolGuid,\r
-                             (VOID**) &EfiSimpleFileSystemProtocol,\r
+                             (VOID**)&EfiSimpleFileSystemProtocol,\r
                              gImageHandle,\r
                              NULL,\r
                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
@@ -561,7 +558,7 @@ 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
@@ -569,6 +566,8 @@ ShellOpenFileByName(
 {\r
   EFI_HANDLE                    DeviceHandle;\r
   EFI_DEVICE_PATH_PROTOCOL      *FilePath;\r
+  EFI_STATUS                    Status;\r
+  EFI_FILE_INFO                 *FileInfo;\r
 \r
   //\r
   // ASSERT if FileName is NULL\r
@@ -579,11 +578,16 @@ ShellOpenFileByName(
     //\r
     // Use UEFI Shell 2.0 method\r
     //\r
-    return (mEfiShellProtocol->OpenFileByName(FileName,\r
-                                             FileHandle,\r
-                                             OpenMode));\r
-\r
-    ///@todo add the attributes\r
+    Status = mEfiShellProtocol->OpenFileByName(FileName,\r
+                                               FileHandle,\r
+                                               OpenMode);\r
+    if (!EFI_ERROR(Status)){\r
+      FileInfo = FileHandleGetInfo(*FileHandle);\r
+      ASSERT(FileInfo != NULL);\r
+      FileInfo->Attribute = Attributes;\r
+      Status = FileHandleSetInfo(*FileHandle, FileInfo);\r
+    }\r
+    return (Status);\r
   } \r
   //\r
   // Using EFI Shell version\r
@@ -591,7 +595,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
@@ -632,7 +636,7 @@ 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
@@ -975,7 +979,7 @@ ShellGetExecutionBreakFlag(
 CONST CHAR16*\r
 EFIAPI\r
 ShellGetEnvironmentVariable (\r
-  IN CHAR16                     *EnvKey\r
+  IN CONST CHAR16                *EnvKey\r
   )\r
 {\r
   // \r
@@ -993,7 +997,7 @@ ShellGetEnvironmentVariable (
   //\r
   // using EFI Shell\r
   //\r
-  return (mEfiShellEnvironment2->GetEnv(EnvKey));\r
+  return (mEfiShellEnvironment2->GetEnv((CHAR16*)EnvKey));\r
 }\r
 /**\r
   set the value of an environment variable\r
@@ -1211,32 +1215,26 @@ typedef struct {
   EFI_SHELL_FILE_INFO based list.  it is up to the caller to free the memory via\r
   the ShellCloseFileMetaArg function.\r
 \r
-  @param FileList               the EFI shell list type\r
+  @param[in] FileList           the EFI shell list type\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
 LIST_ENTRY*\r
 EFIAPI\r
 InternalShellConvertFileListType (\r
-  LIST_ENTRY                *FileList\r
-  )\r
-{\r
-  LIST_ENTRY                    *ListHead;\r
+  IN LIST_ENTRY                 *FileList,\r
+  IN OUT LIST_ENTRY             *ListHead\r
+  ){\r
   SHELL_FILE_ARG                *OldInfo;\r
-  LIST_ENTRY                *Link;\r
+  LIST_ENTRY                    *Link;\r
   EFI_SHELL_FILE_INFO_NO_CONST  *NewInfo;\r
 \r
   //\r
-  // ASSERT that FileList is not NULL\r
-  //\r
-  ASSERT(FileList != NULL);\r
-\r
-  //\r
-  // Allocate our list head and initialize the list\r
+  // ASSERTs\r
   //\r
-  ListHead = AllocateZeroPool(sizeof(LIST_ENTRY));\r
-  ASSERT (ListHead != NULL);\r
-  ListHead = InitializeListHead (ListHead);\r
+  ASSERT(FileList  != NULL);\r
+  ASSERT(ListHead  != NULL);\r
 \r
   //\r
   // enumerate through each member of the old list and copy\r
@@ -1290,7 +1288,7 @@ InternalShellConvertFileListType (
     //\r
     // add that to the list\r
     //\r
-    InsertTailList(ListHead, (LIST_ENTRY*)NewInfo);\r
+    InsertTailList(ListHead, &NewInfo->Link);\r
   }\r
   return (ListHead);\r
 }\r
@@ -1304,7 +1302,8 @@ InternalShellConvertFileListType (
   and will process '?' and '*' as such.  the list must be freed with a call to \r
   ShellCloseFileMetaArg().\r
 \r
-  This function will fail if called sequentially without freeing the list in the middle.\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
@@ -1327,8 +1326,7 @@ ShellOpenFileMetaArg (
   )\r
 {\r
   EFI_STATUS                    Status;\r
-  LIST_ENTRY                    *EmptyNode;\r
-  LIST_ENTRY                    *mOldStyleFileList;\r
+  LIST_ENTRY                    mOldStyleFileList;\r
   \r
   //\r
   // ASSERT that Arg and ListHead are not NULL\r
@@ -1340,6 +1338,13 @@ ShellOpenFileMetaArg (
   // Check for UEFI Shell 2.0 protocols\r
   //\r
   if (mEfiShellProtocol != NULL) {\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
     return (mEfiShellProtocol->OpenFileList(Arg, \r
                                            OpenMode, \r
                                            ListHead));\r
@@ -1350,43 +1355,36 @@ ShellOpenFileMetaArg (
   //\r
   ASSERT(mEfiShellEnvironment2 != NULL);\r
 \r
-  //\r
-  // allocate memory for old list head\r
-  //\r
-  mOldStyleFileList = (LIST_ENTRY*)AllocatePool(sizeof(LIST_ENTRY));\r
-  ASSERT(mOldStyleFileList != NULL);\r
-\r
   //\r
   // make sure the list head is initialized\r
   //\r
-  InitializeListHead((LIST_ENTRY*)mOldStyleFileList);\r
+  InitializeListHead(&mOldStyleFileList);\r
 \r
   //\r
   // Get the EFI Shell list of files\r
   //\r
-  Status = mEfiShellEnvironment2->FileMetaArg(Arg, mOldStyleFileList);\r
+  Status = mEfiShellEnvironment2->FileMetaArg(Arg, &mOldStyleFileList);\r
   if (EFI_ERROR(Status)) {\r
     *ListHead = NULL;\r
     return (Status);\r
   }\r
 \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
+  }\r
+\r
   //\r
   // Convert that to equivalent of UEFI Shell 2.0 structure\r
   //\r
-  EmptyNode = InternalShellConvertFileListType(mOldStyleFileList);\r
+  InternalShellConvertFileListType(&mOldStyleFileList, &(*ListHead)->Link);\r
 \r
   //\r
   // Free the EFI Shell version that was converted.\r
   //\r
-  ASSERT_EFI_ERROR(mEfiShellEnvironment2->FreeFileList(mOldStyleFileList));\r
-  FreePool(mOldStyleFileList);\r
-  mOldStyleFileList = NULL;\r
-\r
-  //\r
-  // remove the empty head of the list\r
-  //\r
-  *ListHead = (EFI_SHELL_FILE_INFO*)RemoveEntryList(EmptyNode);\r
-  FreePool(EmptyNode);  \r
+  mEfiShellEnvironment2->FreeFileList(&mOldStyleFileList);\r
 \r
   return (Status);\r
 }\r
@@ -1422,7 +1420,9 @@ ShellCloseFileMetaArg (
     // Since this is EFI Shell version we need to free our internally made copy \r
     // of the list\r
     //\r
-    for (Node = GetFirstNode((LIST_ENTRY*)*ListHead) ; IsListEmpty((LIST_ENTRY*)*ListHead) == FALSE ; Node = GetFirstNode((LIST_ENTRY*)*ListHead)) {\r
+    for ( Node = GetFirstNode(&(*ListHead)->Link) \r
+        ; IsListEmpty(&(*ListHead)->Link) == FALSE \r
+        ; Node = GetFirstNode(&(*ListHead)->Link)) {\r
       RemoveEntryList(Node);\r
       ((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->Handle->Close(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->Handle);\r
       FreePool(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->FullName);\r
@@ -1435,7 +1435,7 @@ ShellCloseFileMetaArg (
 }\r
 \r
 typedef struct {\r
-  LIST_ENTRY List;\r
+  LIST_ENTRY     Link;\r
   CHAR16         *Name;\r
   ParamType      Type;\r
   CHAR16         *Value;\r
@@ -1488,9 +1488,13 @@ 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
@@ -1602,29 +1606,6 @@ InternalCommandLineParse (
       //\r
       // do nothing for NULL argv\r
       //\r
-    } else if (GetItemValue == TRUE) {\r
-      ASSERT(CurrentItemPackage != NULL);\r
-      //\r
-      // get the item VALUE for the previous flag\r
-      //\r
-      GetItemValue = FALSE;\r
-      CurrentItemPackage->Value = AllocateZeroPool(StrSize(Argv[LoopCounter]));\r
-      ASSERT(CurrentItemPackage->Value != NULL);\r
-      StrCpy(CurrentItemPackage->Value, Argv[LoopCounter]);\r
-      InsertTailList(*CheckPackage, (LIST_ENTRY*)CurrentItemPackage);\r
-    } else if (InternalIsFlag(Argv[LoopCounter]) == FALSE) {\r
-      //\r
-      // add this one as a non-flag\r
-      //\r
-      CurrentItemPackage = AllocatePool(sizeof(SHELL_PARAM_PACKAGE));\r
-      ASSERT(CurrentItemPackage != NULL);\r
-      CurrentItemPackage->Name  = NULL;\r
-      CurrentItemPackage->Type  = TypePosition;\r
-      CurrentItemPackage->Value = AllocatePool(StrSize(Argv[LoopCounter]));\r
-      ASSERT(CurrentItemPackage->Value != NULL);\r
-      StrCpy(CurrentItemPackage->Value, Argv[LoopCounter]);\r
-      CurrentItemPackage->OriginalPosition = Count++;\r
-      InsertTailList(*CheckPackage, (LIST_ENTRY*)CurrentItemPackage);\r
     } else if (InternalIsOnCheckList(Argv[LoopCounter], CheckList, &CurrentItemType) == TRUE) {\r
       //\r
       // this is a flag\r
@@ -1636,6 +1617,7 @@ InternalCommandLineParse (
       StrCpy(CurrentItemPackage->Name,  Argv[LoopCounter]);\r
       CurrentItemPackage->Type  = CurrentItemType;\r
       CurrentItemPackage->OriginalPosition = (UINTN)(-1);\r
+      CurrentItemPackage->Value = NULL;\r
 \r
       //\r
       // Does this flag require a value\r
@@ -1649,9 +1631,31 @@ InternalCommandLineParse (
         //\r
         // this item has no value expected; we are done\r
         //\r
-        CurrentItemPackage->Value = NULL;\r
-        InsertTailList(*CheckPackage, (LIST_ENTRY*)CurrentItemPackage);\r
+        InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);\r
       }\r
+    } else if (GetItemValue == TRUE && InternalIsFlag(Argv[LoopCounter]) == FALSE) {\r
+      ASSERT(CurrentItemPackage != NULL);\r
+      //\r
+      // get the item VALUE for the previous flag\r
+      //\r
+      GetItemValue = FALSE;\r
+      CurrentItemPackage->Value = AllocateZeroPool(StrSize(Argv[LoopCounter]));\r
+      ASSERT(CurrentItemPackage->Value != NULL);\r
+      StrCpy(CurrentItemPackage->Value, Argv[LoopCounter]);\r
+      InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);\r
+    } else if (InternalIsFlag(Argv[LoopCounter]) == FALSE) {\r
+      //\r
+      // add this one as a non-flag\r
+      //\r
+      CurrentItemPackage = AllocatePool(sizeof(SHELL_PARAM_PACKAGE));\r
+      ASSERT(CurrentItemPackage != NULL);\r
+      CurrentItemPackage->Name  = NULL;\r
+      CurrentItemPackage->Type  = TypePosition;\r
+      CurrentItemPackage->Value = AllocatePool(StrSize(Argv[LoopCounter]));\r
+      ASSERT(CurrentItemPackage->Value != NULL);\r
+      StrCpy(CurrentItemPackage->Value, Argv[LoopCounter]);\r
+      CurrentItemPackage->OriginalPosition = Count++;\r
+      InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);\r
     } else if (ProblemParam) {\r
       //\r
       // this was a non-recognised flag... error!\r
@@ -1771,7 +1775,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
+      ; Node != CheckPackage \r
+      ; Node = GetFirstNode(CheckPackage)\r
+      ){\r
     //\r
     // Remove it from the list\r
     //\r
@@ -1839,12 +1846,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
@@ -1883,12 +1900,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
@@ -1927,7 +1960,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
@@ -1936,4 +1972,202 @@ ShellCommandLineGetRawValue (
     }\r
   }\r
   return (NULL);\r
-}
\ No newline at end of file
+}\r
+/**\r
+  This is a find and replace function.  it will return the NewString as a copy of \r
+  SourceString with each instance of FindTarget replaced with ReplaceWith.\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] NewSize                  Size in bytes of NewString\r
+  @param[in] FindTarget               String to look for\r
+  @param[in[ ReplaceWith              String to replace FindTarget with\r
+\r
+  @retval EFI_INVALID_PARAMETER       SourceString was NULL\r
+  @retval EFI_INVALID_PARAMETER       NewString was NULL\r
+  @retval EFI_INVALID_PARAMETER       FindTarget was NULL\r
+  @retval EFI_INVALID_PARAMETER       ReplaceWith was NULL\r
+  @retval EFI_INVALID_PARAMETER       FindTarget had length < 1\r
+  @retval EFI_INVALID_PARAMETER       SourceString had length < 1\r
+  @retval EFI_BUFFER_TOO_SMALL        NewSize was less than the minimum size to hold \r
+                                      the new string (truncation occurred)\r
+  @retval EFI_SUCCESS                 the string was sucessfully copied with replacement\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+CopyReplace(\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
+  if ( (SourceString == NULL)\r
+    || (NewString    == NULL)\r
+    || (FindTarget   == NULL)\r
+    || (ReplaceWith  == NULL)\r
+    || (StrLen(FindTarget) < 1)\r
+    || (StrLen(SourceString) < 1)\r
+    ){\r
+    return (EFI_INVALID_PARAMETER);\r
+  }\r
+  NewString = SetMem16(NewString, NewSize, L'\0');\r
+  while (*SourceString != L'\0') {\r
+    if (StrnCmp(SourceString, FindTarget, StrLen(FindTarget)) == 0) {\r
+      SourceString += StrLen(FindTarget);\r
+      if ((StrSize(NewString) + (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
+        return (EFI_BUFFER_TOO_SMALL);\r
+      }\r
+      StrnCat(NewString, SourceString, 1);\r
+      SourceString++;\r
+    }\r
+  }\r
+  return (EFI_SUCCESS);\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] 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
+  VA_LIST           Marker;\r
+  UINTN             BufferSize;\r
+  CHAR16            *PostReplaceFormat;\r
+  CHAR16            *PostReplaceFormat2;\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
+  ASSERT_EFI_ERROR(Status);\r
+  Status = CopyReplace(PostReplaceFormat,  PostReplaceFormat2, BufferSize, L"%E", L"%%E");\r
+  ASSERT_EFI_ERROR(Status);\r
+  Status = CopyReplace(PostReplaceFormat2, PostReplaceFormat,  BufferSize, L"%H", L"%%H");\r
+  ASSERT_EFI_ERROR(Status);\r
+  Status = CopyReplace(PostReplaceFormat,  PostReplaceFormat2, BufferSize, L"%B", L"%%B");\r
+  ASSERT_EFI_ERROR(Status);\r
+  Status = CopyReplace(PostReplaceFormat2, PostReplaceFormat,  BufferSize, 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
+\r
+  if (Col != -1 && Row != -1) {\r
+    Status = gST->ConOut->SetCursorPosition(gST->ConOut, Col, Row);\r
+    ASSERT_EFI_ERROR(Status);\r
+  }\r
+\r
+  NormalAttribute = gST->ConOut->Mode->Attribute;\r
+  FormatWalker = PostReplaceFormat2;\r
+  while (*FormatWalker != L'\0') {\r
+    //\r
+    // Find the next attribute change request\r
+    //\r
+    ResumeLocation = StrStr(FormatWalker, L"%");\r
+    if (ResumeLocation != NULL) {\r
+      *ResumeLocation = L'\0';\r
+    }\r
+    //\r
+    // print the current FormatWalker string\r
+    //\r
+    Status = gST->ConOut->OutputString(gST->ConOut, FormatWalker);\r
+    ASSERT_EFI_ERROR(Status);\r
+    //\r
+    // update the attribute\r
+    //\r
+    if (ResumeLocation != NULL) {\r
+      switch (*(ResumeLocation+1)) {\r
+        case (L'N'):\r
+          gST->ConOut->SetAttribute(gST->ConOut, NormalAttribute);\r
+          break;\r
+        case (L'E'):\r
+          gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_YELLOW, ((NormalAttribute&(BIT4|BIT5|BIT6))>>4)));\r
+          break;\r
+        case (L'H'):\r
+          gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_WHITE, ((NormalAttribute&(BIT4|BIT5|BIT6))>>4)));\r
+          break;\r
+        case (L'B'):\r
+          gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_BLUE, ((NormalAttribute&(BIT4|BIT5|BIT6))>>4)));\r
+          break;\r
+        case (L'V'):\r
+          gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_GREEN, ((NormalAttribute&(BIT4|BIT5|BIT6))>>4)));\r
+          break;\r
+        default:\r
+          ASSERT(FALSE);\r
+          break;\r
+      }\r
+    } else {\r
+      //\r
+      // reset to normal now...\r
+      //\r
+      gST->ConOut->SetAttribute(gST->ConOut, NormalAttribute);\r
+      break;\r
+    }\r
+\r
+    //\r
+    // update FormatWalker to Resume + 2 (skip the % and the indicator)\r
+    //\r
+    FormatWalker = ResumeLocation + 2;\r
+  }\r
+\r
+  FreePool(PostReplaceFormat2);\r
+\r
+  return (Return);\r
+}\r