]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ShellPkg/Application/Shell/Shell.c
ShellPkg: Fix the support for command line comments.
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
index 0cf09ec76b70b07d5aa62e227514449f52984888..29c65b7f27eda34c5e0c558a27dab7004c3e28f7 100644 (file)
@@ -1,8 +1,8 @@
 /** @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 - 2014, Intel Corporation. All rights reserved.<BR>\r
+  (C) Copyright 2013-2014, 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
   which accompanies this distribution.  The full text of the license may be found at\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
@@ -144,7 +143,7 @@ ContainsSplit(
 {\r
   CONST CHAR16 *TempSpot;\r
   TempSpot = FindSplit(CmdLine);\r
-  return (TempSpot != NULL && *TempSpot != CHAR_NULL);\r
+  return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));\r
 }\r
 \r
 /**\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
@@ -493,6 +509,7 @@ 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
@@ -506,6 +523,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 +533,7 @@ UefiMain (
     }\r
   }\r
 \r
+FreeResources:\r
   //\r
   // uninstall protocols / free memory / etc...\r
   //\r
@@ -576,10 +595,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 +769,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 +802,180 @@ 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
+  //\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
-        //\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
+      //\r
+      // First argument should be Shell.efi image name\r
+      //\r
+      if (LoopVar == 0) {\r
+        continue;\r
+      }\r
+\r
+      ShellInfoObject.ShellInitSettings.FileName = AllocateCopyPool(StrSize(CurrentArg), 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
+      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 +986,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
@@ -909,12 +1025,12 @@ DoStartupScript(
     if (FileStringPath == NULL) {\r
       return (EFI_OUT_OF_RESOURCES);\r
     }\r
-    StrCpy(FileStringPath, ShellInfoObject.ShellInitSettings.FileName);\r
+    StrnCpy(FileStringPath, ShellInfoObject.ShellInitSettings.FileName, NewSize/sizeof(CHAR16) -1);\r
     if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
-      StrCat(FileStringPath, L" ");\r
-      StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);\r
+      StrnCat(FileStringPath, L" ", NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);\r
+      StrnCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions, NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);\r
     }\r
-    Status = RunCommand(FileStringPath);\r
+    Status = RunCommand(FileStringPath, ExitStatus);\r
     FreePool(FileStringPath);\r
     return (Status);\r
 \r
@@ -991,7 +1107,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 +1124,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 +1195,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
@@ -1117,9 +1245,8 @@ AddLineToCommandHistory(
 \r
   Node = AllocateZeroPool(sizeof(BUFFER_LIST));\r
   ASSERT(Node != NULL);\r
-  Node->Buffer = AllocateZeroPool(StrSize(Buffer));\r
+  Node->Buffer = AllocateCopyPool(StrSize(Buffer), Buffer);\r
   ASSERT(Node->Buffer != NULL);\r
-  StrCpy(Node->Buffer, Buffer);\r
 \r
   InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);\r
 }\r
@@ -1150,11 +1277,117 @@ 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
+  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
+      FirstPercent[0] = L'\"';\r
+      SecondPercent[0] = L'\"';\r
+\r
+      //\r
+      // We need to remove from FirstPercent to SecondPercent\r
+      //\r
+      CopyMem(FirstPercent + 1, SecondPercent, StrSize(SecondPercent));\r
+      CurrentLocator = FirstPercent + 2;\r
+      continue;\r
+    }\r
+    ASSERT(FirstQuote < SecondPercent);\r
+    CurrentLocator = FirstQuote;\r
+  }\r
   return (EFI_SUCCESS);\r
 }\r
 \r
@@ -1180,7 +1413,6 @@ ShellConvertVariables (
   CHAR16              *NewCommandLine1;\r
   CHAR16              *NewCommandLine2;\r
   CHAR16              *Temp;\r
-  CHAR16              *Temp2;\r
   UINTN               ItemSize;\r
   CHAR16              *ItemTemp;\r
   SCRIPT_FILE         *CurrentScriptFile;\r
@@ -1241,7 +1473,7 @@ ShellConvertVariables (
   //\r
   // now do the replacements...\r
   //\r
-  NewCommandLine1 = AllocateZeroPool(NewSize);\r
+  NewCommandLine1 = AllocateCopyPool(NewSize, OriginalCommandLine);\r
   NewCommandLine2 = AllocateZeroPool(NewSize);\r
   ItemTemp        = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));\r
   if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {\r
@@ -1250,16 +1482,15 @@ ShellConvertVariables (
     SHELL_FREE_NON_NULL(ItemTemp);\r
     return (NULL);\r
   }\r
-  StrCpy(NewCommandLine1, OriginalCommandLine);\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
+    StrnCpy(ItemTemp, L"%", ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16))-1);\r
+    StrnCat(ItemTemp, MasterEnvList, ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16))-1 - StrLen(ItemTemp));\r
+    StrnCat(ItemTemp, L"%", ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16))-1 - StrLen(ItemTemp));\r
     ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);\r
-    StrCpy(NewCommandLine1, NewCommandLine2);\r
+    StrnCpy(NewCommandLine1, NewCommandLine2, NewSize/sizeof(CHAR16)-1);\r
   }\r
   if (CurrentScriptFile != NULL) {\r
     for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)\r
@@ -1267,52 +1498,20 @@ 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
+    StrnCpy(NewCommandLine1, NewCommandLine2, NewSize/sizeof(CHAR16)-1);\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
-    }\r
-\r
+    StripUnreplacedEnvironmentVariables(NewCommandLine1);\r
   }\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
+  StrnCpy(NewCommandLine1, NewCommandLine2, NewSize/sizeof(CHAR16)-1);\r
   \r
   FreePool(NewCommandLine2);\r
   FreePool(ItemTemp);\r
@@ -1327,6 +1526,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 +1537,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 +1592,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 +1607,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 +1635,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] 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 +1660,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
@@ -1529,6 +1730,14 @@ ShellSubstituteAliases(
 \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
 EFIAPI\r
@@ -1545,17 +1754,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
@@ -1570,7 +1781,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
@@ -1578,27 +1789,140 @@ 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, StrSize(CmdLine));\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[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
 **/\r
 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
@@ -1609,9 +1933,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
@@ -1620,11 +1949,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
@@ -1656,255 +1985,573 @@ ChangeMappedDrive(
 }\r
 \r
 /**\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
+  Reprocess the command line to direct all -? to the help command.\r
 \r
-  @param[in] CmdLine      The command line to parse.\r
+  if found, will add "help" as argv[0], and move the rest later.\r
 \r
-  @retval EFI_SUCCESS     The command was completed.\r
-  @retval EFI_ABORTED     The command's operation was aborted.\r
+  @param[in,out] CmdLine        pointer to the command line to update\r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
-RunCommand(\r
-  IN CONST CHAR16   *CmdLine\r
+DoHelpUpdate(\r
+  IN OUT CHAR16 **CmdLine\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
-  UINTN                     Count;\r
-  UINTN                     Count2;\r
-  CHAR16                    *CleanOriginal;\r
-\r
-  ASSERT(CmdLine != NULL);\r
-  if (StrLen(CmdLine) == 0) {\r
-    return (EFI_SUCCESS);\r
-  }\r
+  CHAR16 *CurrentParameter;\r
+  CHAR16 *Walker;\r
+  CHAR16 *LastWalker;\r
+  CHAR16 *NewCommandLine;\r
+  EFI_STATUS Status;\r
 \r
-  CommandName         = NULL;\r
-  CommandWithPath     = NULL;\r
-  DevPath             = NULL;\r
-  Status              = EFI_SUCCESS;\r
-  CleanOriginal       = NULL;\r
+  Status = EFI_SUCCESS;\r
 \r
-  CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);\r
-  if (CleanOriginal == NULL) {\r
+  CurrentParameter = AllocateZeroPool(StrSize(*CmdLine));\r
+  if (CurrentParameter == NULL) {\r
     return (EFI_OUT_OF_RESOURCES);\r
   }\r
 \r
-  TrimSpaces(&CleanOriginal);\r
+  Walker = *CmdLine;\r
+  while(Walker != NULL && *Walker != CHAR_NULL) {\r
+    LastWalker = Walker;\r
+    GetNextParameter(&Walker, &CurrentParameter, StrSize(*CmdLine));\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
 \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
+      // We know the space is sufficient since we just calculated it.\r
+      //\r
+      StrnCpy(NewCommandLine, L"help ", 5);\r
+      StrnCat(NewCommandLine, *CmdLine, StrLen(*CmdLine));\r
+      SHELL_FREE_NON_NULL(*CmdLine);\r
+      *CmdLine = NewCommandLine;\r
+      break;\r
     }\r
-    return (EFI_SUCCESS);\r
   }\r
 \r
-  Status = ShellSubstituteAliases(&CleanOriginal);\r
-  if (EFI_ERROR(Status)) {\r
-    return (Status);\r
-  }\r
+  SHELL_FREE_NON_NULL(CurrentParameter);\r
 \r
-  Status = ShellSubstituteVariables(&CleanOriginal);\r
-  if (EFI_ERROR(Status)) {\r
-    return (Status);\r
-  }\r
+  return (Status);\r
+}\r
 \r
-  TrimSpaces(&CleanOriginal);\r
+/**\r
+  Function to update the shell variable "lasterror".\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
+  @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
+  ASSERT (*CmdLine != NULL);\r
+\r
+  TrimSpaces(CmdLine);\r
+\r
+  //\r
+  // update for help parsing\r
+  //\r
+  if (StrStr(*CmdLine, L"?") != NULL) {\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
+    // 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
-    ///@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
+  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
-    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
+  @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
+  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, &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
+      // Update last error status.\r
+      // some commands do not update last error.\r
+      //\r
+      if (LastError) {\r
+        SetLastError(CommandReturnedStatus);\r
       }\r
-    } else {\r
-      TrimSpaces(&CleanOriginal);\r
-    \r
+      if (ExitStatus != NULL) {\r
+        *ExitStatus = CommandReturnedStatus;\r
+      }\r
+\r
       //\r
-      // get the argc and argv updated for internal commands\r
+      // Pass thru the exitcode from the app.\r
       //\r
-      Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, CleanOriginal, &Argv, &Argc);\r
-      ASSERT_EFI_ERROR(Status);\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
-      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
+  // 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
+  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
+\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
+  EFI_STATUS                StartStatus;\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
-      // command or file?\r
+      // Process a relative path and also check in the path environment variable\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
+      if (CommandWithPath == NULL) {\r
+        CommandWithPath = ShellFindFilePathEx(FirstParameter, mExecutableExtensions);\r
+      }\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
+      // 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
-          // Pass thru the exitcode from the app.\r
+          // Get the device path of the application image\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
+          DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
+          if (DevPath == NULL){\r
+            Status = EFI_OUT_OF_RESOURCES;\r
+            break;\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
+          // Execute the device path\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
+          Status = InternalShellExecuteDevicePath(\r
+            &gImageHandle,\r
+            DevPath,\r
+            CmdLine,\r
+            NULL,\r
+            &StartStatus,\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
-            DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
-            ASSERT(DevPath != NULL);\r
-            Status = InternalShellExecuteDevicePath(\r
-              &gImageHandle,\r
-              DevPath,\r
-              CleanOriginal,\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
+            CalleeExitStatus = (SHELL_STATUS) StartStatus;\r
           }\r
-        }\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
-      // Print some error info.\r
+      // Do nothing.\r
       //\r
-      if (EFI_ERROR(Status)) {\r
-        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));\r
-      }\r
+      break;\r
+  }\r
 \r
-      CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
+  SHELL_FREE_NON_NULL(CommandWithPath);\r
 \r
-      RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);\r
+  if (ExitStatus != NULL) {\r
+    *ExitStatus = CalleeExitStatus;\r
+  }\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
+  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
+    TrimSpaces(&CmdLine);\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
+  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] 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
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RunCommand(\r
+  IN CONST CHAR16         *CmdLine,\r
+  OUT      SHELL_STATUS   *ExitStatus\r
+  )\r
+{\r
+  EFI_STATUS                Status;\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
+  Status              = EFI_SUCCESS;\r
+  CleanOriginal       = 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
+  // 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
+  // Afterward, again remove spaces, in case any were between the last command-parameter and '#'.\r
+  //\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
-  SHELL_FREE_NON_NULL(CommandName);\r
-  SHELL_FREE_NON_NULL(CommandWithPath);\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
+    SHELL_FREE_NON_NULL(CleanOriginal);\r
+    return (EFI_SUCCESS);\r
+  }\r
+\r
+  Status = ProcessCommandLineToFinal(&CleanOriginal);\r
+  if (EFI_ERROR(Status)) {\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
+  //\r
+  if (ContainsSplit(CleanOriginal)) {\r
+    Status = ProcessNewSplitCommandLine(CleanOriginal, ExitStatus);\r
+    SHELL_FREE_NON_NULL(CleanOriginal);\r
+    return (Status);\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, StrSize(CleanOriginal));\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 (FirstParameter);\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
+      // Whatever was typed, it was invalid.\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(CleanOriginal);\r
+  SHELL_FREE_NON_NULL(FirstParameter);\r
 \r
   return (Status);\r
 }\r
@@ -1947,13 +2594,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
@@ -1969,8 +2619,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
@@ -2021,11 +2674,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
@@ -2062,15 +2717,17 @@ RunScriptFileHandle (
       ; // conditional increment in the body of the loop\r
   ){\r
     ASSERT(CommandLine2 != NULL);\r
-    StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl);\r
+    StrnCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl, PcdGet16(PcdShellPrintBufferSize)/sizeof(CHAR16)-1);\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
@@ -2081,7 +2738,7 @@ 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
+      StrnCpy(CommandLine, CommandLine2, PcdGet16(PcdShellPrintBufferSize)/sizeof(CHAR16)-1);\r
 \r
       //\r
       // Remove the %0 to %9 from the command line (if we have some arguments)\r
@@ -2089,34 +2746,34 @@ 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, PcdGet16 (PcdShellPrintBufferSize), 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, PcdGet16 (PcdShellPrintBufferSize), 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, PcdGet16 (PcdShellPrintBufferSize), 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, PcdGet16 (PcdShellPrintBufferSize), 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, PcdGet16 (PcdShellPrintBufferSize), 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, PcdGet16 (PcdShellPrintBufferSize), 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, PcdGet16 (PcdShellPrintBufferSize), 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, PcdGet16 (PcdShellPrintBufferSize), 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, PcdGet16 (PcdShellPrintBufferSize), 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, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, FALSE);\r
             ASSERT_EFI_ERROR(Status);\r
             break;\r
           case 0:\r
@@ -2133,7 +2790,7 @@ RunScriptFileHandle (
       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
+      StrnCpy(CommandLine2, CommandLine, PcdGet16(PcdShellPrintBufferSize)/sizeof(CHAR16)-1);\r
 \r
       LastCommand = NewScriptFile->CurrentCommand;\r
 \r
@@ -2152,7 +2809,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
@@ -2174,7 +2831,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
@@ -2182,7 +2839,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
@@ -2194,9 +2852,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
@@ -2228,6 +2888,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
@@ -2235,30 +2900,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