]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ShellPkg/Application/Shell/Shell.c
Add default way for switch in function 'RunCommandOrFile' of 'Shell.c'.
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
index 3401bc064446abb8bb2520e6061e451cabc2fc24..ef821a5c5d021bc5ce20dfb8abb6ba37774af35b 100644 (file)
@@ -1,7 +1,8 @@
 /** @file\r
   This is THE shell (application)\r
 \r
-  Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2009 - 2013, 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
   which accompanies this distribution.  The full text of the license may be found at\r
@@ -23,22 +24,24 @@ SHELL_INFO ShellInfoObject = {
   FALSE,\r
   FALSE,\r
   {\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
+    {{\r
+      0,\r
+      0,\r
+      0,\r
+      0,\r
+      0,\r
+      0,\r
+      0,\r
+      0,\r
+      0\r
+    }},\r
     0,\r
     NULL,\r
     NULL\r
   },\r
-  {0,0},\r
+  {{NULL, NULL}, NULL},\r
   {\r
-    {0,0},\r
+    {{NULL, NULL}, NULL},\r
     0,\r
     0,\r
     TRUE\r
@@ -50,12 +53,175 @@ SHELL_INFO ShellInfoObject = {
   NULL,\r
   NULL,\r
   NULL,\r
-  {0,0,NULL,NULL},\r
-  {0,0},\r
+  {{NULL, NULL}, NULL, NULL},\r
+  {{NULL, NULL}, NULL, NULL},\r
+  NULL,\r
+  NULL,\r
+  NULL,\r
+  NULL,\r
+  NULL,\r
+  NULL,\r
+  NULL,\r
+  NULL,\r
+  FALSE\r
 };\r
 \r
 STATIC CONST CHAR16 mScriptExtension[]      = L".NSH";\r
 STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";\r
+STATIC CONST CHAR16 mStartupScript[]        = L"startup.nsh";\r
+\r
+/**\r
+  Cleans off leading and trailing spaces and tabs\r
+\r
+  @param[in] String pointer to the string to trim them off\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TrimSpaces(\r
+  IN CHAR16 **String\r
+  )\r
+{\r
+  ASSERT(String != NULL);\r
+  ASSERT(*String!= NULL);\r
+  //\r
+  // Remove any spaces and tabs at the beginning of the (*String).\r
+  //\r
+  while (((*String)[0] == L' ') || ((*String)[0] == L'\t')) {\r
+    CopyMem((*String), (*String)+1, StrSize((*String)) - sizeof((*String)[0]));\r
+  }\r
+\r
+  //\r
+  // Remove any spaces and tabs at the end of the (*String).\r
+  //\r
+  while ((StrLen (*String) > 0) && (((*String)[StrLen((*String))-1] == L' ') || ((*String)[StrLen((*String))-1] == L'\t'))) {\r
+    (*String)[StrLen((*String))-1] = CHAR_NULL;\r
+  }\r
+\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+  Find a command line contains a split operation\r
+\r
+  @param[in] CmdLine      The command line to parse.\r
+\r
+  @retval                 A pointer to the | character in CmdLine or NULL if not present.\r
+**/\r
+CONST\r
+CHAR16*\r
+EFIAPI\r
+FindSplit(\r
+  IN CONST CHAR16 *CmdLine\r
+  )\r
+{\r
+  CONST CHAR16 *TempSpot;\r
+  TempSpot = NULL;\r
+  if (StrStr(CmdLine, L"|") != NULL) {\r
+    for (TempSpot = CmdLine ; TempSpot != NULL && *TempSpot != CHAR_NULL ; TempSpot++) {\r
+      if (*TempSpot == L'^' && *(TempSpot+1) == L'|') {\r
+        TempSpot++;\r
+      } else if (*TempSpot == L'|') {\r
+        break;\r
+      }\r
+    }\r
+  }\r
+  return (TempSpot);\r
+}\r
+\r
+/**\r
+  Determine if a command line contains a split operation\r
+\r
+  @param[in] CmdLine      The command line to parse.\r
+\r
+  @retval TRUE            CmdLine has a valid split.\r
+  @retval FALSE           CmdLine does not have a valid split.\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+ContainsSplit(\r
+  IN CONST CHAR16 *CmdLine\r
+  )\r
+{\r
+  CONST CHAR16 *TempSpot;\r
+  TempSpot = FindSplit(CmdLine);\r
+  return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));\r
+}\r
+\r
+/**\r
+  Function to start monitoring for CTRL-S using SimpleTextInputEx.  This \r
+  feature's enabled state was not known when the shell initially launched.\r
+\r
+  @retval EFI_SUCCESS           The feature is enabled.\r
+  @retval EFI_OUT_OF_RESOURCES  There is not enough mnemory available.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InternalEfiShellStartCtrlSMonitor(\r
+  VOID\r
+  )\r
+{\r
+  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;\r
+  EFI_KEY_DATA                      KeyData;\r
+  EFI_STATUS                        Status;\r
+\r
+  Status = gBS->OpenProtocol(\r
+    gST->ConsoleInHandle,\r
+    &gEfiSimpleTextInputExProtocolGuid,\r
+    (VOID**)&SimpleEx,\r
+    gImageHandle,\r
+    NULL,\r
+    EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
+  if (EFI_ERROR(Status)) {\r
+    ShellPrintHiiEx(\r
+      -1, \r
+      -1, \r
+      NULL,\r
+      STRING_TOKEN (STR_SHELL_NO_IN_EX),\r
+      ShellInfoObject.HiiHandle);\r
+    return (EFI_SUCCESS);\r
+  }\r
+\r
+  KeyData.KeyState.KeyToggleState = 0;\r
+  KeyData.Key.ScanCode            = 0;\r
+  KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;\r
+  KeyData.Key.UnicodeChar         = L's';\r
+\r
+  Status = SimpleEx->RegisterKeyNotify(\r
+    SimpleEx,\r
+    &KeyData,\r
+    NotificationFunction,\r
+    &ShellInfoObject.CtrlSNotifyHandle1);\r
+  \r
+  KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
+  if (!EFI_ERROR(Status)) {\r
+    Status = SimpleEx->RegisterKeyNotify(\r
+      SimpleEx,\r
+      &KeyData,\r
+      NotificationFunction,\r
+      &ShellInfoObject.CtrlSNotifyHandle2);\r
+  }\r
+  KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;\r
+  KeyData.Key.UnicodeChar         = 19;\r
+\r
+  if (!EFI_ERROR(Status)) {\r
+    Status = SimpleEx->RegisterKeyNotify(\r
+      SimpleEx,\r
+      &KeyData,\r
+      NotificationFunction,\r
+      &ShellInfoObject.CtrlSNotifyHandle3);\r
+  }  \r
+  KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
+  if (!EFI_ERROR(Status)) {\r
+    Status = SimpleEx->RegisterKeyNotify(\r
+      SimpleEx,\r
+      &KeyData,\r
+      NotificationFunction,\r
+      &ShellInfoObject.CtrlSNotifyHandle4);\r
+  }\r
+  return (Status);\r
+}\r
+\r
+\r
 \r
 /**\r
   The entry point for the application.\r
@@ -74,17 +240,11 @@ UefiMain (
   IN EFI_SYSTEM_TABLE  *SystemTable\r
   )\r
 {\r
-  EFI_STATUS  Status;\r
-  CHAR16      *TempString;\r
-  UINTN       Size;\r
-//    EFI_INPUT_KEY                 Key;\r
-\r
-//  gST->ConOut->OutputString(gST->ConOut, L"ReadKeyStroke Calling\r\n");\r
-//\r
-//  Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
-//\r
-//  gST->ConOut->OutputString(gST->ConOut, L"ReadKeyStroke Done\r\n");\r
-//  gBS->Stall (1000000);\r
+  EFI_STATUS                      Status;\r
+  CHAR16                          *TempString;\r
+  UINTN                           Size;\r
+  EFI_HANDLE                      ConInHandle;\r
+  EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *OldConIn;\r
 \r
   if (PcdGet8(PcdShellSupportLevel) > 3) {\r
     return (EFI_UNSUPPORTED);\r
@@ -94,7 +254,9 @@ UefiMain (
   // Clear the screen\r
   //\r
   Status = gST->ConOut->ClearScreen(gST->ConOut);\r
-  ASSERT_EFI_ERROR(Status);\r
+  if (EFI_ERROR(Status)) {\r
+    return (Status);\r
+  }\r
 \r
   //\r
   // Populate the global structure from PCDs\r
@@ -136,178 +298,223 @@ 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
-  ASSERT_EFI_ERROR(Status);\r
-\r
-  //\r
-  // Enable the cursor to be visible\r
-  //\r
-  gST->ConOut->EnableCursor (gST->ConOut, TRUE);\r
+  if (!EFI_ERROR(Status)) {\r
+    //\r
+    // Enable the cursor to be visible\r
+    //\r
+    gST->ConOut->EnableCursor (gST->ConOut, TRUE);\r
 \r
-  //\r
-  // If supporting EFI 1.1 we need to install HII protocol\r
-  // only do this if PcdShellRequireHiiPlatform == FALSE\r
-  //\r
-  // remove EFI_UNSUPPORTED check above when complete.\r
-  ///@todo add support for Framework HII\r
+    //\r
+    // If supporting EFI 1.1 we need to install HII protocol\r
+    // only do this if PcdShellRequireHiiPlatform == FALSE\r
+    //\r
+    // remove EFI_UNSUPPORTED check above when complete.\r
+    ///@todo add support for Framework HII\r
 \r
-  //\r
-  // install our (solitary) HII package\r
-  //\r
-  ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);\r
-  if (ShellInfoObject.HiiHandle == NULL) {\r
-    if (PcdGetBool(PcdShellSupportFrameworkHii)) {\r
-      ///@todo Add our package into Framework HII\r
-    }\r
+    //\r
+    // install our (solitary) HII package\r
+    //\r
+    ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);\r
     if (ShellInfoObject.HiiHandle == NULL) {\r
-      return (EFI_NOT_STARTED);\r
+      if (PcdGetBool(PcdShellSupportFrameworkHii)) {\r
+        ///@todo Add our package into Framework HII\r
+      }\r
+      if (ShellInfoObject.HiiHandle == NULL) {\r
+        return (EFI_NOT_STARTED);\r
+      }\r
     }\r
-  }\r
-\r
-  //\r
-  // create and install the EfiShellParametersProtocol\r
-  //\r
-  Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);\r
-  ASSERT_EFI_ERROR(Status);\r
-  ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL);\r
 \r
-  //\r
-  // create and install the EfiShellProtocol\r
-  //\r
-  Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol);\r
-  ASSERT_EFI_ERROR(Status);\r
-  ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL);\r
-\r
-  //\r
-  // Now initialize the shell library (it requires Shell Parameters protocol)\r
-  //\r
-  Status = ShellInitialize();\r
-  ASSERT_EFI_ERROR(Status);\r
+    //\r
+    // create and install the EfiShellParametersProtocol\r
+    //\r
+    Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);\r
+    ASSERT_EFI_ERROR(Status);\r
+    ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL);\r
 \r
-  Status = CommandInit();\r
-  ASSERT_EFI_ERROR(Status);\r
+    //\r
+    // create and install the EfiShellProtocol\r
+    //\r
+    Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol);\r
+    ASSERT_EFI_ERROR(Status);\r
+    ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL);\r
 \r
-  //\r
-  // Check the command line\r
-  //\r
-  Status = ProcessCommandLine();\r
+    //\r
+    // Now initialize the shell library (it requires Shell Parameters protocol)\r
+    //\r
+    Status = ShellInitialize();\r
+    ASSERT_EFI_ERROR(Status);\r
 \r
-  //\r
-  // If shell support level is >= 1 create the mappings and paths\r
-  //\r
-  if (PcdGet8(PcdShellSupportLevel) >= 1) {\r
-    Status = ShellCommandCreateInitialMappingsAndPaths();\r
-  }\r
+    Status = CommandInit();\r
+    ASSERT_EFI_ERROR(Status);\r
 \r
-  //\r
-  // save the device path for the loaded image and the device path for the filepath (under loaded image)\r
-  // These are where to look for the startup.nsh file\r
-  //\r
-  Status = GetDevicePathsForImageAndFile(&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);\r
-  ASSERT_EFI_ERROR(Status);\r
+    //\r
+    // Check the command line\r
+    //\r
+    Status = ProcessCommandLine();\r
 \r
-  //\r
-  // Display the version\r
-  //\r
-  if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {\r
-    ShellPrintHiiEx (\r
-      0,\r
-      gST->ConOut->Mode->CursorRow,\r
-      NULL,\r
-      STRING_TOKEN (STR_VER_OUTPUT_MAIN),\r
-      ShellInfoObject.HiiHandle,\r
-      SupportLevel[PcdGet8(PcdShellSupportLevel)],\r
-      gEfiShellProtocol->MajorVersion,\r
-      gEfiShellProtocol->MinorVersion,\r
-      (gST->Hdr.Revision&0xffff0000)>>16,\r
-      (gST->Hdr.Revision&0x0000ffff),\r
-      gST->FirmwareVendor,\r
-      gST->FirmwareRevision\r
-     );\r
-  }\r
+    //\r
+    // If shell support level is >= 1 create the mappings and paths\r
+    //\r
+    if (PcdGet8(PcdShellSupportLevel) >= 1) {\r
+      Status = ShellCommandCreateInitialMappingsAndPaths();\r
+    }\r
 \r
-  //\r
-  // Display the mapping\r
-  //\r
-  if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {\r
-    Status = RunCommand(L"map");\r
+    //\r
+    // save the device path for the loaded image and the device path for the filepath (under loaded image)\r
+    // These are where to look for the startup.nsh file\r
+    //\r
+    Status = GetDevicePathsForImageAndFile(&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);\r
     ASSERT_EFI_ERROR(Status);\r
-  }\r
 \r
-  //\r
-  // init all the built in alias'\r
-  //\r
-  Status = SetBuiltInAlias();\r
-  ASSERT_EFI_ERROR(Status);\r
+    //\r
+    // Display the version\r
+    //\r
+    if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {\r
+      ShellPrintHiiEx (\r
+        0,\r
+        gST->ConOut->Mode->CursorRow,\r
+        NULL,\r
+        STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL),\r
+        ShellInfoObject.HiiHandle,\r
+        SupportLevel[PcdGet8(PcdShellSupportLevel)],\r
+        gEfiShellProtocol->MajorVersion,\r
+        gEfiShellProtocol->MinorVersion\r
+       );\r
+\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER),\r
+        ShellInfoObject.HiiHandle,\r
+        (CHAR16 *) PcdGetPtr (PcdShellSupplier)\r
+       );\r
+\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI),\r
+        ShellInfoObject.HiiHandle,\r
+        (gST->Hdr.Revision&0xffff0000)>>16,\r
+        (gST->Hdr.Revision&0x0000ffff),\r
+        gST->FirmwareVendor,\r
+        gST->FirmwareRevision\r
+       );\r
+    }\r
 \r
-  //\r
-  // Initialize environment variables\r
-  //\r
-  if (ShellCommandGetProfileList() != NULL) {\r
-    Status = InternalEfiShellSetEnv(L"profiles", ShellCommandGetProfileList(), TRUE);\r
+    //\r
+    // Display the mapping\r
+    //\r
+    if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {\r
+      Status = RunCommand(L"map");\r
+      ASSERT_EFI_ERROR(Status);\r
+    }\r
+\r
+    //\r
+    // init all the built in alias'\r
+    //\r
+    Status = SetBuiltInAlias();\r
     ASSERT_EFI_ERROR(Status);\r
-  }\r
 \r
-  Size        = 100;\r
-  TempString  = AllocateZeroPool(Size);\r
+    //\r
+    // Initialize environment variables\r
+    //\r
+    if (ShellCommandGetProfileList() != NULL) {\r
+      Status = InternalEfiShellSetEnv(L"profiles", ShellCommandGetProfileList(), TRUE);\r
+      ASSERT_EFI_ERROR(Status);\r
+    }\r
 \r
-  UnicodeSPrint(TempString, Size, L"%d", PcdGet8(PcdShellSupportLevel));\r
-  Status = InternalEfiShellSetEnv(L"uefishellsupport", TempString, TRUE);\r
-  ASSERT_EFI_ERROR(Status);\r
+    Size        = 100;\r
+    TempString  = AllocateZeroPool(Size);\r
 \r
-  UnicodeSPrint(TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);\r
-  Status = InternalEfiShellSetEnv(L"uefishellversion", TempString, TRUE);\r
-  ASSERT_EFI_ERROR(Status);\r
+    UnicodeSPrint(TempString, Size, L"%d", PcdGet8(PcdShellSupportLevel));\r
+    Status = InternalEfiShellSetEnv(L"uefishellsupport", TempString, TRUE);\r
+    ASSERT_EFI_ERROR(Status);\r
 \r
-  UnicodeSPrint(TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);\r
-  Status = InternalEfiShellSetEnv(L"uefiversion", TempString, TRUE);\r
-  ASSERT_EFI_ERROR(Status);\r
+    UnicodeSPrint(TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);\r
+    Status = InternalEfiShellSetEnv(L"uefishellversion", TempString, TRUE);\r
+    ASSERT_EFI_ERROR(Status);\r
 \r
-  FreePool(TempString);\r
+    UnicodeSPrint(TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);\r
+    Status = InternalEfiShellSetEnv(L"uefiversion", TempString, TRUE);\r
+    ASSERT_EFI_ERROR(Status);\r
 \r
-  if (!EFI_ERROR(Status)) {\r
-    if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {\r
-      //\r
-      // Set up the event for CTRL-C monitoring...\r
-      //\r
+    FreePool(TempString);\r
 \r
-      ///@todo add support for using SimpleInputEx here\r
-      //  if SimpleInputEx is not available display a warning.\r
-    }\r
+    if (!EFI_ERROR(Status)) {\r
+      if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {\r
+        //\r
+        // Set up the event for CTRL-C monitoring...\r
+        //\r
+        Status = InernalEfiShellStartMonitor();\r
+      }\r
 \r
-    if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) {\r
-      //\r
-      // process the startup script or launch the called app.\r
-      //\r
-      Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);\r
-    }\r
+      if (!EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
+        //\r
+        // Set up the event for CTRL-S monitoring...\r
+        //\r
+        Status = InternalEfiShellStartCtrlSMonitor();\r
+      }\r
 \r
-    if ((PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
-      //\r
-      // begin the UI waiting loop\r
-      //\r
-      do {\r
+      if (!EFI_ERROR(Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
         //\r
-        // clean out all the memory allocated for CONST <something> * return values\r
-        // between each shell prompt presentation\r
+        // close off the gST->ConIn\r
         //\r
-        if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){\r
-          FreeBufferList(&ShellInfoObject.BufferToFreeList);\r
-        }\r
+        OldConIn      = gST->ConIn;\r
+        ConInHandle   = gST->ConsoleInHandle;\r
+        gST->ConIn = CreateSimpleTextInOnFile((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle);\r
+      } else {\r
+        OldConIn      = NULL;\r
+        ConInHandle   = NULL;\r
+      }\r
 \r
+      if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) {\r
         //\r
-        // Reset page break back to default.\r
+        // process the startup script or launch the called app.\r
         //\r
-        ShellInfoObject.PageBreakEnabled        = PcdGetBool(PcdShellPageBreakDefault);\r
-        ShellInfoObject.ConsoleInfo->Enabled    = TRUE;\r
-        ShellInfoObject.ConsoleInfo->RowCounter = 0;\r
+        Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);\r
+      }\r
 \r
+      if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
         //\r
-        // Display Prompt\r
+        // begin the UI waiting loop\r
         //\r
-        Status = DoShellPrompt();\r
-      } while (!ShellCommandGetExit());\r
+        do {\r
+          //\r
+          // clean out all the memory allocated for CONST <something> * return values\r
+          // between each shell prompt presentation\r
+          //\r
+          if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){\r
+            FreeBufferList(&ShellInfoObject.BufferToFreeList);\r
+          }\r
+\r
+          //\r
+          // Reset page break back to default.\r
+          //\r
+          ShellInfoObject.PageBreakEnabled        = PcdGetBool(PcdShellPageBreakDefault);\r
+          ShellInfoObject.ConsoleInfo->Enabled    = TRUE;\r
+          ShellInfoObject.ConsoleInfo->RowCounter = 0;\r
+\r
+          //\r
+          // Reset the CTRL-C event (yes we ignore the return values)\r
+          //\r
+          Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);\r
+\r
+          //\r
+          // Display Prompt\r
+          //\r
+          Status = DoShellPrompt();\r
+        } while (!ShellCommandGetExit());\r
+      }\r
+      if (OldConIn != NULL && ConInHandle != NULL) {\r
+        CloseSimpleTextInOnFile (gST->ConIn);\r
+        gST->ConIn            = OldConIn;\r
+        gST->ConsoleInHandle  = ConInHandle;\r
+      }\r
     }\r
   }\r
+\r
   //\r
   // uninstall protocols / free memory / etc...\r
   //\r
@@ -315,11 +522,6 @@ UefiMain (
     gBS->CloseEvent(ShellInfoObject.UserBreakTimer);\r
     DEBUG_CODE(ShellInfoObject.UserBreakTimer = NULL;);\r
   }\r
-\r
-  if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){\r
-    ShellInfoObject.NewEfiShellProtocol->SetEnv(L"cwd", L"", TRUE);\r
-  }\r
-\r
   if (ShellInfoObject.ImageDevPath != NULL) {\r
     FreePool(ShellInfoObject.ImageDevPath);\r
     DEBUG_CODE(ShellInfoObject.ImageDevPath = NULL;);\r
@@ -333,6 +535,9 @@ UefiMain (
     DEBUG_CODE(ShellInfoObject.NewShellParametersProtocol = NULL;);\r
   }\r
   if (ShellInfoObject.NewEfiShellProtocol != NULL){\r
+    if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){\r
+      InternalEfiShellSetEnv(L"cwd", NULL, TRUE);\r
+    }\r
     CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol);\r
     DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);\r
   }\r
@@ -342,7 +547,7 @@ UefiMain (
   }\r
 \r
   if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){\r
-    ASSERT(FALSE);\r
+    ASSERT(FALSE); ///@todo finish this de-allocation.\r
   }\r
 \r
   if (ShellInfoObject.ShellInitSettings.FileName != NULL) {\r
@@ -371,6 +576,9 @@ UefiMain (
     DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);\r
   }\r
 \r
+  if (ShellCommandGetExit()) {\r
+    return ((EFI_STATUS)ShellCommandGetExitCode());\r
+  }\r
   return (Status);\r
 }\r
 \r
@@ -456,16 +664,14 @@ IsScriptOnlyCommand(
   return (FALSE);\r
 }\r
 \r
-\r
-\r
 /**\r
   This function will populate the 2 device path protocol parameters based on the\r
   global gImageHandle.  The DevPath will point to the device path for the handle that has\r
   loaded image protocol installed on it.  The FilePath will point to the device path\r
   for the file that was loaded.\r
 \r
-  @param[in,out] DevPath       On a sucessful return the device path to the loaded image.\r
-  @param[in,out] FilePath      On a sucessful return the device path to the file.\r
+  @param[in, out] DevPath       On a sucessful return the device path to the loaded image.\r
+  @param[in, out] FilePath      On a sucessful return the device path to the file.\r
 \r
   @retval EFI_SUCCESS           The 2 device paths were sucessfully returned.\r
   @retval other                 A error from gBS->HandleProtocol.\r
@@ -506,6 +712,11 @@ GetDevicePathsForImageAndFile (
     if (!EFI_ERROR (Status)) {\r
       *DevPath  = DuplicateDevicePath (ImageDevicePath);\r
       *FilePath = DuplicateDevicePath (LoadedImage->FilePath);\r
+      gBS->CloseProtocol(\r
+                  LoadedImage->DeviceHandle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  gImageHandle,\r
+                  NULL);\r
     }\r
     gBS->CloseProtocol(\r
                 gImageHandle,\r
@@ -526,8 +737,10 @@ STATIC CONST SHELL_PARAM_ITEM mShellParamList[] = {
   {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
@@ -568,6 +781,7 @@ ProcessCommandLine(
   UINTN         Count;\r
   UINTN         LoopVar;\r
   CHAR16        *ProblemParam;\r
+  UINT64        Intermediate;\r
 \r
   Package       = NULL;\r
   ProblemParam  = NULL;\r
@@ -578,7 +792,10 @@ ProcessCommandLine(
   Size = 0;\r
   TempConst = ShellCommandLineGetRawValue(Package, Count++);\r
   if (TempConst != NULL && StrLen(TempConst)) {\r
-    ShellInfoObject.ShellInitSettings.FileName = AllocatePool(StrSize(TempConst));\r
+    ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(TempConst));\r
+    if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
+      return (EFI_OUT_OF_RESOURCES);\r
+    }\r
     StrCpy(ShellInfoObject.ShellInitSettings.FileName, TempConst);\r
     ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;\r
     for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
@@ -593,10 +810,18 @@ ProcessCommandLine(
                       &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
     }\r
@@ -620,18 +845,21 @@ ProcessCommandLine(
   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
-  if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay) {\r
+  ShellInfoObject.ShellInitSettings.Delay = 5;\r
+\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) {\r
-      ShellInfoObject.ShellInitSettings.Delay = StrDecimalToUintn (TempConst);\r
-    } else {\r
-      ShellInfoObject.ShellInitSettings.Delay = 5;\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
-  } else {\r
-    ShellInfoObject.ShellInitSettings.Delay = 5;\r
   }\r
-\r
   ShellCommandLineFreeVarList(Package);\r
 \r
   return (Status);\r
@@ -661,7 +889,9 @@ DoStartupScript(
   EFI_DEVICE_PATH_PROTOCOL      *NewPath;\r
   EFI_DEVICE_PATH_PROTOCOL      *NamePath;\r
   CHAR16                        *FileStringPath;\r
+  CHAR16                        *TempSpot;\r
   UINTN                         NewSize;\r
+  CONST CHAR16                  *MapName;\r
 \r
   Key.UnicodeChar = CHAR_NULL;\r
   Key.ScanCode    = 0;\r
@@ -676,9 +906,12 @@ DoStartupScript(
       NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16);\r
     }\r
     FileStringPath = AllocateZeroPool(NewSize);\r
+    if (FileStringPath == NULL) {\r
+      return (EFI_OUT_OF_RESOURCES);\r
+    }\r
     StrCpy(FileStringPath, ShellInfoObject.ShellInitSettings.FileName);\r
     if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
-      StrCat (FileStringPath, L" ");\r
+      StrCat(FileStringPath, L" ");\r
       StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);\r
     }\r
     Status = RunCommand(FileStringPath);\r
@@ -697,18 +930,22 @@ DoStartupScript(
     return (EFI_SUCCESS);\r
   }\r
 \r
+  gST->ConOut->EnableCursor(gST->ConOut, FALSE);\r
   //\r
   // print out our warning and see if they press a key\r
   //\r
-  for ( Status = EFI_UNSUPPORTED, Delay = (ShellInfoObject.ShellInitSettings.Delay * 10)\r
-      ; Delay > 0 && EFI_ERROR(Status)\r
+  for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay\r
+      ; Delay != 0 && EFI_ERROR(Status)\r
       ; Delay--\r
      ){\r
-    ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay/10);\r
-    gBS->Stall (100000);\r
-    Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
+    ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay);\r
+    gBS->Stall (1000000);\r
+    if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
+      Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
+    }\r
   }\r
   ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);\r
+  gST->ConOut->EnableCursor(gST->ConOut, TRUE);\r
 \r
   //\r
   // ESC was pressed\r
@@ -717,37 +954,59 @@ DoStartupScript(
     return (EFI_SUCCESS);\r
   }\r
 \r
-  NamePath = FileDevicePath (NULL, L"startup.nsh");\r
   //\r
-  // Try the first location\r
+  // Try the first location (must be file system)\r
   //\r
-  NewPath = AppendDevicePathNode (ImagePath, NamePath);\r
-  Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
+  MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath(&ImagePath);\r
+  if (MapName != NULL) {\r
+    FileStringPath = NULL;\r
+    NewSize = 0;\r
+    FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0);\r
+    if (FileStringPath == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+    } else {\r
+      TempSpot = StrStr(FileStringPath, L";");\r
+      if (TempSpot != NULL) {\r
+        *TempSpot = CHAR_NULL;\r
+      }\r
+      FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);\r
+      PathRemoveLastItem(FileStringPath);\r
+      FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);\r
+      Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);\r
+      FreePool(FileStringPath);\r
+    }\r
+  }\r
   if (EFI_ERROR(Status)) {\r
+    NamePath = FileDevicePath (NULL, mStartupScript);\r
+    NewPath = AppendDevicePathNode (ImagePath, NamePath);\r
+    FreePool(NamePath);\r
+\r
     //\r
-    // Try the second location\r
+    // Try the location\r
     //\r
-    FreePool(NewPath);\r
-    NewPath = AppendDevicePathNode (FilePath , NamePath);\r
     Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
+    FreePool(NewPath);\r
   }\r
-\r
   //\r
   // If we got a file, run it\r
   //\r
-  if (!EFI_ERROR(Status)) {\r
-    Status = RunScriptFileHandle (FileHandle, L"startup.nsh");\r
+  if (!EFI_ERROR(Status) && FileHandle != NULL) {\r
+    Status = RunScriptFileHandle (FileHandle, mStartupScript);\r
     ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);\r
   } else {\r
-    //\r
-    // we return success since we dont need to have a startup script\r
-    //\r
-    Status = EFI_SUCCESS;\r
-    ASSERT(FileHandle == NULL);\r
+    FileStringPath = ShellFindFilePath(mStartupScript);\r
+    if (FileStringPath == NULL) {\r
+      //\r
+      // we return success since we dont need to have a startup script\r
+      //\r
+      Status = EFI_SUCCESS;\r
+      ASSERT(FileHandle == NULL);\r
+    } else {\r
+      Status = RunScriptFile(FileStringPath);\r
+      FreePool(FileStringPath);\r
+    }\r
   }\r
 \r
-  FreePool(NamePath);\r
-  FreePool(NewPath);\r
 \r
   return (Status);\r
 }\r
@@ -869,10 +1128,10 @@ AddLineToCommandHistory(
   Checks if a string is an alias for another command.  If yes, then it replaces the alias name\r
   with the correct command name.\r
 \r
-  @param[in,out] CommandString    Upon entry the potential alias.  Upon return the\r
-                                  command name if it was an alias.  If it was not\r
-                                  an alias it will be unchanged.  This function may\r
-                                  change the buffer to fit the command name.\r
+  @param[in, out] CommandString    Upon entry the potential alias.  Upon return the\r
+                                   command name if it was an alias.  If it was not\r
+                                   an alias it will be unchanged.  This function may\r
+                                   change the buffer to fit the command name.\r
 \r
   @retval EFI_SUCCESS             The name was changed.\r
   @retval EFI_SUCCESS             The name was not an alias.\r
@@ -891,7 +1150,7 @@ ShellConvertAlias(
     return (EFI_SUCCESS);\r
   }\r
   FreePool(*CommandString);\r
-  *CommandString = AllocatePool(StrSize(NewString));\r
+  *CommandString = AllocateZeroPool(StrSize(NewString));\r
   if (*CommandString == NULL) {\r
     return (EFI_OUT_OF_RESOURCES);\r
   }\r
@@ -921,6 +1180,7 @@ ShellConvertVariables (
   CHAR16              *NewCommandLine1;\r
   CHAR16              *NewCommandLine2;\r
   CHAR16              *Temp;\r
+  CHAR16              *Temp2;\r
   UINTN               ItemSize;\r
   CHAR16              *ItemTemp;\r
   SCRIPT_FILE         *CurrentScriptFile;\r
@@ -979,20 +1239,17 @@ ShellConvertVariables (
   }\r
 \r
   //\r
-  // Quick out if none were found...\r
-  //\r
-  if (NewSize == StrSize(OriginalCommandLine)) {\r
-    ASSERT(Temp == NULL);\r
-    Temp = StrnCatGrow(&Temp, NULL, OriginalCommandLine, 0);\r
-    return (Temp);\r
-  }\r
-\r
-  //\r
-  // now do the replacements...\r
+  // now do the replacements...\r
   //\r
   NewCommandLine1 = AllocateZeroPool(NewSize);\r
   NewCommandLine2 = AllocateZeroPool(NewSize);\r
   ItemTemp        = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));\r
+  if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {\r
+    SHELL_FREE_NON_NULL(NewCommandLine1);\r
+    SHELL_FREE_NON_NULL(NewCommandLine2);\r
+    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
@@ -1012,8 +1269,51 @@ ShellConvertVariables (
     ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);\r
     StrCpy(NewCommandLine1, NewCommandLine2);\r
     }\r
+\r
+    //\r
+    // Remove non-existant environment variables in scripts only\r
+    //\r
+    for (Temp = NewCommandLine1 ; Temp != NULL ; ) {\r
+      Temp = StrStr(Temp, L"%");\r
+      if (Temp == NULL) {\r
+        break;\r
+      }\r
+      while (*(Temp - 1) == L'^') {\r
+        Temp = StrStr(Temp + 1, L"%");\r
+        if (Temp == NULL) {\r
+          break;\r
+       }\r
+      }\r
+      if (Temp == NULL) {\r
+        break;\r
+      }\r
+      \r
+      Temp2 = StrStr(Temp + 1, L"%");\r
+      if (Temp2 == NULL) {\r
+        break;\r
+      }\r
+      while (*(Temp2 - 1) == L'^') {\r
+        Temp2 = StrStr(Temp2 + 1, L"%");\r
+        if (Temp2 == NULL) {\r
+          break;\r
+        }\r
+      }\r
+      if (Temp2 == NULL) {\r
+        break;\r
+      }\r
+      \r
+      Temp2++;\r
+      CopyMem(Temp, Temp2, StrSize(Temp2));\r
+    }\r
+\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
+  \r
   FreePool(NewCommandLine2);\r
   FreePool(ItemTemp);\r
 \r
@@ -1059,7 +1359,16 @@ RunSplitCommand(
 \r
   NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);\r
   OurCommandLine  = StrnCatGrow(&OurCommandLine , &Size2, CmdLine                , StrStr(CmdLine, L"|") - CmdLine);\r
-  if (NextCommandLine[0] != CHAR_NULL &&\r
+\r
+  if (NextCommandLine == NULL || OurCommandLine == NULL) {\r
+    SHELL_FREE_NON_NULL(OurCommandLine);\r
+    SHELL_FREE_NON_NULL(NextCommandLine);\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  } else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) {\r
+    SHELL_FREE_NON_NULL(OurCommandLine);\r
+    SHELL_FREE_NON_NULL(NextCommandLine);\r
+    return (EFI_INVALID_PARAMETER);\r
+  } else if (NextCommandLine[0] != CHAR_NULL &&\r
       NextCommandLine[0] == L'a' &&\r
       NextCommandLine[1] == L' '\r
      ){\r
@@ -1080,7 +1389,6 @@ RunSplitCommand(
   ASSERT(Split->SplitStdOut != NULL);\r
   InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);\r
 \r
-  ASSERT(StrStr(OurCommandLine, L"|") == NULL);\r
   Status = RunCommand(OurCommandLine);\r
 \r
   //\r
@@ -1123,296 +1431,815 @@ RunSplitCommand(
 }\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
+  Take the original command line, substitute any variables, free \r
+  the original string, return the modified copy\r
 \r
-  @param[in] CmdLine      The command line to parse.\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
 \r
-  @retval EFI_SUCCESS     The command was completed.\r
-  @retval EFI_ABORTED     The command's operation was aborted.\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
-RunCommand(\r
-  IN CONST CHAR16   *CmdLine\r
+ShellSubstituteVariables(\r
+  IN CHAR16 **CmdLine\r
   )\r
 {\r
-  EFI_STATUS                Status;\r
-  CHAR16                    *CommandName;\r
-  SHELL_STATUS              ShellStatus;\r
-  UINTN                     Argc;\r
-  CHAR16                    **Argv;\r
-  BOOLEAN                   LastError;\r
-  CHAR16                    LeString[11];\r
-  CHAR16                    *PostAliasCmdLine;\r
-  UINTN                     PostAliasSize;\r
-  CHAR16                    *PostVariableCmdLine;\r
-  CHAR16                    *CommandWithPath;\r
-  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
-  CHAR16                    *TempLocation3;\r
-  UINTN                     Count;\r
-  UINTN                     Count2;\r
-  CHAR16                    *CleanOriginal;\r
-  SPLIT_LIST                *Split;\r
-\r
-  ASSERT(CmdLine != NULL);\r
-  if (StrLen(CmdLine) == 0) {\r
-    return (EFI_SUCCESS);\r
+  CHAR16      *NewCmdLine;\r
+  NewCmdLine = ShellConvertVariables(*CmdLine);\r
+  SHELL_FREE_NON_NULL(*CmdLine);\r
+  if (NewCmdLine == NULL) {\r
+    return (EFI_OUT_OF_RESOURCES);\r
   }\r
+  *CmdLine = NewCmdLine;\r
+  return (EFI_SUCCESS);\r
+}\r
 \r
-  CommandName         = NULL;\r
-  PostVariableCmdLine = NULL;\r
-  PostAliasCmdLine    = NULL;\r
-  CommandWithPath     = NULL;\r
-  DevPath             = NULL;\r
-  Status              = EFI_SUCCESS;\r
-  CleanOriginal       = NULL;\r
-  Split               = NULL;\r
+/**\r
+  Take the original command line, substitute any alias in the first group of space delimited characters, free \r
+  the original string, return the modified copy\r
+\r
+  @param[in] CmdLine  pointer to the command line to update\r
+  @param[out]CmdName  upon successful return the name of the command to be run\r
+\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
+ShellSubstituteAliases(\r
+  IN CHAR16 **CmdLine\r
+  )\r
+{\r
+  CHAR16      *NewCmdLine;\r
+  CHAR16      *CommandName;\r
+  EFI_STATUS  Status;\r
+  UINTN       PostAliasSize;\r
+  ASSERT(CmdLine != NULL);\r
+  ASSERT(*CmdLine!= NULL);\r
 \r
-  CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);\r
-  while (CleanOriginal[StrLen(CleanOriginal)-1] == L' ') {\r
-    CleanOriginal[StrLen(CleanOriginal)-1] = CHAR_NULL;\r
-  }\r
-  while (CleanOriginal[0] == L' ') {\r
-    CopyMem(CleanOriginal, CleanOriginal+1, StrSize(CleanOriginal) - sizeof(CleanOriginal[0]));\r
-  }\r
 \r
   CommandName = NULL;\r
-  if (StrStr(CleanOriginal, L" ") == NULL){\r
-    StrnCatGrow(&CommandName, NULL, CleanOriginal, 0);\r
+  if (StrStr((*CmdLine), L" ") == NULL){\r
+    StrnCatGrow(&CommandName, NULL, (*CmdLine), 0);\r
   } else {\r
-    StrnCatGrow(&CommandName, NULL, CleanOriginal, StrStr(CleanOriginal, L" ") - CleanOriginal);\r
+    StrnCatGrow(&CommandName, NULL, (*CmdLine), StrStr((*CmdLine), L" ") - (*CmdLine));\r
   }\r
 \r
-  ASSERT(PostAliasCmdLine == NULL);\r
+  //\r
+  // This cannot happen 'inline' since the CmdLine can need extra space.\r
+  //\r
+  NewCmdLine = NULL;\r
   if (!ShellCommandIsCommandOnList(CommandName)) {\r
     //\r
     // Convert via alias\r
     //\r
     Status = ShellConvertAlias(&CommandName);\r
+    if (EFI_ERROR(Status)){\r
+      return (Status);\r
+    }\r
     PostAliasSize = 0;\r
-    PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, CommandName, 0);\r
-    PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, StrStr(CleanOriginal, L" "), 0);\r
-    ASSERT_EFI_ERROR(Status);\r
+    NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, CommandName, 0);\r
+    if (NewCmdLine == NULL) {\r
+      SHELL_FREE_NON_NULL(CommandName);\r
+      SHELL_FREE_NON_NULL(*CmdLine);\r
+      return (EFI_OUT_OF_RESOURCES);\r
+    }\r
+    NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, StrStr((*CmdLine), L" "), 0);\r
+    if (NewCmdLine == NULL) {\r
+      SHELL_FREE_NON_NULL(CommandName);\r
+      SHELL_FREE_NON_NULL(*CmdLine);\r
+      return (EFI_OUT_OF_RESOURCES);\r
+    }\r
   } else {\r
-    PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, NULL, CleanOriginal, 0);\r
+    NewCmdLine = StrnCatGrow(&NewCmdLine, NULL, (*CmdLine), 0);\r
   }\r
 \r
-  if (CleanOriginal != NULL) {\r
-    FreePool(CleanOriginal);\r
-    CleanOriginal = NULL;\r
+  SHELL_FREE_NON_NULL(*CmdLine);\r
+  SHELL_FREE_NON_NULL(CommandName);\r
\r
+  //\r
+  // re-assign the passed in double pointer to point to our newly allocated buffer\r
+  //\r
+  *CmdLine = NewCmdLine;\r
+\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+  Takes the Argv[0] part of the command line and determine the meaning of it.\r
+\r
+  @param[in] CmdLine  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
+GetOperationType(\r
+  IN CONST CHAR16 *CmdName\r
+  )\r
+{\r
+        CHAR16* FileWithPath;\r
+  CONST CHAR16* TempLocation;\r
+  CONST CHAR16* TempLocation2;\r
+\r
+  FileWithPath = NULL;\r
+  //\r
+  // test for an internal command.\r
+  //\r
+  if (ShellCommandIsCommandOnList(CmdName)) {\r
+    return (INTERNAL_COMMAND);\r
   }\r
 \r
-  if (CommandName != NULL) {\r
-    FreePool(CommandName);\r
-    CommandName = NULL;\r
+  //\r
+  // Test for file system change request.  anything ending with : and cant have spaces.\r
+  //\r
+  if (CmdName[(StrLen(CmdName)-1)] == L':') {\r
+    if (StrStr(CmdName, L" ") != NULL) {\r
+      return (UNKNOWN_INVALID);\r
+    }\r
+    return (FILE_SYS_CHANGE);\r
   }\r
 \r
-  PostVariableCmdLine = ShellConvertVariables(PostAliasCmdLine);\r
+  //\r
+  // Test for a file\r
+  //\r
+  if ((FileWithPath = ShellFindFilePathEx(CmdName, mExecutableExtensions)) != NULL) {\r
+    //\r
+    // See if that file has a script file extension\r
+    //\r
+    if (StrLen(FileWithPath) > 4) {\r
+      TempLocation = FileWithPath+StrLen(FileWithPath)-4;\r
+      TempLocation2 = mScriptExtension;\r
+      if (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0) {\r
+        SHELL_FREE_NON_NULL(FileWithPath);\r
+        return (SCRIPT_FILE_NAME);\r
+      }\r
+    }\r
 \r
+    //\r
+    // Was a file, but not a script.  we treat this as an application.\r
+    //\r
+    SHELL_FREE_NON_NULL(FileWithPath);\r
+    return (EFI_APPLICATION);\r
+  }\r
+  \r
+  SHELL_FREE_NON_NULL(FileWithPath);\r
   //\r
-  // we can now free the modified by alias command line\r
+  // No clue what this is... return invalid flag...\r
   //\r
-  if (PostAliasCmdLine != NULL) {\r
-    FreePool(PostAliasCmdLine);\r
-    PostAliasCmdLine = NULL;\r
+  return (UNKNOWN_INVALID);\r
+}\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
-  while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {\r
-    PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;\r
+  FirstParameter = StrStr(Temp, L"|");\r
+  if (FirstParameter != NULL) {\r
+    *FirstParameter = CHAR_NULL;\r
   }\r
-  while (PostVariableCmdLine[0] == L' ') {\r
-    CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));\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
-  // We dont do normal processing with a split command line (output from one command input to another)\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
-  TempLocation3 = NULL;\r
-  if (StrStr(PostVariableCmdLine, L"|") != NULL) {\r
-    for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) {\r
-      if (*TempLocation3 == L'^' && *(TempLocation3+1) == L'|') {\r
-        TempLocation3++;\r
-      } else if (*TempLocation3 == L'|') {\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
+\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
+  )\r
+{\r
+  SPLIT_LIST                *Split;\r
+  EFI_STATUS                Status;\r
+\r
+  Status = VerifySplit(CmdLine);\r
+  if (EFI_ERROR(Status)) {\r
+    return (Status);\r
+  }\r
+\r
+  Split = NULL;\r
+\r
+  //\r
+  // are we in an existing split???\r
+  //\r
+  if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {\r
+    Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);\r
+  }\r
+\r
+  if (Split == NULL) {\r
+    Status = RunSplitCommand(CmdLine, NULL, NULL);\r
+  } else {\r
+    Status = RunSplitCommand(CmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
+  }\r
+  if (EFI_ERROR(Status)) {\r
+    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);\r
+  }\r
+  return (Status);\r
+}\r
+\r
+/**\r
+  Handle a request to change the current file system\r
+\r
+  @param[in] CmdLine  The passed in command line\r
+\r
+  @retval EFI_SUCCESS The operation was successful\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ChangeMappedDrive(\r
+  IN CONST CHAR16 *CmdLine\r
+  )\r
+{\r
+  EFI_STATUS Status;\r
+  Status = EFI_SUCCESS;\r
+\r
+  //\r
+  // make sure we are the right operation\r
+  //\r
+  ASSERT(CmdLine[(StrLen(CmdLine)-1)] == L':' && StrStr(CmdLine, L" ") == NULL);\r
+  \r
+  //\r
+  // Call the protocol API to do the work\r
+  //\r
+  Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, CmdLine);\r
+\r
+  //\r
+  // Report any errors\r
+  //\r
+  if (EFI_ERROR(Status)) {\r
+    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine);\r
+  }\r
+\r
+  return (Status);\r
+}\r
+\r
+/**\r
+  Reprocess the command line to direct all -? to the help command.\r
+\r
+  if found, will add "help" as argv[0], and move the rest later.\r
+\r
+  @param[in,out] CmdLine        pointer to the command line to update\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DoHelpUpdate(\r
+  IN OUT CHAR16 **CmdLine\r
+  )\r
+{\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
-  if (TempLocation3 != NULL && *TempLocation3 != CHAR_NULL) {\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
+  // update for help parsing\r
+  //\r
+  if (StrStr(*CmdLine, L"?") != NULL) {\r
     //\r
-    // are we in an existing split???\r
+    // This may do nothing if the ? does not indicate help.\r
+    // Save all the details for in the API below.\r
     //\r
-    if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {\r
-      Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);\r
-    }\r
+    Status = DoHelpUpdate(CmdLine);\r
+  }\r
 \r
-    if (Split == NULL) {\r
-      Status = RunSplitCommand(PostVariableCmdLine, NULL, NULL);\r
-    } else {\r
-      Status = RunSplitCommand(PostVariableCmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
-    }\r
-  } else {\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
+  @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
+)\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
-    // If this is a mapped drive change handle that...\r
+    // Run the internal command.\r
     //\r
-    if (PostVariableCmdLine[(StrLen(PostVariableCmdLine)-1)] == L':' && StrStr(PostVariableCmdLine, L" ") == NULL) {\r
-      Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, PostVariableCmdLine);\r
-      if (EFI_ERROR(Status)) {\r
-        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, PostVariableCmdLine);\r
+    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
+\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
-      FreePool(PostVariableCmdLine);\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
+  // 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
-    Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr);\r
-    if (EFI_ERROR(Status)) {\r
-      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);\r
-    } else {\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
+  @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
+)\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_STATUS                StatusCode;\r
+  CHAR16                    *CommandWithPath;\r
+  EFI_DEVICE_PATH_PROTOCOL  *DevPath;\r
+\r
+  Status            = EFI_SUCCESS;\r
+  CommandWithPath   = NULL;\r
+  DevPath           = NULL;\r
+\r
+  switch (Type) {\r
+    case   INTERNAL_COMMAND:\r
+      Status = RunInternalCommand(CmdLine, FirstParameter, ParamProtocol);\r
+      break;\r
+    case   SCRIPT_FILE_NAME:\r
+    case   EFI_APPLICATION:\r
       //\r
-      // remove the < and/or > from the command line now\r
+      // Process a fully qualified path\r
       //\r
-      for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) {\r
-        if (*TempLocation3 == L'^') {\r
-          if (*(TempLocation3+1) == L'<' || *(TempLocation3+1) == L'>') {\r
-            CopyMem(TempLocation3, TempLocation3+1, StrSize(TempLocation3) - sizeof(TempLocation3[0]));\r
-          }\r
-        } else if (*TempLocation3 == L'>') {\r
-          *TempLocation3 = CHAR_NULL;\r
-        } else if ((*TempLocation3 == L'1' || *TempLocation3 == L'2')&&(*(TempLocation3+1) == L'>')) {\r
-          *TempLocation3 = CHAR_NULL;\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
-      while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {\r
-        PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;\r
-      }\r
-      while (PostVariableCmdLine[0] == L' ') {\r
-        CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));\r
+      //\r
+      // 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
-      // get the argc and argv updated for internal commands\r
+      // This should be impossible now.\r
       //\r
-      Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &Argv, &Argc);\r
-      ASSERT_EFI_ERROR(Status);\r
-\r
-      for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) {\r
-        if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[Count], L"-?") == ShellInfoObject.NewShellParametersProtocol->Argv[Count]\r
-        ||  (ShellInfoObject.NewShellParametersProtocol->Argv[0][0] == L'?' && ShellInfoObject.NewShellParametersProtocol->Argv[0][1] == CHAR_NULL)\r
-          ) {\r
-          //\r
-          // We need to redo the arguments since a parameter was -?\r
-          // move them all down 1 to the end, then up one then replace the first with help\r
-          //\r
-          FreePool(ShellInfoObject.NewShellParametersProtocol->Argv[Count]);\r
-          ShellInfoObject.NewShellParametersProtocol->Argv[Count] = NULL;\r
-          for (Count2 = Count ; (Count2 + 1) < ShellInfoObject.NewShellParametersProtocol->Argc ; Count2++) {\r
-            ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2+1];\r
-          }\r
-          ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = NULL;\r
-          for (Count2 = ShellInfoObject.NewShellParametersProtocol->Argc -1 ; Count2 > 0 ; Count2--) {\r
-            ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2-1];\r
-          }\r
-          ShellInfoObject.NewShellParametersProtocol->Argv[0] = NULL;\r
-          ShellInfoObject.NewShellParametersProtocol->Argv[0] = StrnCatGrow(&ShellInfoObject.NewShellParametersProtocol->Argv[0], NULL, L"help", 0);\r
-          break;\r
-        }\r
-      }\r
+      ASSERT(CommandWithPath != NULL);\r
 \r
       //\r
-      // command or file?\r
+      // Make sure that path is not just a directory (or not found)\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
-          UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%08x", ShellStatus);\r
-          DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););\r
-          if (LastError) {\r
-            InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);\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 (CommandWithPath);\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
-        } else {\r
+\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
-          } else {\r
-            DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
-            ASSERT(DevPath != NULL);\r
-            Status = InternalShellExecuteDevicePath(\r
-              &gImageHandle,\r
-              DevPath,\r
-              PostVariableCmdLine,\r
-              NULL,\r
-              NULL\r
-             );\r
-          }\r
-        }\r
-      }\r
-      CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
+          Status = InternalShellExecuteDevicePath(\r
+            &gImageHandle,\r
+            DevPath,\r
+            CmdLine,\r
+            NULL,\r
+            &StatusCode\r
+           );\r
 \r
-      RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);\r
+          SHELL_FREE_NON_NULL(DevPath);\r
 \r
-      RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr);\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
+          // Update last error status.\r
+          //\r
+          SetLastError(StatusCode);\r
+          break;\r
+        default:\r
+          //\r
+          // Do nothing.\r
+          //\r
+          break;\r
       }\r
-    }\r
+      break;\r
+    default:\r
+      //\r
+      // Do nothing.\r
+      //\r
+      break;\r
   }\r
 \r
-  SHELL_FREE_NON_NULL(CommandName);\r
   SHELL_FREE_NON_NULL(CommandWithPath);\r
-  SHELL_FREE_NON_NULL(PostVariableCmdLine);\r
-  SHELL_FREE_NON_NULL(DevPath);\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
+  @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
+)\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(Type, CmdLine, FirstParameter, ParamProtocol);\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
+\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
+  )\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
+  // 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);\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);\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(Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol);\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
@@ -1473,21 +2300,29 @@ RunScriptFileHandle (
   SCRIPT_COMMAND_LIST *LastCommand;\r
   BOOLEAN             Ascii;\r
   BOOLEAN             PreScriptEchoState;\r
+  BOOLEAN             PreCommandEchoState;\r
   CONST CHAR16        *CurDir;\r
   UINTN               LineCount;\r
+  CHAR16              LeString[50];\r
 \r
   ASSERT(!ShellCommandGetScriptExit());\r
 \r
   PreScriptEchoState = ShellCommandGetEchoState();\r
 \r
   NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));\r
-  ASSERT(NewScriptFile != NULL);\r
+  if (NewScriptFile == NULL) {\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  }\r
 \r
   //\r
   // Set up the name\r
   //\r
   ASSERT(NewScriptFile->ScriptName == NULL);\r
   NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);\r
+  if (NewScriptFile->ScriptName == NULL) {\r
+    DeleteScriptFileStruct(NewScriptFile);\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  }\r
 \r
   //\r
   // Save the parameters (used to replace %0 to %9 later on)\r
@@ -1495,10 +2330,17 @@ RunScriptFileHandle (
   NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;\r
   if (NewScriptFile->Argc != 0) {\r
     NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));\r
-    ASSERT(NewScriptFile->Argv != NULL);\r
+    if (NewScriptFile->Argv == NULL) {\r
+      DeleteScriptFileStruct(NewScriptFile);\r
+      return (EFI_OUT_OF_RESOURCES);\r
+    }\r
     for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {\r
       ASSERT(NewScriptFile->Argv[LoopVar] == NULL);\r
       NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);\r
+      if (NewScriptFile->Argv[LoopVar] == NULL) {\r
+        DeleteScriptFileStruct(NewScriptFile);\r
+        return (EFI_OUT_OF_RESOURCES);\r
+      }\r
     }\r
   } else {\r
     NewScriptFile->Argv = NULL;\r
@@ -1518,7 +2360,10 @@ RunScriptFileHandle (
       continue;\r
     }\r
     NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));\r
-    ASSERT(NewScriptFile->CurrentCommand != NULL);\r
+    if (NewScriptFile->CurrentCommand == NULL) {\r
+      DeleteScriptFileStruct(NewScriptFile);\r
+      return (EFI_OUT_OF_RESOURCES);\r
+    }\r
 \r
     NewScriptFile->CurrentCommand->Cl   = CommandLine;\r
     NewScriptFile->CurrentCommand->Data = NULL;\r
@@ -1535,13 +2380,23 @@ RunScriptFileHandle (
   //\r
   // Now enumerate through the commands and run each one.\r
   //\r
-  CommandLine = AllocatePool(PcdGet16(PcdShellPrintBufferSize));\r
-  CommandLine2 = AllocatePool(PcdGet16(PcdShellPrintBufferSize));\r
+  CommandLine = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));\r
+  if (CommandLine == NULL) {\r
+    DeleteScriptFileStruct(NewScriptFile);\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  }\r
+  CommandLine2 = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));\r
+  if (CommandLine2 == NULL) {\r
+    FreePool(CommandLine);\r
+    DeleteScriptFileStruct(NewScriptFile);\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  }\r
 \r
   for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)\r
       ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)\r
       ; // conditional increment in the body of the loop\r
   ){\r
+    ASSERT(CommandLine2 != NULL);\r
     StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl);\r
 \r
     //\r
@@ -1619,29 +2474,60 @@ RunScriptFileHandle (
 \r
       for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);\r
 \r
-      if (CommandLine3[0] == L':' ) {\r
+      if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {\r
         //\r
         // This line is a goto target / label\r
         //\r
       } else {\r
         if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {\r
-          if (ShellCommandGetEchoState()) {\r
-            CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
-            if (CurDir != NULL && StrLen(CurDir) > 1) {\r
-              ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
-            } else {\r
-              ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
+          if (CommandLine3[0] == L'@') {\r
+            //\r
+            // We need to save the current echo state\r
+            // and disable echo for just this command.\r
+            //\r
+            PreCommandEchoState = ShellCommandGetEchoState();\r
+            ShellCommandSetEchoState(FALSE);\r
+            Status = RunCommand(CommandLine3+1);\r
+\r
+            //\r
+            // If command was "@echo -off" or "@echo -on" then don't restore echo state\r
+            //\r
+            if (StrCmp (L"@echo -off", CommandLine3) != 0 &&\r
+                StrCmp (L"@echo -on", CommandLine3) != 0) {\r
+              //\r
+              // Now restore the pre-'@' echo state.\r
+              //\r
+              ShellCommandSetEchoState(PreCommandEchoState);\r
             }\r
-            ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);\r
+          } else {\r
+            if (ShellCommandGetEchoState()) {\r
+              CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
+              if (CurDir != NULL && StrLen(CurDir) > 1) {\r
+                ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
+              } else {\r
+                ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
+              }\r
+              ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);\r
+            }\r
+            Status = RunCommand(CommandLine3);\r
           }\r
-          Status = RunCommand(CommandLine3);\r
         }\r
 \r
         if (ShellCommandGetScriptExit()) {\r
-          ShellCommandRegisterExit(FALSE);\r
+          //\r
+          // ShellCommandGetExitCode() always returns a UINT64\r
+          //\r
+          UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellCommandGetExitCode());\r
+          DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
+          InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
+\r
+          ShellCommandRegisterExit(FALSE, 0);\r
           Status = EFI_SUCCESS;\r
           break;\r
         }\r
+        if (ShellGetExecutionBreakFlag()) {\r
+          break;\r
+        }\r
         if (EFI_ERROR(Status)) {\r
           break;\r
         }\r
@@ -1666,11 +2552,17 @@ RunScriptFileHandle (
     }\r
   }\r
 \r
-  ShellCommandSetEchoState(PreScriptEchoState);\r
 \r
   FreePool(CommandLine);\r
   FreePool(CommandLine2);\r
   ShellCommandSetNewScript (NULL);\r
+\r
+  //\r
+  // Only if this was the last script reset the state.\r
+  //\r
+  if (ShellCommandGetCurrentScriptFile()==NULL) {\r
+    ShellCommandSetEchoState(PreScriptEchoState);\r
+  }\r
   return (EFI_SUCCESS);\r
 }\r
 \r