]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ShellPkg/Application/Shell/Shell.c
ShellPkg: refactor elimination of non-replaced environment variables
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
index 53f1bc3059aed2aedf75d9885ec0217087b3baa8..ba3be3f6c344236e393bc9a9174ddfa1a06d5725 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   This is THE shell (application)\r
 \r
-  Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>\r
   Copyright (c) 2013, Hewlett-Packard Development Company, L.P.\r
   This program and the accompanying materials\r
   are licensed and made available under the terms and conditions of the BSD License\r
@@ -71,9 +71,9 @@ STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";
 STATIC CONST CHAR16 mStartupScript[]        = L"startup.nsh";\r
 \r
 /**\r
-  Cleans off leading and trailing spaces and tabs\r
+  Cleans off leading and trailing spaces and tabs.\r
 \r
-  @param[in] String pointer to the string to trim them off\r
+  @param[in] String pointer to the string to trim them off.\r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
@@ -91,9 +91,9 @@ TrimSpaces(
   }\r
 \r
   //\r
-  // Remove any spaces at the end of the (*String).\r
+  // Remove any spaces and tabs at the end of the (*String).\r
   //\r
-  while ((*String)[StrLen((*String))-1] == L' ') {\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
@@ -107,8 +107,7 @@ TrimSpaces(
 \r
   @retval                 A pointer to the | character in CmdLine or NULL if not present.\r
 **/\r
-CONST\r
-CHAR16*\r
+CONST CHAR16*\r
 EFIAPI\r
 FindSplit(\r
   IN CONST CHAR16 *CmdLine\r
@@ -245,6 +244,9 @@ UefiMain (
   UINTN                           Size;\r
   EFI_HANDLE                      ConInHandle;\r
   EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *OldConIn;\r
+  UINTN                           ExitDataSize;\r
+  CHAR16                          *ExitData;\r
+  SHELL_STATUS                    ExitStatus;\r
 \r
   if (PcdGet8(PcdShellSupportLevel) > 3) {\r
     return (EFI_UNSUPPORTED);\r
@@ -298,6 +300,12 @@ UefiMain (
   // install our console logger.  This will keep a log of the output for back-browsing\r
   //\r
   Status = ConsoleLoggerInstall(ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);\r
+  if(EFI_ERROR (Status)) {\r
+    ExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));\r
+  } else {\r
+    ExitStatus = SHELL_SUCCESS;\r
+  }\r
+       \r
   if (!EFI_ERROR(Status)) {\r
     //\r
     // Enable the cursor to be visible\r
@@ -320,7 +328,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
@@ -350,7 +359,10 @@ UefiMain (
     //\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
@@ -407,7 +419,7 @@ UefiMain (
     // Display the mapping\r
     //\r
     if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {\r
-      Status = RunCommand(L"map");\r
+      Status = RunCommand(L"map", NULL);\r
       ASSERT_EFI_ERROR(Status);\r
     }\r
 \r
@@ -473,7 +485,11 @@ UefiMain (
         //\r
         // process the startup script or launch the called app.\r
         //\r
-        Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);\r
+        Status = DoStartupScript(\r
+                  ShellInfoObject.ImageDevPath,\r
+                  ShellInfoObject.FileDevPath,\r
+                  &ExitStatus\r
+                  );\r
       }\r
 \r
       if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
@@ -506,6 +522,7 @@ UefiMain (
           //\r
           Status = DoShellPrompt();\r
         } while (!ShellCommandGetExit());\r
+        ExitStatus = (SHELL_STATUS) ShellCommandGetExitCode();\r
       }\r
       if (OldConIn != NULL && ConInHandle != NULL) {\r
         CloseSimpleTextInOnFile (gST->ConIn);\r
@@ -515,6 +532,7 @@ UefiMain (
     }\r
   }\r
 \r
+FreeResources:\r
   //\r
   // uninstall protocols / free memory / etc...\r
   //\r
@@ -576,10 +594,33 @@ UefiMain (
     DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);\r
   }\r
 \r
-  if (ShellCommandGetExit()) {\r
-    return ((EFI_STATUS)ShellCommandGetExitCode());\r
+  if (!EFI_ERROR (Status)) {\r
+    // If the command exited with an error, we pass this error out in the ExitData\r
+    // so that it can be retrieved by the EfiShellExecute function (which may\r
+    // start the shell with gBS->StartImage)\r
+    if (ExitStatus != SHELL_SUCCESS) {\r
+      // Allocate a buffer for exit data to pass to gBS->Exit().\r
+      // This buffer will contain the empty string immediately followed by\r
+      // the shell's exit status. (The empty string is required by the UEFI spec)\r
+      ExitDataSize = (sizeof (CHAR16) + sizeof (SHELL_STATUS));\r
+      ExitData = AllocatePool (ExitDataSize);\r
+      if (ExitData == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+      ExitData[0] = '\0';\r
+      // Use CopyMem to avoid alignment faults\r
+      CopyMem ((ExitData + 1), &ExitStatus, sizeof (ExitStatus));\r
+\r
+      gBS->Exit (ImageHandle, EFI_ABORTED, ExitDataSize, ExitData);\r
+\r
+      ASSERT (FALSE);\r
+      return EFI_SUCCESS;\r
+    } else {\r
+      return EFI_SUCCESS;\r
+    }\r
+  } else {\r
+    return Status;\r
   }\r
-  return (Status);\r
 }\r
 \r
 /**\r
@@ -727,20 +768,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
@@ -774,95 +801,171 @@ ProcessCommandLine(
   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
+                  &gEfiUnicodeCollationProtocolGuid,\r
+                  NULL,\r
+                  (VOID **) &UnicodeCollation\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\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.Delay = 5;\r
+\r
+  // Start LoopVar at 1 to ignore Argv[0] which is the name of this binary\r
+  // (probably "Shell.efi")\r
+  for (LoopVar = 1 ; 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
-        //\r
-        // We found the file... add the rest of the params...\r
-        //\r
-        for ( ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
-          ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));\r
-          StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
-                      &Size,\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
-          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
+    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"-delay",\r
+                                 CurrentArg\r
+                                 ) == 0) {\r
+      ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay        = TRUE;\r
+      // Check for optional delay value following "-delay"\r
+      DelayValueStr = gEfiShellParametersProtocol->Argv[LoopVar + 1];\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
+      // Unrecognised 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
+      ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(CurrentArg));\r
+      if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
+        return (EFI_OUT_OF_RESOURCES);\r
+      }\r
+      //\r
+      // We found `file-name`.\r
+      //\r
+      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;\r
+\r
+      StrCpy (ShellInfoObject.ShellInitSettings.FileName, CurrentArg);\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
+        StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
+                    &Size,\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
+        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
-    }\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
-    if (TempConst != NULL && !EFI_ERROR(ShellConvertStringToUint64(TempConst, &Intermediate, FALSE, FALSE))) {\r
-      ShellInfoObject.ShellInitSettings.Delay = (UINTN)Intermediate;\r
-    }\r
   }\r
-  ShellCommandLineFreeVarList(Package);\r
 \r
-  return (Status);\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -873,13 +976,16 @@ ProcessCommandLine(
   @param ImagePath              the path to the image for shell.  first place to look for the startup script\r
   @param FilePath               the path to the file for shell.  second place to look for the startup script.\r
 \r
+  @param[out] ExitStatus      The exit code of the script. Ignored if NULL.\r
+\r
   @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
+  OUT SHELL_STATUS             *ExitStatus\r
   )\r
 {\r
   EFI_STATUS                    Status;\r
@@ -914,7 +1020,7 @@ DoStartupScript(
       StrCat(FileStringPath, L" ");\r
       StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);\r
     }\r
-    Status = RunCommand(FileStringPath);\r
+    Status = RunCommand(FileStringPath, ExitStatus);\r
     FreePool(FileStringPath);\r
     return (Status);\r
 \r
@@ -991,7 +1097,13 @@ DoStartupScript(
   // If we got a file, run it\r
   //\r
   if (!EFI_ERROR(Status) && FileHandle != NULL) {\r
-    Status = RunScriptFileHandle (FileHandle, mStartupScript);\r
+    Status = RunScriptFile (\r
+              mStartupScript,\r
+              FileHandle,\r
+              L"",\r
+              ShellInfoObject.NewShellParametersProtocol,\r
+              ExitStatus\r
+              );\r
     ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);\r
   } else {\r
     FileStringPath = ShellFindFilePath(mStartupScript);\r
@@ -1002,7 +1114,13 @@ DoStartupScript(
       Status = EFI_SUCCESS;\r
       ASSERT(FileHandle == NULL);\r
     } else {\r
-      Status = RunScriptFile(FileStringPath);\r
+      Status = RunScriptFile(\r
+                FileStringPath,\r
+                NULL,\r
+                L"",\r
+                ShellInfoObject.NewShellParametersProtocol,\r
+                ExitStatus\r
+                );\r
       FreePool(FileStringPath);\r
     }\r
   }\r
@@ -1067,8 +1185,8 @@ DoShellPrompt (
   //\r
   if (!EFI_ERROR (Status)) {\r
     CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;\r
-    Status = RunCommand(CmdLine);\r
-  }\r
+    Status = RunCommand(CmdLine, NULL);\r
+    }\r
 \r
   //\r
   // Done with this command\r
@@ -1158,6 +1276,113 @@ ShellConvertAlias(
   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
+EFIAPI\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 dont 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
+  This function will eliminate unreplaced (and therefore non-found) environment variables.\r
+\r
+  @param[in,out] CmdLine   The command line to update.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+StripUnreplacedEnvironmentVariables(\r
+  IN OUT CHAR16 *CmdLine\r
+  )\r
+{\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 dont have 2 % we are done.\r
+      //\r
+      break;\r
+    }\r
+\r
+    if (FirstQuote < FirstPercent) {\r
+      SecondQuote = FirstQuote!= NULL?FindNextInstance(FirstQuote+1, L"\"", TRUE):NULL;\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
+    ASSERT(FirstPercent < FirstQuote);\r
+    if (SecondPercent < FirstQuote) {\r
+      //\r
+      // We need to remove from FirstPercent to SecondPercent\r
+      //\r
+      CopyMem(FirstPercent, SecondPercent + 1, StrSize(SecondPercent + 1));\r
+\r
+      //\r
+      // dont need to update the locator.  both % characters are gone.\r
+      //\r
+      continue;\r
+    }\r
+    ASSERT(FirstQuote < SecondPercent);\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
@@ -1180,7 +1405,6 @@ ShellConvertVariables (
   CHAR16              *NewCommandLine1;\r
   CHAR16              *NewCommandLine2;\r
   CHAR16              *Temp;\r
-  CHAR16              *Temp2;\r
   UINTN               ItemSize;\r
   CHAR16              *ItemTemp;\r
   SCRIPT_FILE         *CurrentScriptFile;\r
@@ -1273,39 +1497,7 @@ ShellConvertVariables (
     //\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
-    }\r
-\r
+    StripUnreplacedEnvironmentVariables(NewCommandLine1);\r
   }\r
 \r
   //\r
@@ -1327,6 +1519,9 @@ ShellConvertVariables (
   @param[in] StdIn          The pointer to the Standard input.\r
   @param[in] StdOut         The pointer to the Standard output.\r
 \r
+  @param[out] ExitStatus      The exit code of the last command in the pipeline.\r
+                              Ignored if NULL.\r
+\r
   @retval EFI_SUCCESS       The split command is executed successfully.\r
   @retval other             Some error occurs when executing the split command.\r
 **/\r
@@ -1335,7 +1530,8 @@ EFIAPI
 RunSplitCommand(\r
   IN CONST CHAR16             *CmdLine,\r
   IN       SHELL_FILE_HANDLE  *StdIn,\r
-  IN       SHELL_FILE_HANDLE  *StdOut\r
+  IN       SHELL_FILE_HANDLE  *StdOut,\r
+  OUT      SHELL_STATUS       *ExitStatus\r
   )\r
 {\r
   EFI_STATUS        Status;\r
@@ -1389,7 +1585,7 @@ RunSplitCommand(
   ASSERT(Split->SplitStdOut != NULL);\r
   InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);\r
 \r
-  Status = RunCommand(OurCommandLine);\r
+  Status = RunCommand(OurCommandLine, NULL);\r
 \r
   //\r
   // move the output from the first to the in to the second.\r
@@ -1404,7 +1600,7 @@ RunSplitCommand(
   ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);\r
 \r
   if (!EFI_ERROR(Status)) {\r
-    Status = RunCommand(NextCommandLine);\r
+    Status = RunCommand(NextCommandLine, ExitStatus);\r
   }\r
 \r
   //\r
@@ -1432,13 +1628,12 @@ RunSplitCommand(
 \r
 /**\r
   Take the original command line, substitute any variables, free \r
-  the original string, return the modified copy\r
+  the original string, return the modified copy.\r
 \r
-  @param[in,out] CmdLine  pointer to the command line to update\r
-  @param[out]CmdName      upon successful return the name of the command to be run\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
+  @retval EFI_SUCCESS           the function was successful.\r
+  @retval EFI_OUT_OF_RESOURCES  a memory allocation failed.\r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
@@ -1458,13 +1653,12 @@ ShellSubstituteVariables(
 \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
+  the original string, return the modified copy.\r
 \r
-  @param[in] CmdLine  pointer to the command line to update\r
-  @param[out]CmdName  upon successful return the name of the command to be run\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
+  @retval EFI_SUCCESS           the function was successful.\r
+  @retval EFI_OUT_OF_RESOURCES  a memory allocation failed.\r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
@@ -1530,13 +1724,13 @@ ShellSubstituteAliases(
 /**\r
   Takes the Argv[0] part of the command line and determine the meaning of it.\r
 \r
-  @param[in] CmdLine  pointer to the command line to update\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
+  @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
 EFIAPI\r
@@ -1553,17 +1747,19 @@ GetOperationType(
   // test for an internal command.\r
   //\r
   if (ShellCommandIsCommandOnList(CmdName)) {\r
-    return (INTERNAL_COMMAND);\r
+    return (Internal_Command);\r
   }\r
 \r
   //\r
-  // Test for file system change request.  anything ending with : and cant have spaces.\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
-      return (UNKNOWN_INVALID);\r
+    if ( StrStr(CmdName, L" ") != NULL \r
+      || StrLen(StrStr(CmdName, L":")) > 1\r
+      ) {\r
+      return (Unknown_Invalid);\r
     }\r
-    return (FILE_SYS_CHANGE);\r
+    return (File_Sys_Change);\r
   }\r
 \r
   //\r
@@ -1578,7 +1774,7 @@ GetOperationType(
       TempLocation2 = mScriptExtension;\r
       if (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0) {\r
         SHELL_FREE_NON_NULL(FileWithPath);\r
-        return (SCRIPT_FILE_NAME);\r
+        return (Script_File_Name);\r
       }\r
     }\r
 \r
@@ -1586,20 +1782,121 @@ GetOperationType(
     // 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
+    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
+  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
+EFIAPI\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
+    GetNextParameter(&TempWalker, &FirstParameter);\r
+\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
+  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
+EFIAPI\r
+VerifySplit(\r
+  IN CONST CHAR16 *CmdLine\r
+  )\r
+{\r
+  CONST CHAR16  *TempSpot;\r
+  EFI_STATUS    Status;\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
+  // If this was the only item, then get out\r
+  //\r
+  if (!ContainsSplit(CmdLine)) {\r
+    return (EFI_SUCCESS);\r
+  }\r
+\r
+  //\r
+  // recurse to verify the next item\r
+  //\r
+  TempSpot = FindSplit(CmdLine)+1;\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
+  @param[in] CmdLine      Pointer to the command line to process\r
+  @param[out] ExitStatus  The exit status of the command. Ignored if NULL.\r
+                          Invalid if this function returns an error.\r
 \r
   @retval EFI_SUCCESS   The operation was successful\r
   @return               an error occured.\r
@@ -1607,12 +1904,18 @@ GetOperationType(
 EFI_STATUS\r
 EFIAPI\r
 ProcessNewSplitCommandLine(\r
-  IN CONST CHAR16 *CmdLine\r
+  IN CONST CHAR16       *CmdLine,\r
+  OUT      SHELL_STATUS *ExitStatus\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
@@ -1623,9 +1926,14 @@ ProcessNewSplitCommandLine(
   }\r
 \r
   if (Split == NULL) {\r
-    Status = RunSplitCommand(CmdLine, NULL, NULL);\r
+    Status = RunSplitCommand(CmdLine, NULL, NULL, ExitStatus);\r
   } else {\r
-    Status = RunSplitCommand(CmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
+    Status = RunSplitCommand(\r
+              CmdLine,\r
+              Split->SplitStdIn,\r
+              Split->SplitStdOut,\r
+              ExitStatus\r
+              );\r
   }\r
   if (EFI_ERROR(Status)) {\r
     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);\r
@@ -1634,11 +1942,11 @@ ProcessNewSplitCommandLine(
 }\r
 \r
 /**\r
-  Handle a request to change the current file system\r
+  Handle a request to change the current file system.\r
 \r
-  @param[in] CmdLine  The passed in command line\r
+  @param[in] CmdLine  The passed in command line.\r
 \r
-  @retval EFI_SUCCESS The operation was successful\r
+  @retval EFI_SUCCESS The operation was successful.\r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
@@ -1674,50 +1982,424 @@ ChangeMappedDrive(
 \r
   if found, will add "help" as argv[0], and move the rest later.\r
 \r
-  @param[in,out] Argc  The pointer to argc to update \r
-  @param[in,out] Argv  The pointer to argv to update (this is a pointer to an array of string pointers)\r
+  @param[in,out] CmdLine        pointer to the command line to update\r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
-DoHelpUpdateArgcArgv(\r
-  IN OUT UINTN *Argc,\r
-  IN OUT CHAR16 ***Argv\r
+DoHelpUpdate(\r
+  IN OUT CHAR16 **CmdLine\r
   )\r
 {\r
-  UINTN Count;\r
-  UINTN Count2;\r
+  CHAR16 *CurrentParameter;\r
+  CHAR16 *Walker;\r
+  CHAR16 *LastWalker;\r
+  CHAR16 *NewCommandLine;\r
+  EFI_STATUS Status;\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
+    LastWalker = Walker;\r
+    GetNextParameter(&Walker, &CurrentParameter);\r
+    if (StrStr(CurrentParameter, L"-?") == CurrentParameter) {\r
+      LastWalker[0] = L' ';\r
+      LastWalker[1] = L' ';\r
+      NewCommandLine = AllocateZeroPool(StrSize(L"help ") + StrSize(*CmdLine));\r
+      if (NewCommandLine == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        break;\r
+      }\r
+      StrCpy(NewCommandLine, L"help ");\r
+      StrCat(NewCommandLine, *CmdLine);\r
+      SHELL_FREE_NON_NULL(*CmdLine);\r
+      *CmdLine = NewCommandLine;\r
+      break;\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
+EFIAPI\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 occured\r
+**/\r
+EFI_STATUS\r
+EFIAPI\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
+\r
+  TrimSpaces(CmdLine);\r
+\r
   //\r
-  // Check each parameter\r
+  // update for help parsing\r
   //\r
-  for (Count = 0 ; Count < (*Argc) ; Count++) {\r
+  if (StrStr(*CmdLine, L"?") != NULL) {\r
     //\r
-    // if it's "-?" or if the first parameter is "?"\r
+    // This may do nothing if the ? does not indicate help.\r
+    // Save all the details for in the API below.\r
     //\r
-    if (StrStr((*Argv)[Count], L"-?") == (*Argv)[Count] \r
-    ||  ((*Argv)[0][0] == L'?' && (*Argv)[0][1] == CHAR_NULL)\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 upadate 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
+\r
+  @param[out] ExitStatus      The exit code of the command. Ignored if NULL.\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
+RunInternalCommand(\r
+  IN CONST CHAR16                   *CmdLine,\r
+  IN       CHAR16                   *FirstParameter,\r
+  IN EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,\r
+  OUT      SHELL_STATUS             *ExitStatus OPTIONAL\r
+)\r
+{\r
+  EFI_STATUS                Status;\r
+  UINTN                     Argc;\r
+  CHAR16                    **Argv;\r
+  SHELL_STATUS              CommandReturnedStatus;\r
+  BOOLEAN                   LastError;\r
+\r
+  //\r
+  // get the argc and argv updated for internal commands\r
+  //\r
+  Status = UpdateArgcArgv(ParamProtocol, CmdLine, &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
       //\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
+      // Update last error status.\r
+      // some commands do not update last error.\r
       //\r
-      FreePool((*Argv)[Count]);\r
-      (*Argv)[Count] = NULL;\r
-      for (Count2 = Count ; (Count2 + 1) < (*Argc) ; Count2++) {\r
-        (*Argv)[Count2] = (*Argv)[Count2+1];\r
+      if (LastError) {\r
+        SetLastError(CommandReturnedStatus);\r
       }\r
-      (*Argv)[Count2] = NULL;\r
-      for (Count2 = (*Argc) -1 ; Count2 > 0 ; Count2--) {\r
-        (*Argv)[Count2] = (*Argv)[Count2-1];\r
+      if (ExitStatus != NULL) {\r
+        *ExitStatus = CommandReturnedStatus;\r
       }\r
-      (*Argv)[0] = NULL;\r
-      (*Argv)[0] = StrnCatGrow(&(*Argv)[0], NULL, L"help", 0);\r
-      if ((*Argv)[0] == NULL) {\r
-        return (EFI_OUT_OF_RESOURCES);\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
-      break;\r
     }\r
   }\r
-  return (EFI_SUCCESS);\r
+\r
+  //\r
+  // This is guarenteed 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 scipt 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
+  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
+\r
+  @param[out] ExitStatus      The exit code of the command or file.\r
+                              Ignored if NULL.\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
+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      SHELL_STATUS             *ExitStatus\r
+)\r
+{\r
+  EFI_STATUS                Status;\r
+  CHAR16                    *CommandWithPath;\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(\r
+                CmdLine,\r
+                FirstParameter,\r
+                ParamProtocol,\r
+                &CalleeExitStatus\r
+                );\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
+          Status = RunScriptFile (\r
+                    CommandWithPath,\r
+                    NULL,\r
+                    CmdLine,\r
+                    ParamProtocol,\r
+                    &CalleeExitStatus\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
+            NULL,\r
+            NULL\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_SUCCESS;\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
+  if (ExitStatus != NULL) {\r
+    *ExitStatus = CalleeExitStatus;\r
+  }\r
+\r
+  return (Status);\r
+}\r
+\r
+/**\r
+  Function to setup StdIn, StdErr, StdOut, and then 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
+\r
+  @param[out] ExitStatus      The exit code of the command or file.\r
+                              Ignored if NULL.\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
+SetupAndRunCommandOrFile(\r
+  IN   SHELL_OPERATION_TYPES          Type,\r
+  IN   CHAR16                         *CmdLine,\r
+  IN   CHAR16                         *FirstParameter,\r
+  IN   EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,\r
+  OUT  SHELL_STATUS                   *ExitStatus\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
+\r
+  //\r
+  // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII\r
+  //\r
+  Status = UpdateStdInStdOutStdErr(ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
+\r
+  //\r
+  // The StdIn, StdOut, and StdErr are set up.\r
+  // Now run the command, script, or application\r
+  //\r
+  if (!EFI_ERROR(Status)) {\r
+    Status = RunCommandOrFile(\r
+              Type,\r
+              CmdLine,\r
+              FirstParameter,\r
+              ParamProtocol,\r
+              ExitStatus\r
+              );\r
+  }\r
+\r
+  //\r
+  // Now print errors\r
+  //\r
+  if (EFI_ERROR(Status)) {\r
+    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));\r
+  }\r
+\r
+  //\r
+  // put back the original StdIn, StdOut, and StdErr\r
+  //\r
+  RestoreStdInStdOutStdErr(ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
+\r
+  return (Status);\r
 }\r
 \r
 /**\r
@@ -1727,6 +2409,7 @@ DoHelpUpdateArgcArgv(
   command or dispatch an external application.\r
 \r
   @param[in] CmdLine      The command line to parse.\r
+  @param[out] ExitStatus  The exit code of the command. Ignored if NULL.\r
 \r
   @retval EFI_SUCCESS     The command was completed.\r
   @retval EFI_ABORTED     The command's operation was aborted.\r
@@ -1734,35 +2417,21 @@ DoHelpUpdateArgcArgv(
 EFI_STATUS\r
 EFIAPI\r
 RunCommand(\r
-  IN CONST CHAR16   *CmdLine\r
+  IN CONST CHAR16         *CmdLine,\r
+  OUT      SHELL_STATUS   *ExitStatus\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                    *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
   CHAR16                    *CleanOriginal;\r
+  CHAR16                    *FirstParameter;\r
+  CHAR16                    *TempWalker;\r
+  SHELL_OPERATION_TYPES     Type;\r
 \r
   ASSERT(CmdLine != NULL);\r
   if (StrLen(CmdLine) == 0) {\r
     return (EFI_SUCCESS);\r
   }\r
 \r
-  CommandName         = NULL;\r
-  CommandWithPath     = NULL;\r
-  DevPath             = NULL;\r
   Status              = EFI_SUCCESS;\r
   CleanOriginal       = NULL;\r
 \r
@@ -1777,180 +2446,65 @@ RunCommand(
   // 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
-  Status = ShellSubstituteAliases(&CleanOriginal);\r
+  Status = ProcessCommandLineToFinal(&CleanOriginal);\r
   if (EFI_ERROR(Status)) {\r
+    SHELL_FREE_NON_NULL(CleanOriginal);\r
     return (Status);\r
   }\r
 \r
-  Status = ShellSubstituteVariables(&CleanOriginal);\r
-  if (EFI_ERROR(Status)) {\r
-    return (Status);\r
-  }\r
-\r
-  TrimSpaces(&CleanOriginal);\r
-\r
   //\r
   // We dont do normal processing with a split command line (output from one command input to another)\r
   //\r
   if (ContainsSplit(CleanOriginal)) {\r
-    Status = ProcessNewSplitCommandLine(CleanOriginal);\r
-  } else {\r
-    //\r
-    // If this is a mapped drive change handle that...\r
-    //\r
-    if (CleanOriginal[(StrLen(CleanOriginal)-1)] == L':' && StrStr(CleanOriginal, L" ") == NULL) {\r
-      Status = ChangeMappedDrive(CleanOriginal);\r
-      SHELL_FREE_NON_NULL(CleanOriginal);\r
-      return (Status);\r
-    }\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, CleanOriginal, &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
-      TrimSpaces(&CleanOriginal);\r
-    \r
-      //\r
-      // get the argc and argv updated for internal commands\r
-      //\r
-      Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, CleanOriginal, &Argv, &Argc);\r
-      ASSERT_EFI_ERROR(Status);\r
-\r
-      if (StrStr(CleanOriginal, L"?") != NULL) {\r
-        Status = DoHelpUpdateArgcArgv(\r
-          &ShellInfoObject.NewShellParametersProtocol->Argc,\r
-          &ShellInfoObject.NewShellParametersProtocol->Argv);\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
-        //\r
-        // run an external file (or script)\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
-              CleanOriginal,\r
-              NULL,\r
-              &StatusCode\r
-             );\r
+    Status = ProcessNewSplitCommandLine(CleanOriginal, ExitStatus);\r
+    SHELL_FREE_NON_NULL(CleanOriginal);\r
+    return (Status);\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
+  // We need the first parameter information so we can determine the operation type\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
+  GetNextParameter(&TempWalker, &FirstParameter);\r
 \r
+  //\r
+  // Depending on the first parameter we change the behavior\r
+  //\r
+  switch (Type = GetOperationType(FirstParameter)) {\r
+    case   File_Sys_Change:\r
+      Status = ChangeMappedDrive(CleanOriginal);\r
+      break;\r
+    case   Internal_Command:\r
+    case   Script_File_Name:\r
+    case   Efi_Application:\r
+      Status = SetupAndRunCommandOrFile(\r
+                Type,\r
+                CleanOriginal,\r
+                FirstParameter,\r
+                ShellInfoObject.NewShellParametersProtocol,\r
+                ExitStatus\r
+                );\r
+      break;\r
+    default:\r
       //\r
-      // Print some error info.\r
+      // Whatever was typed, it was invalid.\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
-    }\r
+      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
+      SetLastError(SHELL_NOT_FOUND);\r
+      break;\r
   }\r
-\r
-  SHELL_FREE_NON_NULL(CommandName);\r
-  SHELL_FREE_NON_NULL(CommandWithPath);\r
\r
   SHELL_FREE_NON_NULL(CleanOriginal);\r
+  SHELL_FREE_NON_NULL(FirstParameter);\r
 \r
   return (Status);\r
 }\r
@@ -1993,13 +2547,16 @@ IsValidCommandName(
   @param[in] Handle             The handle to the already opened file.\r
   @param[in] Name               The name of the script file.\r
 \r
+  @param[out] ExitStatus      The exit code of the script. Ignored if NULL.\r
+\r
   @retval EFI_SUCCESS           the script completed sucessfully\r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
 RunScriptFileHandle (\r
-  IN SHELL_FILE_HANDLE  Handle,\r
-  IN CONST CHAR16       *Name\r
+  IN  SHELL_FILE_HANDLE  Handle,\r
+  IN  CONST CHAR16       *Name,\r
+  OUT SHELL_STATUS       *ExitStatus\r
   )\r
 {\r
   EFI_STATUS          Status;\r
@@ -2015,8 +2572,11 @@ RunScriptFileHandle (
   CONST CHAR16        *CurDir;\r
   UINTN               LineCount;\r
   CHAR16              LeString[50];\r
+  SHELL_STATUS        CalleeExitStatus;\r
 \r
   ASSERT(!ShellCommandGetScriptExit());\r
+  \r
+  CalleeExitStatus = SHELL_SUCCESS;\r
 \r
   PreScriptEchoState = ShellCommandGetEchoState();\r
 \r
@@ -2067,11 +2627,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
@@ -2198,7 +2760,7 @@ RunScriptFileHandle (
             //\r
             PreCommandEchoState = ShellCommandGetEchoState();\r
             ShellCommandSetEchoState(FALSE);\r
-            Status = RunCommand(CommandLine3+1);\r
+            Status = RunCommand(CommandLine3+1, NULL);\r
 \r
             //\r
             // If command was "@echo -off" or "@echo -on" then don't restore echo state\r
@@ -2220,7 +2782,7 @@ RunScriptFileHandle (
               }\r
               ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);\r
             }\r
-            Status = RunCommand(CommandLine3);\r
+            Status = RunCommand(CommandLine3, NULL);\r
           }\r
         }\r
 \r
@@ -2228,7 +2790,8 @@ RunScriptFileHandle (
           //\r
           // ShellCommandGetExitCode() always returns a UINT64\r
           //\r
-          UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellCommandGetExitCode());\r
+          CalleeExitStatus = (SHELL_STATUS) ShellCommandGetExitCode();\r
+          UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", CalleeExitStatus);\r
           DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
           InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
 \r
@@ -2240,9 +2803,11 @@ RunScriptFileHandle (
           break;\r
         }\r
         if (EFI_ERROR(Status)) {\r
+          CalleeExitStatus = (SHELL_STATUS) Status;\r
           break;\r
         }\r
         if (ShellCommandGetExit()) {\r
+          CalleeExitStatus = (SHELL_STATUS) ShellCommandGetExitCode();\r
           break;\r
         }\r
       }\r
@@ -2274,6 +2839,11 @@ RunScriptFileHandle (
   if (ShellCommandGetCurrentScriptFile()==NULL) {\r
     ShellCommandSetEchoState(PreScriptEchoState);\r
   }\r
+\r
+  if (ExitStatus != NULL) {\r
+    *ExitStatus = CalleeExitStatus;\r
+  }\r
+\r
   return (EFI_SUCCESS);\r
 }\r
 \r
@@ -2281,30 +2851,65 @@ 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
+  @param[out] ExitStatus      The exit code of the script. Ignored if NULL.\r
 \r
   @retval EFI_SUCCESS           the script completed sucessfully\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
+  OUT SHELL_STATUS                   *ExitStatus\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, &Argv, &Argc);\r
+  if (!EFI_ERROR(Status)) {\r
 \r
-  Status = RunScriptFileHandle(FileHandle, ScriptPath);\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, ExitStatus);\r
 \r
-  ShellCloseFile(&FileHandle);\r
+        //\r
+        // now close the file\r
+        //\r
+        ShellCloseFile(&FileHandle);\r
+      }\r
+    } else {\r
+      Status = RunScriptFileHandle(Handle, ScriptPath, ExitStatus);\r
+    }\r
+  }\r
+\r
+  //\r
+  // This is guarenteed 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