]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ShellPkg/Application/Shell/Shell.c
ShellPkg: add array index check for shell delay option
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
index 660e0e5773c0a4891978605ddc117069a7900afd..ec344137d355e2dad689bc54f71c3839a896d7f5 100644 (file)
@@ -1,8 +1,9 @@
 /** @file\r
   This is THE shell (application)\r
 \r
-  Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>\r
-  Copyright (c) 2013, Hewlett-Packard Development Company, L.P.\r
+  Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>\r
+  (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.<BR>\r
+  Copyright 2015-2018 Dell Technologies.<BR>\r
   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
@@ -33,6 +34,7 @@ SHELL_INFO ShellInfoObject = {
       0,\r
       0,\r
       0,\r
+      0,\r
       0\r
     }},\r
     0,\r
@@ -69,32 +71,125 @@ SHELL_INFO ShellInfoObject = {
 STATIC CONST CHAR16 mScriptExtension[]      = L".NSH";\r
 STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";\r
 STATIC CONST CHAR16 mStartupScript[]        = L"startup.nsh";\r
+CONST CHAR16 mNoNestingEnvVarName[]         = L"nonesting";\r
+CONST CHAR16 mNoNestingTrue[]               = L"True";\r
+CONST CHAR16 mNoNestingFalse[]              = L"False";\r
 \r
 /**\r
-  Find a command line contains a split operation\r
+  Cleans off leading and trailing spaces and tabs.\r
 \r
-  @param[in] CmdLine      The command line to parse.\r
+  @param[in] String pointer to the string to trim them off.\r
+**/\r
+EFI_STATUS\r
+TrimSpaces(\r
+  IN CHAR16 **String\r
+  )\r
+{\r
+  ASSERT(String != NULL);\r
+  ASSERT(*String!= NULL);\r
+  //\r
+  // Remove any spaces and tabs at the beginning of the (*String).\r
+  //\r
+  while (((*String)[0] == L' ') || ((*String)[0] == L'\t')) {\r
+    CopyMem((*String), (*String)+1, StrSize((*String)) - sizeof((*String)[0]));\r
+  }\r
+\r
+  //\r
+  // Remove any spaces and tabs at the end of the (*String).\r
+  //\r
+  while ((StrLen (*String) > 0) && (((*String)[StrLen((*String))-1] == L' ') || ((*String)[StrLen((*String))-1] == L'\t'))) {\r
+    (*String)[StrLen((*String))-1] = CHAR_NULL;\r
+  }\r
+\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+  Parse for the next instance of one string within another string. Can optionally make sure that\r
+  the string was not escaped (^ character) per the shell specification.\r
+\r
+  @param[in] SourceString             The string to search within\r
+  @param[in] FindString               The string to look for\r
+  @param[in] CheckForEscapeCharacter  TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances\r
+**/\r
+CHAR16*\r
+FindNextInstance(\r
+  IN CONST CHAR16   *SourceString,\r
+  IN CONST CHAR16   *FindString,\r
+  IN CONST BOOLEAN  CheckForEscapeCharacter\r
+  )\r
+{\r
+  CHAR16 *Temp;\r
+  if (SourceString == NULL) {\r
+    return (NULL);\r
+  }\r
+  Temp = StrStr(SourceString, FindString);\r
+\r
+  //\r
+  // If nothing found, or we don't care about escape characters\r
+  //\r
+  if (Temp == NULL || !CheckForEscapeCharacter) {\r
+    return (Temp);\r
+  }\r
+\r
+  //\r
+  // If we found an escaped character, try again on the remainder of the string\r
+  //\r
+  if ((Temp > (SourceString)) && *(Temp-1) == L'^') {\r
+    return FindNextInstance(Temp+1, FindString, CheckForEscapeCharacter);\r
+  }\r
+\r
+  //\r
+  // we found the right character\r
+  //\r
+  return (Temp);\r
+}\r
+\r
+/**\r
+  Check whether the string between a pair of % is a valid environment variable name.\r
+\r
+  @param[in] BeginPercent       pointer to the first percent.\r
+  @param[in] EndPercent          pointer to the last percent.\r
 \r
-  @retval                 A pointer to the | character in CmdLine or NULL if not present.\r
+  @retval TRUE                          is a valid environment variable name.\r
+  @retval FALSE                         is NOT a valid environment variable name.\r
 **/\r
 BOOLEAN\r
-EFIAPI\r
-FindSplit(\r
-  IN CONST CHAR16 *CmdLine\r
+IsValidEnvironmentVariableName(\r
+  IN CONST CHAR16     *BeginPercent,\r
+  IN CONST CHAR16     *EndPercent\r
   )\r
 {\r
-  CONST CHAR16 *TempSpot;\r
-  TempSpot = NULL;\r
-  if (StrStr(CmdLine, L"|") != NULL) {\r
-    for (TempSpot = CmdLine ; TempSpot != NULL && *TempSpot != CHAR_NULL ; TempSpot++) {\r
-      if (*TempSpot == L'^' && *(TempSpot+1) == L'|') {\r
-        TempSpot++;\r
-      } else if (*TempSpot == L'|') {\r
-        break;\r
+  CONST CHAR16    *Walker;\r
+\r
+  Walker = NULL;\r
+\r
+  ASSERT (BeginPercent != NULL);\r
+  ASSERT (EndPercent != NULL);\r
+  ASSERT (BeginPercent < EndPercent);\r
+\r
+  if ((BeginPercent + 1) == EndPercent) {\r
+    return FALSE;\r
+  }\r
+\r
+  for (Walker = BeginPercent + 1; Walker < EndPercent; Walker++) {\r
+    if (\r
+        (*Walker >= L'0' && *Walker <= L'9') ||\r
+        (*Walker >= L'A' && *Walker <= L'Z') ||\r
+        (*Walker >= L'a' && *Walker <= L'z') ||\r
+        (*Walker == L'_')\r
+      ) {\r
+      if (Walker == BeginPercent + 1 && (*Walker >= L'0' && *Walker <= L'9')) {\r
+        return FALSE;\r
+      } else {\r
+        continue;\r
       }\r
+    } else {\r
+      return FALSE;\r
     }\r
   }\r
-  return (TempSpot);\r
+\r
+  return TRUE;\r
 }\r
 \r
 /**\r
@@ -106,25 +201,55 @@ FindSplit(
   @retval FALSE           CmdLine does not have a valid split.\r
 **/\r
 BOOLEAN\r
-EFIAPI\r
 ContainsSplit(\r
   IN CONST CHAR16 *CmdLine\r
   )\r
 {\r
   CONST CHAR16 *TempSpot;\r
-  TempSpot = FindSplit(CmdLine);\r
-  return (TempSpot != NULL && *TempSpot != CHAR_NULL);\r
+  CONST CHAR16 *FirstQuote;\r
+  CONST CHAR16 *SecondQuote;\r
+\r
+  FirstQuote    = FindNextInstance (CmdLine, L"\"", TRUE);\r
+  SecondQuote   = NULL;\r
+  TempSpot      = FindFirstCharacter(CmdLine, L"|", L'^');\r
+\r
+  if (FirstQuote == NULL    ||\r
+      TempSpot == NULL      ||\r
+      TempSpot == CHAR_NULL ||\r
+      FirstQuote > TempSpot\r
+      ) {\r
+    return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));\r
+  }\r
+\r
+  while ((TempSpot != NULL) && (*TempSpot != CHAR_NULL)) {\r
+    if (FirstQuote == NULL || FirstQuote > TempSpot) {\r
+      break;\r
+    }\r
+    SecondQuote = FindNextInstance (FirstQuote + 1, L"\"", TRUE);\r
+    if (SecondQuote == NULL) {\r
+      break;\r
+    }\r
+    if (SecondQuote < TempSpot) {\r
+      FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);\r
+      continue;\r
+    } else {\r
+      FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);\r
+      TempSpot = FindFirstCharacter(TempSpot + 1, L"|", L'^');\r
+      continue;\r
+    }\r
+  }\r
+\r
+  return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));\r
 }\r
 \r
 /**\r
-  Function to start monitoring for CTRL-S using SimpleTextInputEx.  This \r
+  Function to start monitoring for CTRL-S using SimpleTextInputEx.  This\r
   feature's enabled state was not known when the shell initially launched.\r
 \r
   @retval EFI_SUCCESS           The feature is enabled.\r
-  @retval EFI_OUT_OF_RESOURCES  There is not enough mnemory available.\r
+  @retval EFI_OUT_OF_RESOURCES  There is not enough memory available.\r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
 InternalEfiShellStartCtrlSMonitor(\r
   VOID\r
   )\r
@@ -142,8 +267,8 @@ InternalEfiShellStartCtrlSMonitor(
     EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
   if (EFI_ERROR(Status)) {\r
     ShellPrintHiiEx(\r
-      -1, \r
-      -1, \r
+      -1,\r
+      -1,\r
       NULL,\r
       STRING_TOKEN (STR_SHELL_NO_IN_EX),\r
       ShellInfoObject.HiiHandle);\r
@@ -160,7 +285,7 @@ InternalEfiShellStartCtrlSMonitor(
     &KeyData,\r
     NotificationFunction,\r
     &ShellInfoObject.CtrlSNotifyHandle1);\r
-  \r
+\r
   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
   if (!EFI_ERROR(Status)) {\r
     Status = SimpleEx->RegisterKeyNotify(\r
@@ -178,7 +303,7 @@ InternalEfiShellStartCtrlSMonitor(
       &KeyData,\r
       NotificationFunction,\r
       &ShellInfoObject.CtrlSNotifyHandle3);\r
-  }  \r
+  }\r
   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
   if (!EFI_ERROR(Status)) {\r
     Status = SimpleEx->RegisterKeyNotify(\r
@@ -214,6 +339,7 @@ UefiMain (
   UINTN                           Size;\r
   EFI_HANDLE                      ConInHandle;\r
   EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *OldConIn;\r
+  SPLIT_LIST                      *Split;\r
 \r
   if (PcdGet8(PcdShellSupportLevel) > 3) {\r
     return (EFI_UNSUPPORTED);\r
@@ -289,7 +415,8 @@ UefiMain (
         ///@todo Add our package into Framework HII\r
       }\r
       if (ShellInfoObject.HiiHandle == NULL) {\r
-        return (EFI_NOT_STARTED);\r
+        Status = EFI_NOT_STARTED;\r
+        goto FreeResources;\r
       }\r
     }\r
 \r
@@ -316,10 +443,15 @@ UefiMain (
     Status = CommandInit();\r
     ASSERT_EFI_ERROR(Status);\r
 \r
+    Status = ShellInitEnvVarList ();\r
+\r
     //\r
     // Check the command line\r
     //\r
-    Status = ProcessCommandLine();\r
+    Status = ProcessCommandLine ();\r
+    if (EFI_ERROR (Status)) {\r
+      goto FreeResources;\r
+    }\r
 \r
     //\r
     // If shell support level is >= 1 create the mappings and paths\r
@@ -328,6 +460,29 @@ UefiMain (
       Status = ShellCommandCreateInitialMappingsAndPaths();\r
     }\r
 \r
+    //\r
+    // Set the environment variable for nesting support\r
+    //\r
+    Size = 0;\r
+    TempString = NULL;\r
+    if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) {\r
+      //\r
+      // No change.  require nesting in Shell Protocol Execute()\r
+      //\r
+      StrnCatGrow(&TempString,\r
+                  &Size,\r
+                  L"False",\r
+                  0);\r
+    } else {\r
+      StrnCatGrow(&TempString,\r
+                  &Size,\r
+                  mNoNestingTrue,\r
+                  0);\r
+    }\r
+    Status = InternalEfiShellSetEnv(mNoNestingEnvVarName, TempString, TRUE);\r
+    SHELL_FREE_NON_NULL(TempString);\r
+    Size = 0;\r
+\r
     //\r
     // save the device path for the loaded image and the device path for the filepath (under loaded image)\r
     // These are where to look for the startup.nsh file\r
@@ -462,14 +617,10 @@ UefiMain (
           // Reset page break back to default.\r
           //\r
           ShellInfoObject.PageBreakEnabled        = PcdGetBool(PcdShellPageBreakDefault);\r
+          ASSERT (ShellInfoObject.ConsoleInfo != NULL);\r
           ShellInfoObject.ConsoleInfo->Enabled    = TRUE;\r
           ShellInfoObject.ConsoleInfo->RowCounter = 0;\r
 \r
-          //\r
-          // Reset the CTRL-C event (yes we ignore the return values)\r
-          //\r
-          Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);\r
-\r
           //\r
           // Display Prompt\r
           //\r
@@ -484,6 +635,7 @@ UefiMain (
     }\r
   }\r
 \r
+FreeResources:\r
   //\r
   // uninstall protocols / free memory / etc...\r
   //\r
@@ -507,7 +659,7 @@ UefiMain (
     if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){\r
       InternalEfiShellSetEnv(L"cwd", NULL, TRUE);\r
     }\r
-    CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol);\r
+    CleanUpShellEnvironment (ShellInfoObject.NewEfiShellProtocol);\r
     DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);\r
   }\r
 \r
@@ -516,7 +668,17 @@ UefiMain (
   }\r
 \r
   if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){\r
-    ASSERT(FALSE); ///@todo finish this de-allocation.\r
+    ASSERT(FALSE); ///@todo finish this de-allocation (free SplitStdIn/Out when needed).\r
+\r
+    for ( Split = (SPLIT_LIST*)GetFirstNode (&ShellInfoObject.SplitList.Link)\r
+        ; !IsNull (&ShellInfoObject.SplitList.Link, &Split->Link)\r
+        ; Split = (SPLIT_LIST *)GetNextNode (&ShellInfoObject.SplitList.Link, &Split->Link)\r
+     ) {\r
+      RemoveEntryList (&Split->Link);\r
+      FreePool (Split);\r
+    }\r
+\r
+    DEBUG_CODE (InitializeListHead (&ShellInfoObject.SplitList.Link););\r
   }\r
 \r
   if (ShellInfoObject.ShellInitSettings.FileName != NULL) {\r
@@ -545,6 +707,8 @@ UefiMain (
     DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);\r
   }\r
 \r
+  ShellFreeEnvVarList ();\r
+\r
   if (ShellCommandGetExit()) {\r
     return ((EFI_STATUS)ShellCommandGetExitCode());\r
   }\r
@@ -554,11 +718,11 @@ UefiMain (
 /**\r
   Sets all the alias' that were registered with the ShellCommandLib library.\r
 \r
-  @retval EFI_SUCCESS           all init commands were run sucessfully.\r
+  @retval EFI_SUCCESS           all init commands were run successfully.\r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
 SetBuiltInAlias(\r
+  VOID\r
   )\r
 {\r
   EFI_STATUS          Status;\r
@@ -596,7 +760,6 @@ SetBuiltInAlias(
   @retval FALSE             The 2 command names are not the same.\r
 **/\r
 BOOLEAN\r
-EFIAPI\r
 IsCommand(\r
   IN CONST CHAR16 *Command1,\r
   IN CONST CHAR16 *Command2\r
@@ -617,7 +780,6 @@ IsCommand(
   @retval FALSE             The command is not a script only command.\r
 **/\r
 BOOLEAN\r
-EFIAPI\r
 IsScriptOnlyCommand(\r
   IN CONST CHAR16 *CommandName\r
   )\r
@@ -639,16 +801,15 @@ IsScriptOnlyCommand(
   loaded image protocol installed on it.  The FilePath will point to the device path\r
   for the file that was loaded.\r
 \r
-  @param[in, out] DevPath       On a sucessful return the device path to the loaded image.\r
-  @param[in, out] FilePath      On a sucessful return the device path to the file.\r
+  @param[in, out] DevPath       On a successful return the device path to the loaded image.\r
+  @param[in, out] FilePath      On a successful return the device path to the file.\r
 \r
-  @retval EFI_SUCCESS           The 2 device paths were sucessfully returned.\r
+  @retval EFI_SUCCESS           The 2 device paths were successfully returned.\r
   @retval other                 A error from gBS->HandleProtocol.\r
 \r
   @sa HandleProtocol\r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
 GetDevicePathsForImageAndFile (\r
   IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,\r
   IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath\r
@@ -696,20 +857,6 @@ GetDevicePathsForImageAndFile (
   return (Status);\r
 }\r
 \r
-STATIC CONST SHELL_PARAM_ITEM mShellParamList[] = {\r
-  {L"-nostartup",     TypeFlag},\r
-  {L"-startup",       TypeFlag},\r
-  {L"-noconsoleout",  TypeFlag},\r
-  {L"-noconsolein",   TypeFlag},\r
-  {L"-nointerrupt",   TypeFlag},\r
-  {L"-nomap",         TypeFlag},\r
-  {L"-noversion",     TypeFlag},\r
-  {L"-startup",       TypeFlag},\r
-  {L"-delay",         TypeValue},\r
-  {L"-_exit",         TypeFlag},\r
-  {NULL, TypeMax}\r
-  };\r
-\r
 /**\r
   Process all Uefi Shell 2.0 command line options.\r
 \r
@@ -738,54 +885,235 @@ STATIC CONST SHELL_PARAM_ITEM mShellParamList[] = {
   @retval EFI_SUCCESS           The variable is initialized.\r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
 ProcessCommandLine(\r
   VOID\r
   )\r
 {\r
-  EFI_STATUS    Status;\r
-  LIST_ENTRY    *Package;\r
-  UINTN         Size;\r
-  CONST CHAR16  *TempConst;\r
-  UINTN         Count;\r
-  UINTN         LoopVar;\r
-  CHAR16        *ProblemParam;\r
-  UINT64        Intermediate;\r
-\r
-  Package       = NULL;\r
-  ProblemParam  = NULL;\r
-  \r
-  Status = ShellCommandLineParse (mShellParamList, &Package, NULL, FALSE);\r
-\r
-  Count = 1;\r
-  Size = 0;\r
-  TempConst = ShellCommandLineGetRawValue(Package, Count++);\r
-  if (TempConst != NULL && StrLen(TempConst)) {\r
-    ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(TempConst));\r
-    if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
-      return (EFI_OUT_OF_RESOURCES);\r
+  UINTN                           Size;\r
+  UINTN                           LoopVar;\r
+  CHAR16                          *CurrentArg;\r
+  CHAR16                          *DelayValueStr;\r
+  UINT64                          DelayValue;\r
+  EFI_STATUS                      Status;\r
+  EFI_UNICODE_COLLATION_PROTOCOL  *UnicodeCollation;\r
+\r
+  // `file-name-options` will contain arguments to `file-name` that we don't\r
+  // know about. This would cause ShellCommandLineParse to error, so we parse\r
+  // arguments manually, ignoring those after the first thing that doesn't look\r
+  // like a shell option (which is assumed to be `file-name`).\r
+\r
+  Status = gBS->LocateProtocol (\r
+                  &gEfiUnicodeCollation2ProtocolGuid,\r
+                  NULL,\r
+                  (VOID **) &UnicodeCollation\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    Status = gBS->LocateProtocol (\r
+                    &gEfiUnicodeCollationProtocolGuid,\r
+                    NULL,\r
+                    (VOID **) &UnicodeCollation\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  // Set default options\r
+  ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup      = FALSE;\r
+  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup    = FALSE;\r
+  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = FALSE;\r
+  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn  = FALSE;\r
+  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt  = FALSE;\r
+  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap        = FALSE;\r
+  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion    = FALSE;\r
+  ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay        = FALSE;\r
+  ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit         = FALSE;\r
+  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest       = FALSE;\r
+  ShellInfoObject.ShellInitSettings.Delay = 5;\r
+\r
+  //\r
+  // Start LoopVar at 0 to parse only optional arguments at Argv[0]\r
+  // and parse other parameters from Argv[1].  This is for use case that\r
+  // UEFI Shell boot option is created, and OptionalData is provided\r
+  // that starts with shell command-line options.\r
+  //\r
+  for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
+    CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar];\r
+    if (UnicodeCollation->StriColl (\r
+                            UnicodeCollation,\r
+                            L"-startup",\r
+                            CurrentArg\r
+                            ) == 0) {\r
+      ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup      = TRUE;\r
     }\r
-    StrCpy(ShellInfoObject.ShellInitSettings.FileName, TempConst);\r
-    ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;\r
-    for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
-      if (StrCmp(gEfiShellParametersProtocol->Argv[LoopVar], ShellInfoObject.ShellInitSettings.FileName)==0) {\r
-        LoopVar++;\r
+    else if (UnicodeCollation->StriColl (\r
+                                 UnicodeCollation,\r
+                                 L"-nostartup",\r
+                                 CurrentArg\r
+                                 ) == 0) {\r
+      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup    = TRUE;\r
+    }\r
+    else if (UnicodeCollation->StriColl (\r
+                                 UnicodeCollation,\r
+                                 L"-noconsoleout",\r
+                                 CurrentArg\r
+                                 ) == 0) {\r
+      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = TRUE;\r
+    }\r
+    else if (UnicodeCollation->StriColl (\r
+                                 UnicodeCollation,\r
+                                 L"-noconsolein",\r
+                                 CurrentArg\r
+                                 ) == 0) {\r
+      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn  = TRUE;\r
+    }\r
+    else if (UnicodeCollation->StriColl (\r
+                                 UnicodeCollation,\r
+                                 L"-nointerrupt",\r
+                                 CurrentArg\r
+                                 ) == 0) {\r
+      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt  = TRUE;\r
+    }\r
+    else if (UnicodeCollation->StriColl (\r
+                                 UnicodeCollation,\r
+                                 L"-nomap",\r
+                                 CurrentArg\r
+                                 ) == 0) {\r
+      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap        = TRUE;\r
+    }\r
+    else if (UnicodeCollation->StriColl (\r
+                                 UnicodeCollation,\r
+                                 L"-noversion",\r
+                                 CurrentArg\r
+                                 ) == 0) {\r
+      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion    = TRUE;\r
+    }\r
+    else if (UnicodeCollation->StriColl (\r
+                                 UnicodeCollation,\r
+                                 L"-nonest",\r
+                                 CurrentArg\r
+                                 ) == 0) {\r
+      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest       = TRUE;\r
+    }\r
+    else if (UnicodeCollation->StriColl (\r
+                                 UnicodeCollation,\r
+                                 L"-delay",\r
+                                 CurrentArg\r
+                                 ) == 0) {\r
+      ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay        = TRUE;\r
+      // Check for optional delay value following "-delay"\r
+      if ((LoopVar + 1) >= gEfiShellParametersProtocol->Argc) {\r
+        DelayValueStr = NULL;\r
+      } else {\r
+        DelayValueStr = gEfiShellParametersProtocol->Argv[LoopVar + 1];\r
+      }\r
+      if (DelayValueStr != NULL){\r
+        if (*DelayValueStr == L':') {\r
+          DelayValueStr++;\r
+        }\r
+        if (!EFI_ERROR(ShellConvertStringToUint64 (\r
+                        DelayValueStr,\r
+                        &DelayValue,\r
+                        FALSE,\r
+                        FALSE\r
+                        ))) {\r
+          ShellInfoObject.ShellInitSettings.Delay = (UINTN)DelayValue;\r
+          LoopVar++;\r
+        }\r
+      }\r
+    } else if (UnicodeCollation->StriColl (\r
+                                   UnicodeCollation,\r
+                                   L"-exit",\r
+                                   CurrentArg\r
+                                   ) == 0) {\r
+      ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit         = TRUE;\r
+    } else if (StrnCmp (L"-", CurrentArg, 1) == 0) {\r
+      // Unrecognized option\r
+      ShellPrintHiiEx(-1, -1, NULL,\r
+        STRING_TOKEN (STR_GEN_PROBLEM),\r
+        ShellInfoObject.HiiHandle,\r
+        CurrentArg\r
+        );\r
+      return EFI_INVALID_PARAMETER;\r
+    } else {\r
+      //\r
+      // First argument should be Shell.efi image name\r
+      //\r
+      if (LoopVar == 0) {\r
+        continue;\r
+      }\r
+\r
+      ShellInfoObject.ShellInitSettings.FileName = NULL;\r
+      Size = 0;\r
+      //\r
+      // If first argument contains a space, then add double quotes before the argument\r
+      //\r
+      if (StrStr (CurrentArg, L" ") != NULL) {\r
+        StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0);\r
+        if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
+          return (EFI_OUT_OF_RESOURCES);\r
+        }\r
+      }\r
+      StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileName, &Size, CurrentArg, 0);\r
+      if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
+        return (EFI_OUT_OF_RESOURCES);\r
+      }\r
+      //\r
+      // If first argument contains a space, then add double quotes after the argument\r
+      //\r
+      if (StrStr (CurrentArg, L" ") != NULL) {\r
+        StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0);\r
+        if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
+          return (EFI_OUT_OF_RESOURCES);\r
+        }\r
+      }\r
+      //\r
+      // We found `file-name`.\r
+      //\r
+      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;\r
+      LoopVar++;\r
+\r
+      // Add `file-name-options`\r
+      for (Size = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
+        ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));\r
+        //\r
+        // Add a space between arguments\r
         //\r
-        // We found the file... add the rest of the params...\r
+        if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
+          StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions, &Size, L" ", 0);\r
+          if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
+            SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
+            return (EFI_OUT_OF_RESOURCES);\r
+          }\r
+        }\r
+        //\r
+        // If an argumnent contains a space, then add double quotes before the argument\r
         //\r
-        for ( ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
-          ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));\r
+        if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != NULL) {\r
           StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
                       &Size,\r
-                      L" ",\r
+                      L"\"",\r
                       0);\r
           if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
             SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
             return (EFI_OUT_OF_RESOURCES);\r
           }\r
+        }\r
+        StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
+                    &Size,\r
+                    gEfiShellParametersProtocol->Argv[LoopVar],\r
+                    0);\r
+        if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
+          SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
+          return (EFI_OUT_OF_RESOURCES);\r
+        }\r
+        //\r
+        // If an argumnent contains a space, then add double quotes after the argument\r
+        //\r
+        if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != NULL) {\r
           StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
                       &Size,\r
-                      gEfiShellParametersProtocol->Argv[LoopVar],\r
+                      L"\"",\r
                       0);\r
           if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
             SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
@@ -794,44 +1122,74 @@ ProcessCommandLine(
         }\r
       }\r
     }\r
-  } else {\r
-    ShellCommandLineFreeVarList(Package);\r
-    Package = NULL;\r
-    Status = ShellCommandLineParse (mShellParamList, &Package, &ProblemParam, FALSE);\r
-    if (EFI_ERROR(Status)) {\r
-      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), ShellInfoObject.HiiHandle, ProblemParam);\r
-      FreePool(ProblemParam);\r
-      ShellCommandLineFreeVarList(Package);\r
-      return (EFI_INVALID_PARAMETER);\r
-    }\r
   }\r
 \r
-  ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup      = ShellCommandLineGetFlag(Package, L"-startup");\r
-  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup    = ShellCommandLineGetFlag(Package, L"-nostartup");\r
-  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = ShellCommandLineGetFlag(Package, L"-noconsoleout");\r
-  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn  = ShellCommandLineGetFlag(Package, L"-noconsolein");\r
-  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt  = ShellCommandLineGetFlag(Package, L"-nointerrupt");\r
-  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap        = ShellCommandLineGetFlag(Package, L"-nomap");\r
-  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion    = ShellCommandLineGetFlag(Package, L"-noversion");\r
-  ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay        = ShellCommandLineGetFlag(Package, L"-delay");\r
-  ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit         = ShellCommandLineGetFlag(Package, L"-_exit");\r
-\r
-  ShellInfoObject.ShellInitSettings.Delay = 5;\r
-\r
+  // "-nointerrupt" overrides "-delay"\r
   if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {\r
     ShellInfoObject.ShellInitSettings.Delay = 0;\r
-  } else if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay) {\r
-    TempConst = ShellCommandLineGetValue(Package, L"-delay");\r
-    if (TempConst != NULL && *TempConst == L':') {\r
-      TempConst++;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Function try to find location of the Startup.nsh file.\r
+\r
+  The buffer is callee allocated and should be freed by the caller.\r
+\r
+  @param    ImageDevicePath       The path to the image for shell.  first place to look for the startup script\r
+  @param    FileDevicePath        The path to the file for shell.  second place to look for the startup script.\r
+\r
+  @retval   NULL                  No Startup.nsh file was found.\r
+  @return   !=NULL                Pointer to NULL-terminated path.\r
+**/\r
+CHAR16 *\r
+LocateStartupScript (\r
+  IN EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath,\r
+  IN EFI_DEVICE_PATH_PROTOCOL *FileDevicePath\r
+  )\r
+{\r
+  CHAR16          *StartupScriptPath;\r
+  CHAR16          *TempSpot;\r
+  CONST CHAR16    *MapName;\r
+  UINTN           Size;\r
+\r
+  StartupScriptPath = NULL;\r
+  Size              = 0;\r
+\r
+  //\r
+  // Try to find 'Startup.nsh' in the directory where the shell itself was launched.\r
+  //\r
+  MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath (&ImageDevicePath);\r
+  if (MapName != NULL) {\r
+    StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, MapName, 0);\r
+    if (StartupScriptPath == NULL) {\r
+      //\r
+      // Do not locate the startup script in sys path when out of resource.\r
+      //\r
+      return NULL;\r
     }\r
-    if (TempConst != NULL && !EFI_ERROR(ShellConvertStringToUint64(TempConst, &Intermediate, FALSE, FALSE))) {\r
-      ShellInfoObject.ShellInitSettings.Delay = (UINTN)Intermediate;\r
+    TempSpot = StrStr (StartupScriptPath, L";");\r
+    if (TempSpot != NULL) {\r
+      *TempSpot = CHAR_NULL;\r
     }\r
+\r
+    InternalEfiShellSetEnv(L"homefilesystem", StartupScriptPath, TRUE);\r
+\r
+    StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, ((FILEPATH_DEVICE_PATH *)FileDevicePath)->PathName, 0);\r
+    PathRemoveLastItem (StartupScriptPath);\r
+    StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, mStartupScript, 0);\r
   }\r
-  ShellCommandLineFreeVarList(Package);\r
 \r
-  return (Status);\r
+  //\r
+  // Try to find 'Startup.nsh' in the execution path defined by the envrionment variable PATH.\r
+  //\r
+  if ((StartupScriptPath == NULL) || EFI_ERROR (ShellIsFile (StartupScriptPath))) {\r
+    SHELL_FREE_NON_NULL (StartupScriptPath);\r
+    StartupScriptPath = ShellFindFilePath (mStartupScript);\r
+  }\r
+\r
+  return StartupScriptPath;\r
 }\r
 \r
 /**\r
@@ -845,26 +1203,21 @@ ProcessCommandLine(
   @retval EFI_SUCCESS           the variable is initialized.\r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
 DoStartupScript(\r
-  EFI_DEVICE_PATH_PROTOCOL *ImagePath,\r
-  EFI_DEVICE_PATH_PROTOCOL *FilePath\r
+  IN EFI_DEVICE_PATH_PROTOCOL *ImagePath,\r
+  IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
   )\r
 {\r
   EFI_STATUS                    Status;\r
+  EFI_STATUS                    CalleeStatus;\r
   UINTN                         Delay;\r
   EFI_INPUT_KEY                 Key;\r
-  SHELL_FILE_HANDLE             FileHandle;\r
-  EFI_DEVICE_PATH_PROTOCOL      *NewPath;\r
-  EFI_DEVICE_PATH_PROTOCOL      *NamePath;\r
   CHAR16                        *FileStringPath;\r
-  CHAR16                        *TempSpot;\r
+  CHAR16                        *FullFileStringPath;\r
   UINTN                         NewSize;\r
-  CONST CHAR16                  *MapName;\r
 \r
   Key.UnicodeChar = CHAR_NULL;\r
   Key.ScanCode    = 0;\r
-  FileHandle      = NULL;\r
 \r
   if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) {\r
     //\r
@@ -878,12 +1231,15 @@ DoStartupScript(
     if (FileStringPath == NULL) {\r
       return (EFI_OUT_OF_RESOURCES);\r
     }\r
-    StrCpy(FileStringPath, ShellInfoObject.ShellInitSettings.FileName);\r
+    StrCpyS(FileStringPath, NewSize/sizeof(CHAR16), ShellInfoObject.ShellInitSettings.FileName);\r
     if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
-      StrCat(FileStringPath, L" ");\r
-      StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);\r
+      StrnCatS(FileStringPath, NewSize/sizeof(CHAR16), L" ", NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);\r
+      StrnCatS(FileStringPath, NewSize/sizeof(CHAR16), ShellInfoObject.ShellInitSettings.FileOptions, NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);\r
+    }\r
+    Status = RunShellCommand(FileStringPath, &CalleeStatus);\r
+    if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit == TRUE) {\r
+      ShellCommandRegisterExit(gEfiShellProtocol->BatchIsActive(), (UINT64)CalleeStatus);\r
     }\r
-    Status = RunCommand(FileStringPath);\r
     FreePool(FileStringPath);\r
     return (Status);\r
 \r
@@ -923,60 +1279,23 @@ DoStartupScript(
     return (EFI_SUCCESS);\r
   }\r
 \r
-  //\r
-  // Try the first location (must be file system)\r
-  //\r
-  MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath(&ImagePath);\r
-  if (MapName != NULL) {\r
-    FileStringPath = NULL;\r
-    NewSize = 0;\r
-    FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0);\r
-    if (FileStringPath == NULL) {\r
-      Status = EFI_OUT_OF_RESOURCES;\r
+  FileStringPath = LocateStartupScript (ImagePath, FilePath);\r
+  if (FileStringPath != NULL) {\r
+    FullFileStringPath = FullyQualifyPath(FileStringPath);\r
+    if (FullFileStringPath == NULL) {\r
+      Status = RunScriptFile (FileStringPath, NULL, FileStringPath, ShellInfoObject.NewShellParametersProtocol);\r
     } else {\r
-      TempSpot = StrStr(FileStringPath, L";");\r
-      if (TempSpot != NULL) {\r
-        *TempSpot = CHAR_NULL;\r
-      }\r
-      FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);\r
-      PathRemoveLastItem(FileStringPath);\r
-      FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);\r
-      Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);\r
-      FreePool(FileStringPath);\r
+      Status = RunScriptFile (FullFileStringPath, NULL, FullFileStringPath, ShellInfoObject.NewShellParametersProtocol);\r
+      FreePool(FullFileStringPath);\r
     }\r
-  }\r
-  if (EFI_ERROR(Status)) {\r
-    NamePath = FileDevicePath (NULL, mStartupScript);\r
-    NewPath = AppendDevicePathNode (ImagePath, NamePath);\r
-    FreePool(NamePath);\r
-\r
+    FreePool (FileStringPath);\r
+  } else {\r
     //\r
-    // Try the location\r
+    // we return success since startup script is not mandatory.\r
     //\r
-    Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
-    FreePool(NewPath);\r
-  }\r
-  //\r
-  // If we got a file, run it\r
-  //\r
-  if (!EFI_ERROR(Status) && FileHandle != NULL) {\r
-    Status = RunScriptFileHandle (FileHandle, mStartupScript);\r
-    ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);\r
-  } else {\r
-    FileStringPath = ShellFindFilePath(mStartupScript);\r
-    if (FileStringPath == NULL) {\r
-      //\r
-      // we return success since we dont need to have a startup script\r
-      //\r
-      Status = EFI_SUCCESS;\r
-      ASSERT(FileHandle == NULL);\r
-    } else {\r
-      Status = RunScriptFile(FileStringPath);\r
-      FreePool(FileStringPath);\r
-    }\r
+    Status = EFI_SUCCESS;\r
   }\r
 \r
-\r
   return (Status);\r
 }\r
 \r
@@ -989,7 +1308,6 @@ DoStartupScript(
   @retval RETURN_ABORTED\r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
 DoShellPrompt (\r
   VOID\r
   )\r
@@ -1000,6 +1318,7 @@ DoShellPrompt (
   CONST CHAR16  *CurDir;\r
   UINTN         BufferSize;\r
   EFI_STATUS    Status;\r
+  LIST_ENTRY    OldBufferList;\r
 \r
   CurDir  = NULL;\r
 \r
@@ -1013,6 +1332,7 @@ DoShellPrompt (
     return EFI_OUT_OF_RESOURCES;\r
   }\r
 \r
+  SaveBufferList(&OldBufferList);\r
   CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
 \r
   //\r
@@ -1035,6 +1355,11 @@ DoShellPrompt (
   // Null terminate the string and parse it\r
   //\r
   if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Reset the CTRL-C event just before running the command (yes we ignore the return values)\r
+    //\r
+    Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);\r
+\r
     CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;\r
     Status = RunCommand(CmdLine);\r
   }\r
@@ -1042,6 +1367,7 @@ DoShellPrompt (
   //\r
   // Done with this command\r
   //\r
+  RestoreBufferList(&OldBufferList);\r
   FreePool (CmdLine);\r
   return Status;\r
 }\r
@@ -1053,8 +1379,7 @@ DoShellPrompt (
   @param Buffer   Something to pass to FreePool when the shell is exiting.\r
 **/\r
 VOID*\r
-EFIAPI\r
-AddBufferToFreeList(\r
+AddBufferToFreeList (\r
   VOID *Buffer\r
   )\r
 {\r
@@ -1064,33 +1389,97 @@ AddBufferToFreeList(
     return (NULL);\r
   }\r
 \r
-  BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST));\r
-  ASSERT(BufferListEntry != NULL);\r
+  BufferListEntry = AllocateZeroPool (sizeof (BUFFER_LIST));\r
+  if (BufferListEntry == NULL) {\r
+    return NULL;\r
+  }\r
+\r
   BufferListEntry->Buffer = Buffer;\r
-  InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);\r
+  InsertTailList (&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);\r
   return (Buffer);\r
 }\r
 \r
+\r
+/**\r
+  Create a new buffer list and stores the old one to OldBufferList\r
+\r
+  @param OldBufferList   The temporary list head used to store the nodes in BufferToFreeList.\r
+**/\r
+VOID\r
+SaveBufferList (\r
+  OUT LIST_ENTRY     *OldBufferList\r
+  )\r
+{\r
+  CopyMem (OldBufferList, &ShellInfoObject.BufferToFreeList.Link, sizeof (LIST_ENTRY));\r
+  InitializeListHead (&ShellInfoObject.BufferToFreeList.Link);\r
+}\r
+\r
+/**\r
+  Restore previous nodes into BufferToFreeList .\r
+\r
+  @param OldBufferList   The temporary list head used to store the nodes in BufferToFreeList.\r
+**/\r
+VOID\r
+RestoreBufferList (\r
+  IN OUT LIST_ENTRY     *OldBufferList\r
+  )\r
+{\r
+  FreeBufferList (&ShellInfoObject.BufferToFreeList);\r
+  CopyMem (&ShellInfoObject.BufferToFreeList.Link, OldBufferList, sizeof (LIST_ENTRY));\r
+}\r
+\r
+\r
 /**\r
   Add a buffer to the Line History List\r
 \r
   @param Buffer     The line buffer to add.\r
 **/\r
 VOID\r
-EFIAPI\r
 AddLineToCommandHistory(\r
   IN CONST CHAR16 *Buffer\r
   )\r
 {\r
   BUFFER_LIST *Node;\r
+  BUFFER_LIST *Walker;\r
+  UINT16       MaxHistoryCmdCount;\r
+  UINT16       Count;\r
+\r
+  Count = 0;\r
+  MaxHistoryCmdCount = PcdGet16(PcdShellMaxHistoryCommandCount);\r
+\r
+  if (MaxHistoryCmdCount == 0) {\r
+    return ;\r
+  }\r
+\r
 \r
   Node = AllocateZeroPool(sizeof(BUFFER_LIST));\r
-  ASSERT(Node != NULL);\r
-  Node->Buffer = AllocateZeroPool(StrSize(Buffer));\r
-  ASSERT(Node->Buffer != NULL);\r
-  StrCpy(Node->Buffer, Buffer);\r
+  if (Node == NULL) {\r
+    return;\r
+  }\r
+\r
+  Node->Buffer = AllocateCopyPool (StrSize (Buffer), Buffer);\r
+  if (Node->Buffer == NULL) {\r
+    FreePool (Node);\r
+    return;\r
+  }\r
 \r
-  InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);\r
+  for ( Walker = (BUFFER_LIST*)GetFirstNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link)\r
+      ; !IsNull(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)\r
+      ; Walker = (BUFFER_LIST*)GetNextNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)\r
+   ){\r
+    Count++;\r
+  }\r
+  if (Count < MaxHistoryCmdCount){\r
+    InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);\r
+  } else {\r
+    Walker = (BUFFER_LIST*)GetFirstNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link);\r
+    RemoveEntryList(&Walker->Link);\r
+    if (Walker->Buffer != NULL) {\r
+      FreePool(Walker->Buffer);\r
+    }\r
+    FreePool(Walker);\r
+    InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);\r
+  }\r
 }\r
 \r
 /**\r
@@ -1107,7 +1496,6 @@ AddLineToCommandHistory(
   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.\r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
 ShellConvertAlias(\r
   IN OUT CHAR16 **CommandString\r
   )\r
@@ -1119,39 +1507,102 @@ ShellConvertAlias(
     return (EFI_SUCCESS);\r
   }\r
   FreePool(*CommandString);\r
-  *CommandString = AllocateZeroPool(StrSize(NewString));\r
+  *CommandString = AllocateCopyPool(StrSize(NewString), NewString);\r
   if (*CommandString == NULL) {\r
     return (EFI_OUT_OF_RESOURCES);\r
   }\r
-  StrCpy(*CommandString, NewString);\r
   return (EFI_SUCCESS);\r
 }\r
 \r
 /**\r
-  Function allocates a new command line and replaces all instances of environment\r
-  variable names that are correctly preset to their values.\r
-\r
-  If the return value is not NULL the memory must be caller freed.\r
-\r
-  @param[in] OriginalCommandLine    The original command line\r
+  This function will eliminate unreplaced (and therefore non-found) environment variables.\r
 \r
-  @retval NULL                      An error ocurred.\r
-  @return                           The new command line with no environment variables present.\r
+  @param[in,out] CmdLine   The command line to update.\r
 **/\r
-CHAR16*\r
-EFIAPI\r
-ShellConvertVariables (\r
-  IN CONST CHAR16 *OriginalCommandLine\r
+EFI_STATUS\r
+StripUnreplacedEnvironmentVariables(\r
+  IN OUT CHAR16 *CmdLine\r
   )\r
 {\r
-  CONST CHAR16        *MasterEnvList;\r
-  UINTN               NewSize;\r
-  CHAR16              *NewCommandLine1;\r
-  CHAR16              *NewCommandLine2;\r
-  CHAR16              *Temp;\r
-  CHAR16              *Temp2;\r
-  UINTN               ItemSize;\r
-  CHAR16              *ItemTemp;\r
+  CHAR16 *FirstPercent;\r
+  CHAR16 *FirstQuote;\r
+  CHAR16 *SecondPercent;\r
+  CHAR16 *SecondQuote;\r
+  CHAR16 *CurrentLocator;\r
+\r
+  for (CurrentLocator = CmdLine ; CurrentLocator != NULL ; ) {\r
+    FirstQuote = FindNextInstance(CurrentLocator, L"\"", TRUE);\r
+    FirstPercent = FindNextInstance(CurrentLocator, L"%", TRUE);\r
+    SecondPercent = FirstPercent!=NULL?FindNextInstance(FirstPercent+1, L"%", TRUE):NULL;\r
+    if (FirstPercent == NULL || SecondPercent == NULL) {\r
+      //\r
+      // If we ever don't have 2 % we are done.\r
+      //\r
+      break;\r
+    }\r
+\r
+    if (FirstQuote!= NULL && FirstQuote < FirstPercent) {\r
+      SecondQuote = FindNextInstance(FirstQuote+1, L"\"", TRUE);\r
+      //\r
+      // Quote is first found\r
+      //\r
+\r
+      if (SecondQuote < FirstPercent) {\r
+        //\r
+        // restart after the pair of "\r
+        //\r
+        CurrentLocator = SecondQuote + 1;\r
+      } else /* FirstPercent < SecondQuote */{\r
+        //\r
+        // Restart on the first percent\r
+        //\r
+        CurrentLocator = FirstPercent;\r
+      }\r
+      continue;\r
+    }\r
+\r
+    if (FirstQuote == NULL || SecondPercent < FirstQuote) {\r
+      if (IsValidEnvironmentVariableName(FirstPercent, SecondPercent)) {\r
+        //\r
+        // We need to remove from FirstPercent to SecondPercent\r
+        //\r
+        CopyMem(FirstPercent, SecondPercent + 1, StrSize(SecondPercent + 1));\r
+        //\r
+        // don't need to update the locator.  both % characters are gone.\r
+        //\r
+      } else {\r
+        CurrentLocator = SecondPercent + 1;\r
+      }\r
+      continue;\r
+    }\r
+    CurrentLocator = FirstQuote;\r
+  }\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+  Function allocates a new command line and replaces all instances of environment\r
+  variable names that are correctly preset to their values.\r
+\r
+  If the return value is not NULL the memory must be caller freed.\r
+\r
+  @param[in] OriginalCommandLine    The original command line\r
+\r
+  @retval NULL                      An error occurred.\r
+  @return                           The new command line with no environment variables present.\r
+**/\r
+CHAR16*\r
+ShellConvertVariables (\r
+  IN CONST CHAR16 *OriginalCommandLine\r
+  )\r
+{\r
+  CONST CHAR16        *MasterEnvList;\r
+  UINTN               NewSize;\r
+  CHAR16              *NewCommandLine1;\r
+  CHAR16              *NewCommandLine2;\r
+  CHAR16              *Temp;\r
+  UINTN               ItemSize;\r
+  CHAR16              *ItemTemp;\r
   SCRIPT_FILE         *CurrentScriptFile;\r
   ALIAS_LIST          *AliasListNode;\r
 \r
@@ -1177,7 +1628,7 @@ ShellConvertVariables (
         ;  Temp = StrStr(Temp+1, AliasListNode->Alias)\r
        ){\r
         //\r
-        // we need a preceeding and if there is space no ^ preceeding (if no space ignore)\r
+        // we need a preceding and if there is space no ^ preceding (if no space ignore)\r
         //\r
         if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {\r
           NewSize += StrSize(AliasListNode->CommandString);\r
@@ -1198,7 +1649,7 @@ ShellConvertVariables (
       ;  Temp = StrStr(Temp+1, MasterEnvList)\r
      ){\r
       //\r
-      // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)\r
+      // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)\r
       //\r
       if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&\r
         ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {\r
@@ -1210,7 +1661,7 @@ ShellConvertVariables (
   //\r
   // now do the replacements...\r
   //\r
-  NewCommandLine1 = AllocateZeroPool(NewSize);\r
+  NewCommandLine1 = AllocateZeroPool (NewSize);\r
   NewCommandLine2 = AllocateZeroPool(NewSize);\r
   ItemTemp        = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));\r
   if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {\r
@@ -1219,16 +1670,26 @@ ShellConvertVariables (
     SHELL_FREE_NON_NULL(ItemTemp);\r
     return (NULL);\r
   }\r
-  StrCpy(NewCommandLine1, OriginalCommandLine);\r
+  CopyMem (NewCommandLine1, OriginalCommandLine, StrSize (OriginalCommandLine));\r
+\r
   for (MasterEnvList = EfiShellGetEnv(NULL)\r
-    ;  MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL\r
+    ;  MasterEnvList != NULL && *MasterEnvList != CHAR_NULL\r
     ;  MasterEnvList += StrLen(MasterEnvList) + 1\r
    ){\r
-    StrCpy(ItemTemp, L"%");\r
-    StrCat(ItemTemp, MasterEnvList);\r
-    StrCat(ItemTemp, L"%");\r
+    StrCpyS( ItemTemp,\r
+              ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),\r
+              L"%"\r
+              );\r
+    StrCatS( ItemTemp,\r
+              ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),\r
+              MasterEnvList\r
+              );\r
+    StrCatS( ItemTemp,\r
+              ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),\r
+              L"%"\r
+              );\r
     ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);\r
-    StrCpy(NewCommandLine1, NewCommandLine2);\r
+    StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);\r
   }\r
   if (CurrentScriptFile != NULL) {\r
     for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)\r
@@ -1236,53 +1697,21 @@ ShellConvertVariables (
       ;  AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
    ){\r
     ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);\r
-    StrCpy(NewCommandLine1, NewCommandLine2);\r
-    }\r
-\r
-    //\r
-    // Remove non-existant environment variables in scripts only\r
-    //\r
-    for (Temp = NewCommandLine1 ; Temp != NULL ; ) {\r
-      Temp = StrStr(Temp, L"%");\r
-      if (Temp == NULL) {\r
-        break;\r
-      }\r
-      while (*(Temp - 1) == L'^') {\r
-        Temp = StrStr(Temp + 1, L"%");\r
-        if (Temp == NULL) {\r
-          break;\r
-       }\r
-      }\r
-      if (Temp == NULL) {\r
-        break;\r
-      }\r
-      \r
-      Temp2 = StrStr(Temp + 1, L"%");\r
-      if (Temp2 == NULL) {\r
-        break;\r
-      }\r
-      while (*(Temp2 - 1) == L'^') {\r
-        Temp2 = StrStr(Temp2 + 1, L"%");\r
-        if (Temp2 == NULL) {\r
-          break;\r
-        }\r
-      }\r
-      if (Temp2 == NULL) {\r
-        break;\r
-      }\r
-      \r
-      Temp2++;\r
-      CopyMem(Temp, Temp2, StrSize(Temp2));\r
+    StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);\r
     }\r
-\r
   }\r
 \r
+  //\r
+  // Remove non-existent environment variables\r
+  //\r
+  StripUnreplacedEnvironmentVariables(NewCommandLine1);\r
+\r
   //\r
   // Now cleanup any straggler intentionally ignored "%" characters\r
   //\r
   ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);\r
-  StrCpy(NewCommandLine1, NewCommandLine2);\r
-  \r
+  StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);\r
+\r
   FreePool(NewCommandLine2);\r
   FreePool(ItemTemp);\r
 \r
@@ -1300,11 +1729,10 @@ ShellConvertVariables (
   @retval other             Some error occurs when executing the split command.\r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
 RunSplitCommand(\r
   IN CONST CHAR16             *CmdLine,\r
-  IN       SHELL_FILE_HANDLE  *StdIn,\r
-  IN       SHELL_FILE_HANDLE  *StdOut\r
+  IN       SHELL_FILE_HANDLE  StdIn,\r
+  IN       SHELL_FILE_HANDLE  StdOut\r
   )\r
 {\r
   EFI_STATUS        Status;\r
@@ -1313,88 +1741,876 @@ RunSplitCommand(
   UINTN             Size1;\r
   UINTN             Size2;\r
   SPLIT_LIST        *Split;\r
-  SHELL_FILE_HANDLE *TempFileHandle;\r
+  SHELL_FILE_HANDLE TempFileHandle;\r
   BOOLEAN           Unicode;\r
 \r
   ASSERT(StdOut == NULL);\r
 \r
   ASSERT(StrStr(CmdLine, L"|") != NULL);\r
 \r
-  Status          = EFI_SUCCESS;\r
-  NextCommandLine = NULL;\r
-  OurCommandLine  = NULL;\r
-  Size1           = 0;\r
-  Size2           = 0;\r
+  Status          = EFI_SUCCESS;\r
+  NextCommandLine = NULL;\r
+  OurCommandLine  = NULL;\r
+  Size1           = 0;\r
+  Size2           = 0;\r
+\r
+  NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);\r
+  OurCommandLine  = StrnCatGrow(&OurCommandLine , &Size2, CmdLine                , StrStr(CmdLine, L"|") - CmdLine);\r
+\r
+  if (NextCommandLine == NULL || OurCommandLine == NULL) {\r
+    SHELL_FREE_NON_NULL(OurCommandLine);\r
+    SHELL_FREE_NON_NULL(NextCommandLine);\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  } else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) {\r
+    SHELL_FREE_NON_NULL(OurCommandLine);\r
+    SHELL_FREE_NON_NULL(NextCommandLine);\r
+    return (EFI_INVALID_PARAMETER);\r
+  } else if (NextCommandLine[0] == L'a' &&\r
+             (NextCommandLine[1] == L' ' || NextCommandLine[1] == CHAR_NULL)\r
+            ){\r
+    CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));\r
+    while (NextCommandLine[0] == L' ') {\r
+      CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));\r
+    }\r
+    if (NextCommandLine[0] == CHAR_NULL) {\r
+      SHELL_FREE_NON_NULL(OurCommandLine);\r
+      SHELL_FREE_NON_NULL(NextCommandLine);\r
+      return (EFI_INVALID_PARAMETER);\r
+    }\r
+    Unicode = FALSE;\r
+  } else {\r
+    Unicode = TRUE;\r
+  }\r
+\r
+\r
+  //\r
+  // make a SPLIT_LIST item and add to list\r
+  //\r
+  Split = AllocateZeroPool(sizeof(SPLIT_LIST));\r
+  if (Split == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  Split->SplitStdIn   = StdIn;\r
+  Split->SplitStdOut  = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);\r
+  ASSERT(Split->SplitStdOut != NULL);\r
+  InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);\r
+\r
+  Status = RunCommand(OurCommandLine);\r
+\r
+  //\r
+  // move the output from the first to the in to the second.\r
+  //\r
+  TempFileHandle      = Split->SplitStdOut;\r
+  if (Split->SplitStdIn == StdIn) {\r
+    Split->SplitStdOut = NULL;\r
+  } else {\r
+    Split->SplitStdOut  = Split->SplitStdIn;\r
+  }\r
+  Split->SplitStdIn   = TempFileHandle;\r
+  ShellInfoObject.NewEfiShellProtocol->SetFilePosition (Split->SplitStdIn, 0);\r
+\r
+  if (!EFI_ERROR(Status)) {\r
+    Status = RunCommand(NextCommandLine);\r
+  }\r
+\r
+  //\r
+  // remove the top level from the ScriptList\r
+  //\r
+  ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);\r
+  RemoveEntryList(&Split->Link);\r
+\r
+  //\r
+  // Note that the original StdIn is now the StdOut...\r
+  //\r
+  if (Split->SplitStdOut != NULL) {\r
+    ShellInfoObject.NewEfiShellProtocol->CloseFile (Split->SplitStdOut);\r
+  }\r
+  if (Split->SplitStdIn != NULL) {\r
+    ShellInfoObject.NewEfiShellProtocol->CloseFile (Split->SplitStdIn);\r
+  }\r
+\r
+  FreePool(Split);\r
+  FreePool(NextCommandLine);\r
+  FreePool(OurCommandLine);\r
+\r
+  return (Status);\r
+}\r
+\r
+/**\r
+  Take the original command line, substitute any variables, free\r
+  the original string, return the modified copy.\r
+\r
+  @param[in] CmdLine  pointer to the command line to update.\r
+\r
+  @retval EFI_SUCCESS           the function was successful.\r
+  @retval EFI_OUT_OF_RESOURCES  a memory allocation failed.\r
+**/\r
+EFI_STATUS\r
+ShellSubstituteVariables(\r
+  IN CHAR16 **CmdLine\r
+  )\r
+{\r
+  CHAR16      *NewCmdLine;\r
+  NewCmdLine = ShellConvertVariables(*CmdLine);\r
+  SHELL_FREE_NON_NULL(*CmdLine);\r
+  if (NewCmdLine == NULL) {\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  }\r
+  *CmdLine = NewCmdLine;\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+  Take the original command line, substitute any alias in the first group of space delimited characters, free\r
+  the original string, return the modified copy.\r
+\r
+  @param[in] CmdLine  pointer to the command line to update.\r
+\r
+  @retval EFI_SUCCESS           the function was successful.\r
+  @retval EFI_OUT_OF_RESOURCES  a memory allocation failed.\r
+**/\r
+EFI_STATUS\r
+ShellSubstituteAliases(\r
+  IN CHAR16 **CmdLine\r
+  )\r
+{\r
+  CHAR16      *NewCmdLine;\r
+  CHAR16      *CommandName;\r
+  EFI_STATUS  Status;\r
+  UINTN       PostAliasSize;\r
+  ASSERT(CmdLine != NULL);\r
+  ASSERT(*CmdLine!= NULL);\r
+\r
+\r
+  CommandName = NULL;\r
+  if (StrStr((*CmdLine), L" ") == NULL){\r
+    StrnCatGrow(&CommandName, NULL, (*CmdLine), 0);\r
+  } else {\r
+    StrnCatGrow(&CommandName, NULL, (*CmdLine), StrStr((*CmdLine), L" ") - (*CmdLine));\r
+  }\r
+\r
+  //\r
+  // This cannot happen 'inline' since the CmdLine can need extra space.\r
+  //\r
+  NewCmdLine = NULL;\r
+  if (!ShellCommandIsCommandOnList(CommandName)) {\r
+    //\r
+    // Convert via alias\r
+    //\r
+    Status = ShellConvertAlias(&CommandName);\r
+    if (EFI_ERROR(Status)){\r
+      return (Status);\r
+    }\r
+    PostAliasSize = 0;\r
+    NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, CommandName, 0);\r
+    if (NewCmdLine == NULL) {\r
+      SHELL_FREE_NON_NULL(CommandName);\r
+      SHELL_FREE_NON_NULL(*CmdLine);\r
+      return (EFI_OUT_OF_RESOURCES);\r
+    }\r
+    NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, StrStr((*CmdLine), L" "), 0);\r
+    if (NewCmdLine == NULL) {\r
+      SHELL_FREE_NON_NULL(CommandName);\r
+      SHELL_FREE_NON_NULL(*CmdLine);\r
+      return (EFI_OUT_OF_RESOURCES);\r
+    }\r
+  } else {\r
+    NewCmdLine = StrnCatGrow(&NewCmdLine, NULL, (*CmdLine), 0);\r
+  }\r
+\r
+  SHELL_FREE_NON_NULL(*CmdLine);\r
+  SHELL_FREE_NON_NULL(CommandName);\r
+\r
+  //\r
+  // re-assign the passed in double pointer to point to our newly allocated buffer\r
+  //\r
+  *CmdLine = NewCmdLine;\r
+\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+  Takes the Argv[0] part of the command line and determine the meaning of it.\r
+\r
+  @param[in] CmdName  pointer to the command line to update.\r
+\r
+  @retval Internal_Command    The name is an internal command.\r
+  @retval File_Sys_Change     the name is a file system change.\r
+  @retval Script_File_Name    the name is a NSH script file.\r
+  @retval Unknown_Invalid     the name is unknown.\r
+  @retval Efi_Application     the name is an application (.EFI).\r
+**/\r
+SHELL_OPERATION_TYPES\r
+GetOperationType(\r
+  IN CONST CHAR16 *CmdName\r
+  )\r
+{\r
+        CHAR16* FileWithPath;\r
+  CONST CHAR16* TempLocation;\r
+  CONST CHAR16* TempLocation2;\r
+\r
+  FileWithPath = NULL;\r
+  //\r
+  // test for an internal command.\r
+  //\r
+  if (ShellCommandIsCommandOnList(CmdName)) {\r
+    return (Internal_Command);\r
+  }\r
+\r
+  //\r
+  // Test for file system change request.  anything ending with first : and cant have spaces.\r
+  //\r
+  if (CmdName[(StrLen(CmdName)-1)] == L':') {\r
+    if ( StrStr(CmdName, L" ") != NULL\r
+      || StrLen(StrStr(CmdName, L":")) > 1\r
+      ) {\r
+      return (Unknown_Invalid);\r
+    }\r
+    return (File_Sys_Change);\r
+  }\r
+\r
+  //\r
+  // Test for a file\r
+  //\r
+  if ((FileWithPath = ShellFindFilePathEx(CmdName, mExecutableExtensions)) != NULL) {\r
+    //\r
+    // See if that file has a script file extension\r
+    //\r
+    if (StrLen(FileWithPath) > 4) {\r
+      TempLocation = FileWithPath+StrLen(FileWithPath)-4;\r
+      TempLocation2 = mScriptExtension;\r
+      if (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0) {\r
+        SHELL_FREE_NON_NULL(FileWithPath);\r
+        return (Script_File_Name);\r
+      }\r
+    }\r
+\r
+    //\r
+    // Was a file, but not a script.  we treat this as an application.\r
+    //\r
+    SHELL_FREE_NON_NULL(FileWithPath);\r
+    return (Efi_Application);\r
+  }\r
+\r
+  SHELL_FREE_NON_NULL(FileWithPath);\r
+  //\r
+  // No clue what this is... return invalid flag...\r
+  //\r
+  return (Unknown_Invalid);\r
+}\r
+\r
+/**\r
+  Determine if the first item in a command line is valid.\r
+\r
+  @param[in] CmdLine            The command line to parse.\r
+\r
+  @retval EFI_SUCCESS           The item is valid.\r
+  @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.\r
+  @retval EFI_NOT_FOUND         The operation type is unknown or invalid.\r
+**/\r
+EFI_STATUS\r
+IsValidSplit(\r
+  IN CONST CHAR16 *CmdLine\r
+  )\r
+{\r
+  CHAR16        *Temp;\r
+  CHAR16        *FirstParameter;\r
+  CHAR16        *TempWalker;\r
+  EFI_STATUS    Status;\r
+\r
+  Temp           = NULL;\r
+\r
+  Temp = StrnCatGrow(&Temp, NULL, CmdLine, 0);\r
+  if (Temp == NULL) {\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  }\r
+\r
+  FirstParameter = StrStr(Temp, L"|");\r
+  if (FirstParameter != NULL) {\r
+    *FirstParameter = CHAR_NULL;\r
+  }\r
+\r
+  FirstParameter = NULL;\r
+\r
+  //\r
+  // Process the command line\r
+  //\r
+  Status = ProcessCommandLineToFinal(&Temp);\r
+\r
+  if (!EFI_ERROR(Status)) {\r
+    FirstParameter = AllocateZeroPool(StrSize(CmdLine));\r
+    if (FirstParameter == NULL) {\r
+      SHELL_FREE_NON_NULL(Temp);\r
+      return (EFI_OUT_OF_RESOURCES);\r
+    }\r
+    TempWalker = (CHAR16*)Temp;\r
+    if (!EFI_ERROR(GetNextParameter(&TempWalker, &FirstParameter, StrSize(CmdLine), TRUE))) {\r
+      if (GetOperationType(FirstParameter) == Unknown_Invalid) {\r
+        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
+        SetLastError(SHELL_NOT_FOUND);\r
+        Status = EFI_NOT_FOUND;\r
+      }\r
+    }\r
+  }\r
+\r
+  SHELL_FREE_NON_NULL(Temp);\r
+  SHELL_FREE_NON_NULL(FirstParameter);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Determine if a command line contains with a split contains only valid commands.\r
+\r
+  @param[in] CmdLine      The command line to parse.\r
+\r
+  @retval EFI_SUCCESS     CmdLine has only valid commands, application, or has no split.\r
+  @retval EFI_ABORTED     CmdLine has at least one invalid command or application.\r
+**/\r
+EFI_STATUS\r
+VerifySplit(\r
+  IN CONST CHAR16 *CmdLine\r
+  )\r
+{\r
+  CONST CHAR16  *TempSpot;\r
+  EFI_STATUS    Status;\r
+\r
+  //\r
+  // If this was the only item, then get out\r
+  //\r
+  if (!ContainsSplit(CmdLine)) {\r
+    return (EFI_SUCCESS);\r
+  }\r
+\r
+  //\r
+  // Verify up to the pipe or end character\r
+  //\r
+  Status = IsValidSplit(CmdLine);\r
+  if (EFI_ERROR(Status)) {\r
+    return (Status);\r
+  }\r
+\r
+  //\r
+  // recurse to verify the next item\r
+  //\r
+  TempSpot = FindFirstCharacter(CmdLine, L"|", L'^') + 1;\r
+  if (*TempSpot == L'a' &&\r
+      (*(TempSpot + 1) == L' ' || *(TempSpot + 1) == CHAR_NULL)\r
+     ) {\r
+    // If it's an ASCII pipe '|a'\r
+    TempSpot += 1;\r
+  }\r
+\r
+  return (VerifySplit(TempSpot));\r
+}\r
+\r
+/**\r
+  Process a split based operation.\r
+\r
+  @param[in] CmdLine    pointer to the command line to process\r
+\r
+  @retval EFI_SUCCESS   The operation was successful\r
+  @return               an error occurred.\r
+**/\r
+EFI_STATUS\r
+ProcessNewSplitCommandLine(\r
+  IN CONST CHAR16 *CmdLine\r
+  )\r
+{\r
+  SPLIT_LIST                *Split;\r
+  EFI_STATUS                Status;\r
+\r
+  Status = VerifySplit(CmdLine);\r
+  if (EFI_ERROR(Status)) {\r
+    return (Status);\r
+  }\r
+\r
+  Split = NULL;\r
+\r
+  //\r
+  // are we in an existing split???\r
+  //\r
+  if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {\r
+    Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);\r
+  }\r
+\r
+  if (Split == NULL) {\r
+    Status = RunSplitCommand(CmdLine, NULL, NULL);\r
+  } else {\r
+    Status = RunSplitCommand(CmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
+  }\r
+  if (EFI_ERROR(Status)) {\r
+    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);\r
+  }\r
+  return (Status);\r
+}\r
+\r
+/**\r
+  Handle a request to change the current file system.\r
+\r
+  @param[in] CmdLine  The passed in command line.\r
+\r
+  @retval EFI_SUCCESS The operation was successful.\r
+**/\r
+EFI_STATUS\r
+ChangeMappedDrive(\r
+  IN CONST CHAR16 *CmdLine\r
+  )\r
+{\r
+  EFI_STATUS Status;\r
+  Status = EFI_SUCCESS;\r
+\r
+  //\r
+  // make sure we are the right operation\r
+  //\r
+  ASSERT(CmdLine[(StrLen(CmdLine)-1)] == L':' && StrStr(CmdLine, L" ") == NULL);\r
+\r
+  //\r
+  // Call the protocol API to do the work\r
+  //\r
+  Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, CmdLine);\r
+\r
+  //\r
+  // Report any errors\r
+  //\r
+  if (EFI_ERROR(Status)) {\r
+    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine);\r
+  }\r
+\r
+  return (Status);\r
+}\r
+\r
+/**\r
+  Reprocess the command line to direct all -? to the help command.\r
+\r
+  if found, will add "help" as argv[0], and move the rest later.\r
+\r
+  @param[in,out] CmdLine        pointer to the command line to update\r
+**/\r
+EFI_STATUS\r
+DoHelpUpdate(\r
+  IN OUT CHAR16 **CmdLine\r
+  )\r
+{\r
+  CHAR16 *CurrentParameter;\r
+  CHAR16 *Walker;\r
+  CHAR16 *NewCommandLine;\r
+  EFI_STATUS Status;\r
+  UINTN  NewCmdLineSize;\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  CurrentParameter = AllocateZeroPool(StrSize(*CmdLine));\r
+  if (CurrentParameter == NULL) {\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  }\r
+\r
+  Walker = *CmdLine;\r
+  while(Walker != NULL && *Walker != CHAR_NULL) {\r
+    if (!EFI_ERROR(GetNextParameter(&Walker, &CurrentParameter, StrSize(*CmdLine), TRUE))) {\r
+      if (StrStr(CurrentParameter, L"-?") == CurrentParameter) {\r
+        CurrentParameter[0] = L' ';\r
+        CurrentParameter[1] = L' ';\r
+        NewCmdLineSize = StrSize(L"help ") + StrSize(*CmdLine);\r
+        NewCommandLine = AllocateZeroPool(NewCmdLineSize);\r
+        if (NewCommandLine == NULL) {\r
+          Status = EFI_OUT_OF_RESOURCES;\r
+          break;\r
+        }\r
+\r
+        //\r
+        // We know the space is sufficient since we just calculated it.\r
+        //\r
+        StrnCpyS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), L"help ", 5);\r
+        StrnCatS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), *CmdLine, StrLen(*CmdLine));\r
+        SHELL_FREE_NON_NULL(*CmdLine);\r
+        *CmdLine = NewCommandLine;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+\r
+  SHELL_FREE_NON_NULL(CurrentParameter);\r
+\r
+  return (Status);\r
+}\r
+\r
+/**\r
+  Function to update the shell variable "lasterror".\r
+\r
+  @param[in] ErrorCode      the error code to put into lasterror.\r
+**/\r
+EFI_STATUS\r
+SetLastError(\r
+  IN CONST SHELL_STATUS   ErrorCode\r
+  )\r
+{\r
+  CHAR16 LeString[19];\r
+  if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
+    UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ErrorCode);\r
+  } else {\r
+    UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ErrorCode);\r
+  }\r
+  DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
+  InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
+\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+  Converts the command line to it's post-processed form.  this replaces variables and alias' per UEFI Shell spec.\r
+\r
+  @param[in,out] CmdLine        pointer to the command line to update\r
+\r
+  @retval EFI_SUCCESS           The operation was successful\r
+  @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.\r
+  @return                       some other error occurred\r
+**/\r
+EFI_STATUS\r
+ProcessCommandLineToFinal(\r
+  IN OUT CHAR16 **CmdLine\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  TrimSpaces(CmdLine);\r
+\r
+  Status = ShellSubstituteAliases(CmdLine);\r
+  if (EFI_ERROR(Status)) {\r
+    return (Status);\r
+  }\r
+\r
+  TrimSpaces(CmdLine);\r
+\r
+  Status = ShellSubstituteVariables(CmdLine);\r
+  if (EFI_ERROR(Status)) {\r
+    return (Status);\r
+  }\r
+  ASSERT (*CmdLine != NULL);\r
+\r
+  TrimSpaces(CmdLine);\r
+\r
+  //\r
+  // update for help parsing\r
+  //\r
+  if (StrStr(*CmdLine, L"?") != NULL) {\r
+    //\r
+    // This may do nothing if the ? does not indicate help.\r
+    // Save all the details for in the API below.\r
+    //\r
+    Status = DoHelpUpdate(CmdLine);\r
+  }\r
+\r
+  TrimSpaces(CmdLine);\r
+\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+  Run an internal shell command.\r
+\r
+  This API will update the shell's environment since these commands are libraries.\r
+\r
+  @param[in] CmdLine          the command line to run.\r
+  @param[in] FirstParameter   the first parameter on the command line\r
+  @param[in] ParamProtocol    the shell parameters protocol pointer\r
+  @param[out] CommandStatus   the status from the command line.\r
+\r
+  @retval EFI_SUCCESS     The command was completed.\r
+  @retval EFI_ABORTED     The command's operation was aborted.\r
+**/\r
+EFI_STATUS\r
+RunInternalCommand(\r
+  IN CONST CHAR16                   *CmdLine,\r
+  IN       CHAR16                   *FirstParameter,\r
+  IN EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,\r
+  OUT EFI_STATUS                    *CommandStatus\r
+)\r
+{\r
+  EFI_STATUS                Status;\r
+  UINTN                     Argc;\r
+  CHAR16                    **Argv;\r
+  SHELL_STATUS              CommandReturnedStatus;\r
+  BOOLEAN                   LastError;\r
+  CHAR16                    *Walker;\r
+  CHAR16                    *NewCmdLine;\r
+\r
+  NewCmdLine = AllocateCopyPool (StrSize (CmdLine), CmdLine);\r
+  if (NewCmdLine == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL ; Walker++) {\r
+    if (*Walker == L'^' && *(Walker+1) == L'#') {\r
+      CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0]));\r
+    }\r
+  }\r
+\r
+  //\r
+  // get the argc and argv updated for internal commands\r
+  //\r
+  Status = UpdateArgcArgv(ParamProtocol, NewCmdLine, Internal_Command, &Argv, &Argc);\r
+  if (!EFI_ERROR(Status)) {\r
+    //\r
+    // Run the internal command.\r
+    //\r
+    Status = ShellCommandRunCommandHandler(FirstParameter, &CommandReturnedStatus, &LastError);\r
+\r
+    if (!EFI_ERROR(Status)) {\r
+      if (CommandStatus != NULL) {\r
+        if (CommandReturnedStatus != SHELL_SUCCESS) {\r
+          *CommandStatus = (EFI_STATUS)(CommandReturnedStatus | MAX_BIT);\r
+        } else {\r
+          *CommandStatus = EFI_SUCCESS;\r
+        }\r
+      }\r
+\r
+      //\r
+      // Update last error status.\r
+      // some commands do not update last error.\r
+      //\r
+      if (LastError) {\r
+        SetLastError(CommandReturnedStatus);\r
+      }\r
+\r
+      //\r
+      // Pass thru the exitcode from the app.\r
+      //\r
+      if (ShellCommandGetExit()) {\r
+        //\r
+        // An Exit was requested ("exit" command), pass its value up.\r
+        //\r
+        Status = CommandReturnedStatus;\r
+      } else if (CommandReturnedStatus != SHELL_SUCCESS && IsScriptOnlyCommand(FirstParameter)) {\r
+        //\r
+        // Always abort when a script only command fails for any reason\r
+        //\r
+        Status = EFI_ABORTED;\r
+      } else if (ShellCommandGetCurrentScriptFile() != NULL && CommandReturnedStatus == SHELL_ABORTED) {\r
+        //\r
+        // Abort when in a script and a command aborted\r
+        //\r
+        Status = EFI_ABORTED;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.\r
+  // This is safe even if the update API failed.  In this case, it may be a no-op.\r
+  //\r
+  RestoreArgcArgv(ParamProtocol, &Argv, &Argc);\r
+\r
+  //\r
+  // If a script is running and the command is not a script only command, then\r
+  // change return value to success so the script won't halt (unless aborted).\r
+  //\r
+  // Script only commands have to be able halt the script since the script will\r
+  // not operate if they are failing.\r
+  //\r
+  if ( ShellCommandGetCurrentScriptFile() != NULL\r
+    && !IsScriptOnlyCommand(FirstParameter)\r
+    && Status != EFI_ABORTED\r
+    ) {\r
+    Status = EFI_SUCCESS;\r
+  }\r
+\r
+  FreePool (NewCmdLine);\r
+  return (Status);\r
+}\r
+\r
+/**\r
+  Function to run the command or file.\r
+\r
+  @param[in] Type             the type of operation being run.\r
+  @param[in] CmdLine          the command line to run.\r
+  @param[in] FirstParameter   the first parameter on the command line\r
+  @param[in] ParamProtocol    the shell parameters protocol pointer\r
+  @param[out] CommandStatus   the status from the command line.\r
+\r
+  @retval EFI_SUCCESS     The command was completed.\r
+  @retval EFI_ABORTED     The command's operation was aborted.\r
+**/\r
+EFI_STATUS\r
+RunCommandOrFile(\r
+  IN       SHELL_OPERATION_TYPES    Type,\r
+  IN CONST CHAR16                   *CmdLine,\r
+  IN       CHAR16                   *FirstParameter,\r
+  IN EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,\r
+  OUT EFI_STATUS                    *CommandStatus\r
+)\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_STATUS                StartStatus;\r
+  CHAR16                    *CommandWithPath;\r
+  CHAR16                    *FullCommandWithPath;\r
+  EFI_DEVICE_PATH_PROTOCOL  *DevPath;\r
+  SHELL_STATUS              CalleeExitStatus;\r
+\r
+  Status            = EFI_SUCCESS;\r
+  CommandWithPath   = NULL;\r
+  DevPath           = NULL;\r
+  CalleeExitStatus  = SHELL_INVALID_PARAMETER;\r
+\r
+  switch (Type) {\r
+    case   Internal_Command:\r
+      Status = RunInternalCommand(CmdLine, FirstParameter, ParamProtocol, CommandStatus);\r
+      break;\r
+    case   Script_File_Name:\r
+    case   Efi_Application:\r
+      //\r
+      // Process a fully qualified path\r
+      //\r
+      if (StrStr(FirstParameter, L":") != NULL) {\r
+        ASSERT (CommandWithPath == NULL);\r
+        if (ShellIsFile(FirstParameter) == EFI_SUCCESS) {\r
+          CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, FirstParameter, 0);\r
+        }\r
+      }\r
+\r
+      //\r
+      // Process a relative path and also check in the path environment variable\r
+      //\r
+      if (CommandWithPath == NULL) {\r
+        CommandWithPath = ShellFindFilePathEx(FirstParameter, mExecutableExtensions);\r
+      }\r
+\r
+      //\r
+      // This should be impossible now.\r
+      //\r
+      ASSERT(CommandWithPath != NULL);\r
+\r
+      //\r
+      // Make sure that path is not just a directory (or not found)\r
+      //\r
+      if (!EFI_ERROR(ShellIsDirectory(CommandWithPath))) {\r
+        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
+        SetLastError(SHELL_NOT_FOUND);\r
+      }\r
+      switch (Type) {\r
+        case   Script_File_Name:\r
+          FullCommandWithPath = FullyQualifyPath(CommandWithPath);\r
+          if (FullCommandWithPath == NULL) {\r
+            Status = RunScriptFile (CommandWithPath, NULL, CmdLine, ParamProtocol);\r
+          } else {\r
+            Status = RunScriptFile (FullCommandWithPath, NULL, CmdLine, ParamProtocol);\r
+            FreePool(FullCommandWithPath);\r
+          }\r
+          break;\r
+        case   Efi_Application:\r
+          //\r
+          // Get the device path of the application image\r
+          //\r
+          DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
+          if (DevPath == NULL){\r
+            Status = EFI_OUT_OF_RESOURCES;\r
+            break;\r
+          }\r
+\r
+          //\r
+          // Execute the device path\r
+          //\r
+          Status = InternalShellExecuteDevicePath(\r
+            &gImageHandle,\r
+            DevPath,\r
+            CmdLine,\r
+            NULL,\r
+            &StartStatus\r
+           );\r
+\r
+          SHELL_FREE_NON_NULL(DevPath);\r
+\r
+          if(EFI_ERROR (Status)) {\r
+            CalleeExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));\r
+          } else {\r
+            CalleeExitStatus = (SHELL_STATUS) StartStatus;\r
+          }\r
+\r
+          if (CommandStatus != NULL) {\r
+            *CommandStatus = CalleeExitStatus;\r
+          }\r
+\r
+          //\r
+          // Update last error status.\r
+          //\r
+          // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS\r
+          SetLastError(CalleeExitStatus);\r
+          break;\r
+        default:\r
+          //\r
+          // Do nothing.\r
+          //\r
+          break;\r
+      }\r
+      break;\r
+    default:\r
+      //\r
+      // Do nothing.\r
+      //\r
+      break;\r
+  }\r
+\r
+  SHELL_FREE_NON_NULL(CommandWithPath);\r
+\r
+  return (Status);\r
+}\r
 \r
-  NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);\r
-  OurCommandLine  = StrnCatGrow(&OurCommandLine , &Size2, CmdLine                , StrStr(CmdLine, L"|") - CmdLine);\r
+/**\r
+  Function to setup StdIn, StdErr, StdOut, and then run the command or file.\r
 \r
-  if (NextCommandLine == NULL || OurCommandLine == NULL) {\r
-    SHELL_FREE_NON_NULL(OurCommandLine);\r
-    SHELL_FREE_NON_NULL(NextCommandLine);\r
-    return (EFI_OUT_OF_RESOURCES);\r
-  } else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) {\r
-    SHELL_FREE_NON_NULL(OurCommandLine);\r
-    SHELL_FREE_NON_NULL(NextCommandLine);\r
-    return (EFI_INVALID_PARAMETER);\r
-  } else if (NextCommandLine[0] != CHAR_NULL &&\r
-      NextCommandLine[0] == L'a' &&\r
-      NextCommandLine[1] == L' '\r
-     ){\r
-    CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));\r
-    Unicode = FALSE;\r
-  } else {\r
-    Unicode = TRUE;\r
-  }\r
+  @param[in] Type             the type of operation being run.\r
+  @param[in] CmdLine          the command line to run.\r
+  @param[in] FirstParameter   the first parameter on the command line.\r
+  @param[in] ParamProtocol    the shell parameters protocol pointer\r
+  @param[out] CommandStatus   the status from the command line.\r
 \r
+  @retval EFI_SUCCESS     The command was completed.\r
+  @retval EFI_ABORTED     The command's operation was aborted.\r
+**/\r
+EFI_STATUS\r
+SetupAndRunCommandOrFile(\r
+  IN   SHELL_OPERATION_TYPES          Type,\r
+  IN   CHAR16                         *CmdLine,\r
+  IN   CHAR16                         *FirstParameter,\r
+  IN   EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,\r
+  OUT EFI_STATUS                      *CommandStatus\r
+)\r
+{\r
+  EFI_STATUS                Status;\r
+  SHELL_FILE_HANDLE         OriginalStdIn;\r
+  SHELL_FILE_HANDLE         OriginalStdOut;\r
+  SHELL_FILE_HANDLE         OriginalStdErr;\r
+  SYSTEM_TABLE_INFO         OriginalSystemTableInfo;\r
+  CONST SCRIPT_FILE         *ConstScriptFile;\r
 \r
   //\r
-  // make a SPLIT_LIST item and add to list\r
+  // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII\r
   //\r
-  Split = AllocateZeroPool(sizeof(SPLIT_LIST));\r
-  ASSERT(Split != NULL);\r
-  Split->SplitStdIn   = StdIn;\r
-  Split->SplitStdOut  = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);\r
-  ASSERT(Split->SplitStdOut != NULL);\r
-  InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);\r
-\r
-  Status = RunCommand(OurCommandLine);\r
+  Status = UpdateStdInStdOutStdErr(ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
 \r
   //\r
-  // move the output from the first to the in to the second.\r
+  // The StdIn, StdOut, and StdErr are set up.\r
+  // Now run the command, script, or application\r
   //\r
-  TempFileHandle      = Split->SplitStdOut;\r
-  if (Split->SplitStdIn == StdIn) {\r
-    Split->SplitStdOut = NULL;\r
-  } else {\r
-    Split->SplitStdOut  = Split->SplitStdIn;\r
-  }\r
-  Split->SplitStdIn   = TempFileHandle;\r
-  ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);\r
-\r
   if (!EFI_ERROR(Status)) {\r
-    Status = RunCommand(NextCommandLine);\r
+    TrimSpaces(&CmdLine);\r
+    Status = RunCommandOrFile(Type, CmdLine, FirstParameter, ParamProtocol, CommandStatus);\r
   }\r
 \r
   //\r
-  // remove the top level from the ScriptList\r
+  // Now print errors\r
   //\r
-  ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);\r
-  RemoveEntryList(&Split->Link);\r
+  if (EFI_ERROR(Status)) {\r
+    ConstScriptFile = ShellCommandGetCurrentScriptFile();\r
+    if (ConstScriptFile == NULL || ConstScriptFile->CurrentCommand == NULL) {\r
+      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));\r
+    } else {\r
+      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR_SCRIPT), ShellInfoObject.HiiHandle, (VOID*)(Status), ConstScriptFile->CurrentCommand->Line);\r
+    }\r
+  }\r
 \r
   //\r
-  // Note that the original StdIn is now the StdOut...\r
+  // put back the original StdIn, StdOut, and StdErr\r
   //\r
-  if (Split->SplitStdOut != NULL && Split->SplitStdOut != StdIn) {\r
-    ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));\r
-  }\r
-  if (Split->SplitStdIn != NULL) {\r
-    ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));\r
-  }\r
-\r
-  FreePool(Split);\r
-  FreePool(NextCommandLine);\r
-  FreePool(OurCommandLine);\r
+  RestoreStdInStdOutStdErr(ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
 \r
   return (Status);\r
 }\r
@@ -1402,368 +2618,157 @@ RunSplitCommand(
 /**\r
   Function will process and run a command line.\r
 \r
-  This will determine if the command line represents an internal shell \r
+  This will determine if the command line represents an internal shell\r
   command or dispatch an external application.\r
 \r
   @param[in] CmdLine      The command line to parse.\r
+  @param[out] CommandStatus   The status from the command line.\r
 \r
   @retval EFI_SUCCESS     The command was completed.\r
   @retval EFI_ABORTED     The command's operation was aborted.\r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
-RunCommand(\r
-  IN CONST CHAR16   *CmdLine\r
+RunShellCommand(\r
+  IN CONST CHAR16   *CmdLine,\r
+  OUT EFI_STATUS    *CommandStatus\r
   )\r
 {\r
   EFI_STATUS                Status;\r
-  EFI_STATUS                StatusCode;\r
-  CHAR16                    *CommandName;\r
-  SHELL_STATUS              ShellStatus;\r
-  UINTN                     Argc;\r
-  CHAR16                    **Argv;\r
-  BOOLEAN                   LastError;\r
-  CHAR16                    LeString[19];\r
-  CHAR16                    *PostAliasCmdLine;\r
-  UINTN                     PostAliasSize;\r
-  CHAR16                    *PostVariableCmdLine;\r
-  CHAR16                    *CommandWithPath;\r
-  CONST EFI_DEVICE_PATH_PROTOCOL  *DevPath;\r
-  CONST CHAR16              *TempLocation;\r
-  CONST CHAR16              *TempLocation2;\r
-  SHELL_FILE_HANDLE         OriginalStdIn;\r
-  SHELL_FILE_HANDLE         OriginalStdOut;\r
-  SHELL_FILE_HANDLE         OriginalStdErr;\r
-  SYSTEM_TABLE_INFO         OriginalSystemTableInfo;\r
-  UINTN                     Count;\r
-  UINTN                     Count2;\r
   CHAR16                    *CleanOriginal;\r
-  SPLIT_LIST                *Split;\r
+  CHAR16                    *FirstParameter;\r
+  CHAR16                    *TempWalker;\r
+  SHELL_OPERATION_TYPES     Type;\r
+  CONST CHAR16              *CurDir;\r
 \r
   ASSERT(CmdLine != NULL);\r
   if (StrLen(CmdLine) == 0) {\r
     return (EFI_SUCCESS);\r
   }\r
 \r
-  CommandName         = NULL;\r
-  PostVariableCmdLine = NULL;\r
-  PostAliasCmdLine    = NULL;\r
-  CommandWithPath     = NULL;\r
-  DevPath             = NULL;\r
   Status              = EFI_SUCCESS;\r
   CleanOriginal       = NULL;\r
-  Split               = NULL;\r
 \r
   CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);\r
   if (CleanOriginal == NULL) {\r
     return (EFI_OUT_OF_RESOURCES);\r
   }\r
 \r
+  TrimSpaces(&CleanOriginal);\r
+\r
   //\r
-  // Remove any spaces and tabs at the beginning of the string.\r
+  // NULL out comments (leveraged from RunScriptFileHandle() ).\r
+  // The # character on a line is used to denote that all characters on the same line\r
+  // and to the right of the # are to be ignored by the shell.\r
+  // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.\r
   //\r
-  while ((CleanOriginal[0] == L' ') || (CleanOriginal[0] == L'\t')) {\r
-    CopyMem(CleanOriginal, CleanOriginal+1, StrSize(CleanOriginal) - sizeof(CleanOriginal[0]));\r
+  for (TempWalker = CleanOriginal; TempWalker != NULL && *TempWalker != CHAR_NULL; TempWalker++) {\r
+    if (*TempWalker == L'^') {\r
+      if (*(TempWalker + 1) == L'#') {\r
+        TempWalker++;\r
+      }\r
+    } else if (*TempWalker == L'#') {\r
+      *TempWalker = CHAR_NULL;\r
+    }\r
   }\r
 \r
+  TrimSpaces(&CleanOriginal);\r
+\r
   //\r
   // Handle case that passed in command line is just 1 or more " " characters.\r
   //\r
   if (StrLen (CleanOriginal) == 0) {\r
-    if (CleanOriginal != NULL) {\r
-      FreePool(CleanOriginal);\r
-      CleanOriginal = NULL;\r
-    }\r
+    SHELL_FREE_NON_NULL(CleanOriginal);\r
     return (EFI_SUCCESS);\r
   }\r
 \r
-  //\r
-  // Remove any spaces at the end of the string.\r
-  //\r
-  while (CleanOriginal[StrLen(CleanOriginal)-1] == L' ') {\r
-    CleanOriginal[StrLen(CleanOriginal)-1] = CHAR_NULL;\r
-  }\r
-\r
-  CommandName = NULL;\r
-  if (StrStr(CleanOriginal, L" ") == NULL){\r
-    StrnCatGrow(&CommandName, NULL, CleanOriginal, 0);\r
-  } else {\r
-    StrnCatGrow(&CommandName, NULL, CleanOriginal, StrStr(CleanOriginal, L" ") - CleanOriginal);\r
-  }\r
-\r
-  ASSERT(PostAliasCmdLine == NULL);\r
-  if (!ShellCommandIsCommandOnList(CommandName)) {\r
-    //\r
-    // Convert via alias\r
-    //\r
-    Status = ShellConvertAlias(&CommandName);\r
-    PostAliasSize = 0;\r
-    PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, CommandName, 0);\r
-    PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, StrStr(CleanOriginal, L" "), 0);\r
-    ASSERT_EFI_ERROR(Status);\r
-  } else {\r
-    PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, NULL, CleanOriginal, 0);\r
-  }\r
-\r
-  if (CleanOriginal != NULL) {\r
-    FreePool(CleanOriginal);\r
-    CleanOriginal = NULL;\r
-  }\r
-\r
-  if (CommandName != NULL) {\r
-    FreePool(CommandName);\r
-    CommandName = NULL;\r
+  Status = ProcessCommandLineToFinal(&CleanOriginal);\r
+  if (EFI_ERROR(Status)) {\r
+    SHELL_FREE_NON_NULL(CleanOriginal);\r
+    return (Status);\r
   }\r
 \r
-  PostVariableCmdLine = ShellConvertVariables(PostAliasCmdLine);\r
-\r
   //\r
-  // we can now free the modified by alias command line\r
+  // We don't do normal processing with a split command line (output from one command input to another)\r
   //\r
-  if (PostAliasCmdLine != NULL) {\r
-    FreePool(PostAliasCmdLine);\r
-    PostAliasCmdLine = NULL;\r
-  }\r
-\r
-  if (PostVariableCmdLine == NULL) {\r
-    return (EFI_OUT_OF_RESOURCES);\r
-  }\r
-\r
-  while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {\r
-    PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;\r
-  }\r
-  while (PostVariableCmdLine[0] == L' ') {\r
-    CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));\r
+  if (ContainsSplit(CleanOriginal)) {\r
+    Status = ProcessNewSplitCommandLine(CleanOriginal);\r
+    SHELL_FREE_NON_NULL(CleanOriginal);\r
+    return (Status);\r
   }\r
 \r
   //\r
-  // We dont do normal processing with a split command line (output from one command input to another)\r
+  // We need the first parameter information so we can determine the operation type\r
   //\r
-  if (ContainsSplit(PostVariableCmdLine)) {\r
-    //\r
-    // are we in an existing split???\r
-    //\r
-    if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {\r
-      Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);\r
-    }\r
-\r
-    if (Split == NULL) {\r
-      Status = RunSplitCommand(PostVariableCmdLine, NULL, NULL);\r
-    } else {\r
-      Status = RunSplitCommand(PostVariableCmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
-    }\r
-    if (EFI_ERROR(Status)) {\r
-      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, PostVariableCmdLine);\r
-    }\r
-  } else {\r
-\r
+  FirstParameter = AllocateZeroPool(StrSize(CleanOriginal));\r
+  if (FirstParameter == NULL) {\r
+    SHELL_FREE_NON_NULL(CleanOriginal);\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  }\r
+  TempWalker = CleanOriginal;\r
+  if (!EFI_ERROR(GetNextParameter(&TempWalker, &FirstParameter, StrSize(CleanOriginal), TRUE))) {\r
     //\r
-    // If this is a mapped drive change handle that...\r
+    // Depending on the first parameter we change the behavior\r
     //\r
-    if (PostVariableCmdLine[(StrLen(PostVariableCmdLine)-1)] == L':' && StrStr(PostVariableCmdLine, L" ") == NULL) {\r
-      Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, PostVariableCmdLine);\r
-      if (EFI_ERROR(Status)) {\r
-        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, PostVariableCmdLine);\r
-      }\r
-      FreePool(PostVariableCmdLine);\r
-      return (Status);\r
-    }\r
-\r
-    ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...\r
-    ///      We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...\r
-\r
-\r
-\r
-    Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
-    if (EFI_ERROR(Status)) {\r
-      if (Status == EFI_NOT_FOUND) {\r
-        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle);\r
-      } else {\r
-        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);\r
-      }\r
-    } else {\r
-      while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {\r
-        PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;\r
-      }\r
-      while (PostVariableCmdLine[0] == L' ') {\r
-        CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));\r
-      }\r
-\r
-      //\r
-      // get the argc and argv updated for internal commands\r
-      //\r
-      Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &Argv, &Argc);\r
-      ASSERT_EFI_ERROR(Status);\r
-\r
-      for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) {\r
-        if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[Count], L"-?") == ShellInfoObject.NewShellParametersProtocol->Argv[Count]\r
-        ||  (ShellInfoObject.NewShellParametersProtocol->Argv[0][0] == L'?' && ShellInfoObject.NewShellParametersProtocol->Argv[0][1] == CHAR_NULL)\r
-          ) {\r
-          //\r
-          // We need to redo the arguments since a parameter was -?\r
-          // move them all down 1 to the end, then up one then replace the first with help\r
-          //\r
-          FreePool(ShellInfoObject.NewShellParametersProtocol->Argv[Count]);\r
-          ShellInfoObject.NewShellParametersProtocol->Argv[Count] = NULL;\r
-          for (Count2 = Count ; (Count2 + 1) < ShellInfoObject.NewShellParametersProtocol->Argc ; Count2++) {\r
-            ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2+1];\r
-          }\r
-          ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = NULL;\r
-          for (Count2 = ShellInfoObject.NewShellParametersProtocol->Argc -1 ; Count2 > 0 ; Count2--) {\r
-            ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2-1];\r
-          }\r
-          ShellInfoObject.NewShellParametersProtocol->Argv[0] = NULL;\r
-          ShellInfoObject.NewShellParametersProtocol->Argv[0] = StrnCatGrow(&ShellInfoObject.NewShellParametersProtocol->Argv[0], NULL, L"help", 0);\r
-          break;\r
-        }\r
-      }\r
-\r
-      //\r
-      // command or file?\r
-      //\r
-      if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {\r
-        //\r
-        // Run the command (which was converted if it was an alias)\r
-        //\r
-        if (!EFI_ERROR(Status))  {\r
-          Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError);\r
-          ASSERT_EFI_ERROR(Status);\r
-\r
-          if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
-            UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellStatus);\r
-          } else {\r
-            UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ShellStatus);\r
-          }\r
-          DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
-          if (LastError) {\r
-            InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
-          }\r
-          //\r
-          // Pass thru the exitcode from the app.\r
-          //\r
-          if (ShellCommandGetExit()) {\r
-            Status = ShellStatus;\r
-          } else if (ShellStatus != 0 && IsScriptOnlyCommand(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {\r
-            Status = EFI_ABORTED;\r
-          }\r
-        }\r
-      } else {\r
+    switch (Type = GetOperationType(FirstParameter)) {\r
+      case   File_Sys_Change:\r
+        Status = ChangeMappedDrive (FirstParameter);\r
+        break;\r
+      case   Internal_Command:\r
+      case   Script_File_Name:\r
+      case   Efi_Application:\r
+        Status = SetupAndRunCommandOrFile(Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol, CommandStatus);\r
+        break;\r
+      default:\r
         //\r
-        // run an external file (or script)\r
+        // Whatever was typed, it was invalid.\r
         //\r
-        if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[0], L":") != NULL) {\r
-          ASSERT (CommandWithPath == NULL);\r
-          if (ShellIsFile(ShellInfoObject.NewShellParametersProtocol->Argv[0]) == EFI_SUCCESS) {\r
-            CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
-          }\r
-        }\r
-        if (CommandWithPath == NULL) {\r
-          CommandWithPath = ShellFindFilePathEx(ShellInfoObject.NewShellParametersProtocol->Argv[0], mExecutableExtensions);\r
-        }\r
-        if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) {\r
-          ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]);\r
-\r
-          if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
-            UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", EFI_NOT_FOUND);\r
-          } else {\r
-            UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", EFI_NOT_FOUND);\r
-          }\r
-          DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
-          InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
-        } else {\r
-          //\r
-          // Check if it's a NSH (script) file.\r
-          //\r
-          TempLocation = CommandWithPath+StrLen(CommandWithPath)-4;\r
-          TempLocation2 = mScriptExtension;\r
-          if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) {\r
-            Status = RunScriptFile (CommandWithPath);\r
-          } else {\r
-            DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
-            ASSERT(DevPath != NULL);\r
-            Status = InternalShellExecuteDevicePath(\r
-              &gImageHandle,\r
-              DevPath,\r
-              PostVariableCmdLine,\r
-              NULL,\r
-              &StatusCode\r
-             );\r
-\r
-            //\r
-            // Update last error status.\r
-            //\r
-            if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
-              UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", StatusCode);\r
-            } else {\r
-              UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", StatusCode);\r
-            }\r
-            DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
-            InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
-          }\r
-        }\r
-      }\r
-\r
+        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
+        SetLastError(SHELL_NOT_FOUND);\r
+        break;\r
+    }\r
+  } else {\r
+    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
+    SetLastError(SHELL_NOT_FOUND);\r
+  }\r
+  //\r
+  // Check whether the current file system still exists. If not exist, we need update "cwd" and gShellCurMapping.\r
+  //\r
+  CurDir = EfiShellGetCurDir (NULL);\r
+  if (CurDir != NULL) {\r
+    if (EFI_ERROR(ShellFileExists (CurDir))) {\r
       //\r
-      // Print some error info.\r
+      // EfiShellSetCurDir() cannot set current directory to NULL.\r
+      // EfiShellSetEnv() is not allowed to set the "cwd" variable.\r
+      // Only InternalEfiShellSetEnv () is allowed setting the "cwd" variable.\r
       //\r
-      if (EFI_ERROR(Status)) {\r
-        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));\r
-      }\r
-\r
-      CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
-\r
-      RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);\r
-\r
-      RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
-    }\r
-    if (CommandName != NULL) {\r
-      if (ShellCommandGetCurrentScriptFile() != NULL &&  !IsScriptOnlyCommand(CommandName)) {\r
-        //\r
-        // if this is NOT a scipt only command return success so the script won't quit.\r
-        // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)\r
-        //\r
-        Status = EFI_SUCCESS;\r
-      }\r
+      InternalEfiShellSetEnv (L"cwd", NULL, TRUE);\r
+      gShellCurMapping = NULL;\r
     }\r
   }\r
 \r
-  SHELL_FREE_NON_NULL(CommandName);\r
-  SHELL_FREE_NON_NULL(CommandWithPath);\r
-  SHELL_FREE_NON_NULL(PostVariableCmdLine);\r
+  SHELL_FREE_NON_NULL(CleanOriginal);\r
+  SHELL_FREE_NON_NULL(FirstParameter);\r
 \r
   return (Status);\r
 }\r
 \r
-STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};\r
 /**\r
-  Function determins if the CommandName COULD be a valid command.  It does not determine whether\r
-  this is a valid command.  It only checks for invalid characters.\r
+  Function will process and run a command line.\r
+\r
+  This will determine if the command line represents an internal shell\r
+  command or dispatch an external application.\r
 \r
-  @param[in] CommandName    The name to check\r
+  @param[in] CmdLine      The command line to parse.\r
 \r
-  @retval TRUE              CommandName could be a command name\r
-  @retval FALSE             CommandName could not be a valid command name\r
+  @retval EFI_SUCCESS     The command was completed.\r
+  @retval EFI_ABORTED     The command's operation was aborted.\r
 **/\r
-BOOLEAN\r
-EFIAPI\r
-IsValidCommandName(\r
-  IN CONST CHAR16     *CommandName\r
+EFI_STATUS\r
+RunCommand(\r
+  IN CONST CHAR16   *CmdLine\r
   )\r
 {\r
-  UINTN Count;\r
-  if (CommandName == NULL) {\r
-    ASSERT(FALSE);\r
-    return (FALSE);\r
-  }\r
-  for ( Count = 0\r
-      ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])\r
-      ; Count++\r
-     ){\r
-    if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {\r
-      return (FALSE);\r
-    }\r
-  }\r
-  return (TRUE);\r
+  return (RunShellCommand(CmdLine, NULL));\r
 }\r
 \r
 /**\r
@@ -1772,10 +2777,9 @@ IsValidCommandName(
   @param[in] Handle             The handle to the already opened file.\r
   @param[in] Name               The name of the script file.\r
 \r
-  @retval EFI_SUCCESS           the script completed sucessfully\r
+  @retval EFI_SUCCESS           the script completed successfully\r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
 RunScriptFileHandle (\r
   IN SHELL_FILE_HANDLE  Handle,\r
   IN CONST CHAR16       *Name\r
@@ -1784,6 +2788,7 @@ RunScriptFileHandle (
   EFI_STATUS          Status;\r
   SCRIPT_FILE         *NewScriptFile;\r
   UINTN               LoopVar;\r
+  UINTN               PrintBuffSize;\r
   CHAR16              *CommandLine;\r
   CHAR16              *CommandLine2;\r
   CHAR16              *CommandLine3;\r
@@ -1794,10 +2799,12 @@ RunScriptFileHandle (
   CONST CHAR16        *CurDir;\r
   UINTN               LineCount;\r
   CHAR16              LeString[50];\r
+  LIST_ENTRY          OldBufferList;\r
 \r
   ASSERT(!ShellCommandGetScriptExit());\r
 \r
   PreScriptEchoState = ShellCommandGetEchoState();\r
+  PrintBuffSize = PcdGet16(PcdShellPrintBufferSize);\r
 \r
   NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));\r
   if (NewScriptFile == NULL) {\r
@@ -1824,7 +2831,12 @@ RunScriptFileHandle (
       DeleteScriptFileStruct(NewScriptFile);\r
       return (EFI_OUT_OF_RESOURCES);\r
     }\r
-    for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {\r
+    //\r
+    // Put the full path of the script file into Argv[0] as required by section\r
+    // 3.6.2 of version 2.2 of the shell specification.\r
+    //\r
+    NewScriptFile->Argv[0] = StrnCatGrow(&NewScriptFile->Argv[0], NULL, NewScriptFile->ScriptName, 0);\r
+    for (LoopVar = 1 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {\r
       ASSERT(NewScriptFile->Argv[LoopVar] == NULL);\r
       NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);\r
       if (NewScriptFile->Argv[LoopVar] == NULL) {\r
@@ -1846,11 +2858,13 @@ RunScriptFileHandle (
   while(!ShellFileHandleEof(Handle)) {\r
     CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);\r
     LineCount++;\r
-    if (CommandLine == NULL || StrLen(CommandLine) == 0) {\r
+    if (CommandLine == NULL || StrLen(CommandLine) == 0 || CommandLine[0] == '#') {\r
+      SHELL_FREE_NON_NULL(CommandLine);\r
       continue;\r
     }\r
     NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));\r
     if (NewScriptFile->CurrentCommand == NULL) {\r
+      SHELL_FREE_NON_NULL(CommandLine);\r
       DeleteScriptFileStruct(NewScriptFile);\r
       return (EFI_OUT_OF_RESOURCES);\r
     }\r
@@ -1870,12 +2884,12 @@ RunScriptFileHandle (
   //\r
   // Now enumerate through the commands and run each one.\r
   //\r
-  CommandLine = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));\r
+  CommandLine = AllocateZeroPool(PrintBuffSize);\r
   if (CommandLine == NULL) {\r
     DeleteScriptFileStruct(NewScriptFile);\r
     return (EFI_OUT_OF_RESOURCES);\r
   }\r
-  CommandLine2 = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));\r
+  CommandLine2 = AllocateZeroPool(PrintBuffSize);\r
   if (CommandLine2 == NULL) {\r
     FreePool(CommandLine);\r
     DeleteScriptFileStruct(NewScriptFile);\r
@@ -1887,15 +2901,23 @@ RunScriptFileHandle (
       ; // conditional increment in the body of the loop\r
   ){\r
     ASSERT(CommandLine2 != NULL);\r
-    StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl);\r
+    StrnCpyS( CommandLine2,\r
+              PrintBuffSize/sizeof(CHAR16),\r
+              NewScriptFile->CurrentCommand->Cl,\r
+              PrintBuffSize/sizeof(CHAR16) - 1\r
+              );\r
+\r
+    SaveBufferList(&OldBufferList);\r
 \r
     //\r
     // NULL out comments\r
     //\r
     for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {\r
       if (*CommandLine3 == L'^') {\r
-        if (*(CommandLine3+1) == L'#' || *(CommandLine3+1) == L':') {\r
+        if ( *(CommandLine3+1) == L':') {\r
           CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));\r
+        } else if (*(CommandLine3+1) == L'#') {\r
+          CommandLine3++;\r
         }\r
       } else if (*CommandLine3 == L'#') {\r
         *CommandLine3 = CHAR_NULL;\r
@@ -1906,7 +2928,11 @@ RunScriptFileHandle (
       //\r
       // Due to variability in starting the find and replace action we need to have both buffers the same.\r
       //\r
-      StrCpy(CommandLine, CommandLine2);\r
+      StrnCpyS( CommandLine,\r
+                PrintBuffSize/sizeof(CHAR16),\r
+                CommandLine2,\r
+                PrintBuffSize/sizeof(CHAR16) - 1\r
+                );\r
 \r
       //\r
       // Remove the %0 to %9 from the command line (if we have some arguments)\r
@@ -1914,51 +2940,55 @@ RunScriptFileHandle (
       if (NewScriptFile->Argv != NULL) {\r
         switch (NewScriptFile->Argc) {\r
           default:\r
-            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", NewScriptFile->Argv[9], FALSE, TRUE);\r
+            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%9", NewScriptFile->Argv[9], FALSE, FALSE);\r
             ASSERT_EFI_ERROR(Status);\r
           case 9:\r
-            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", NewScriptFile->Argv[8], FALSE, TRUE);\r
+            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%8", NewScriptFile->Argv[8], FALSE, FALSE);\r
             ASSERT_EFI_ERROR(Status);\r
           case 8:\r
-            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", NewScriptFile->Argv[7], FALSE, TRUE);\r
+            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%7", NewScriptFile->Argv[7], FALSE, FALSE);\r
             ASSERT_EFI_ERROR(Status);\r
           case 7:\r
-            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", NewScriptFile->Argv[6], FALSE, TRUE);\r
+            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%6", NewScriptFile->Argv[6], FALSE, FALSE);\r
             ASSERT_EFI_ERROR(Status);\r
           case 6:\r
-            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", NewScriptFile->Argv[5], FALSE, TRUE);\r
+            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%5", NewScriptFile->Argv[5], FALSE, FALSE);\r
             ASSERT_EFI_ERROR(Status);\r
           case 5:\r
-            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", NewScriptFile->Argv[4], FALSE, TRUE);\r
+            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%4", NewScriptFile->Argv[4], FALSE, FALSE);\r
             ASSERT_EFI_ERROR(Status);\r
           case 4:\r
-            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", NewScriptFile->Argv[3], FALSE, TRUE);\r
+            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%3", NewScriptFile->Argv[3], FALSE, FALSE);\r
             ASSERT_EFI_ERROR(Status);\r
           case 3:\r
-            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", NewScriptFile->Argv[2], FALSE, TRUE);\r
+            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%2", NewScriptFile->Argv[2], FALSE, FALSE);\r
             ASSERT_EFI_ERROR(Status);\r
           case 2:\r
-            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", NewScriptFile->Argv[1], FALSE, TRUE);\r
+            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%1", NewScriptFile->Argv[1], FALSE, FALSE);\r
             ASSERT_EFI_ERROR(Status);\r
           case 1:\r
-            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, TRUE);\r
+            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%0", NewScriptFile->Argv[0], FALSE, FALSE);\r
             ASSERT_EFI_ERROR(Status);\r
             break;\r
           case 0:\r
             break;\r
         }\r
       }\r
-      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", L"\"\"", FALSE, FALSE);\r
-      Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", L"\"\"", FALSE, FALSE);\r
-      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", L"\"\"", FALSE, FALSE);\r
-      Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", L"\"\"", FALSE, FALSE);\r
-      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", L"\"\"", FALSE, FALSE);\r
-      Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", L"\"\"", FALSE, FALSE);\r
-      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", L"\"\"", FALSE, FALSE);\r
-      Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", L"\"\"", FALSE, FALSE);\r
-      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", L"\"\"", FALSE, FALSE);\r
-\r
-      StrCpy(CommandLine2, CommandLine);\r
+      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%1", L"\"\"", FALSE, FALSE);\r
+      Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%2", L"\"\"", FALSE, FALSE);\r
+      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%3", L"\"\"", FALSE, FALSE);\r
+      Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%4", L"\"\"", FALSE, FALSE);\r
+      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%5", L"\"\"", FALSE, FALSE);\r
+      Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%6", L"\"\"", FALSE, FALSE);\r
+      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%7", L"\"\"", FALSE, FALSE);\r
+      Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%8", L"\"\"", FALSE, FALSE);\r
+      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%9", L"\"\"", FALSE, FALSE);\r
+\r
+      StrnCpyS( CommandLine2,\r
+                PrintBuffSize/sizeof(CHAR16),\r
+                CommandLine,\r
+                PrintBuffSize/sizeof(CHAR16) - 1\r
+                );\r
 \r
       LastCommand = NewScriptFile->CurrentCommand;\r
 \r
@@ -2013,15 +3043,19 @@ RunScriptFileHandle (
 \r
           ShellCommandRegisterExit(FALSE, 0);\r
           Status = EFI_SUCCESS;\r
+          RestoreBufferList(&OldBufferList);\r
           break;\r
         }\r
         if (ShellGetExecutionBreakFlag()) {\r
+          RestoreBufferList(&OldBufferList);\r
           break;\r
         }\r
         if (EFI_ERROR(Status)) {\r
+          RestoreBufferList(&OldBufferList);\r
           break;\r
         }\r
         if (ShellCommandGetExit()) {\r
+          RestoreBufferList(&OldBufferList);\r
           break;\r
         }\r
       }\r
@@ -2040,6 +3074,7 @@ RunScriptFileHandle (
         NewScriptFile->CurrentCommand->Reset = TRUE;\r
       }\r
     }\r
+    RestoreBufferList(&OldBufferList);\r
   }\r
 \r
 \r
@@ -2060,30 +3095,95 @@ RunScriptFileHandle (
   Function to process a NSH script file.\r
 \r
   @param[in] ScriptPath         Pointer to the script file name (including file system path).\r
+  @param[in] Handle             the handle of the script file already opened.\r
+  @param[in] CmdLine            the command line to run.\r
+  @param[in] ParamProtocol      the shell parameters protocol pointer\r
 \r
-  @retval EFI_SUCCESS           the script completed sucessfully\r
+  @retval EFI_SUCCESS           the script completed successfully\r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
 RunScriptFile (\r
-  IN CONST CHAR16 *ScriptPath\r
+  IN CONST CHAR16                   *ScriptPath,\r
+  IN SHELL_FILE_HANDLE              Handle OPTIONAL,\r
+  IN CONST CHAR16                   *CmdLine,\r
+  IN EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol\r
   )\r
 {\r
   EFI_STATUS          Status;\r
   SHELL_FILE_HANDLE   FileHandle;\r
+  UINTN                     Argc;\r
+  CHAR16                    **Argv;\r
 \r
   if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {\r
     return (EFI_INVALID_PARAMETER);\r
   }\r
 \r
-  Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
-  if (EFI_ERROR(Status)) {\r
-    return (Status);\r
-  }\r
+  //\r
+  // get the argc and argv updated for scripts\r
+  //\r
+  Status = UpdateArgcArgv(ParamProtocol, CmdLine, Script_File_Name, &Argv, &Argc);\r
+  if (!EFI_ERROR(Status)) {\r
+\r
+    if (Handle == NULL) {\r
+      //\r
+      // open the file\r
+      //\r
+      Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
+      if (!EFI_ERROR(Status)) {\r
+        //\r
+        // run it\r
+        //\r
+        Status = RunScriptFileHandle(FileHandle, ScriptPath);\r
 \r
-  Status = RunScriptFileHandle(FileHandle, ScriptPath);\r
+        //\r
+        // now close the file\r
+        //\r
+        ShellCloseFile(&FileHandle);\r
+      }\r
+    } else {\r
+      Status = RunScriptFileHandle(Handle, ScriptPath);\r
+    }\r
+  }\r
 \r
-  ShellCloseFile(&FileHandle);\r
+  //\r
+  // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.\r
+  // This is safe even if the update API failed.  In this case, it may be a no-op.\r
+  //\r
+  RestoreArgcArgv(ParamProtocol, &Argv, &Argc);\r
 \r
   return (Status);\r
 }\r
+\r
+/**\r
+  Return the pointer to the first occurrence of any character from a list of characters.\r
+\r
+  @param[in] String           the string to parse\r
+  @param[in] CharacterList    the list of character to look for\r
+  @param[in] EscapeCharacter  An escape character to skip\r
+\r
+  @return the location of the first character in the string\r
+  @retval CHAR_NULL no instance of any character in CharacterList was found in String\r
+**/\r
+CONST CHAR16*\r
+FindFirstCharacter(\r
+  IN CONST CHAR16 *String,\r
+  IN CONST CHAR16 *CharacterList,\r
+  IN CONST CHAR16 EscapeCharacter\r
+  )\r
+{\r
+  UINT32 WalkChar;\r
+  UINT32 WalkStr;\r
+\r
+  for (WalkStr = 0; WalkStr < StrLen(String); WalkStr++) {\r
+    if (String[WalkStr] == EscapeCharacter) {\r
+      WalkStr++;\r
+      continue;\r
+    }\r
+    for (WalkChar = 0; WalkChar < StrLen(CharacterList); WalkChar++) {\r
+      if (String[WalkStr] == CharacterList[WalkChar]) {\r
+        return (&String[WalkStr]);\r
+      }\r
+    }\r
+  }\r
+  return (String + StrLen(String));\r
+}\r