]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ShellPkg/Application/Shell/Shell.c
ShellPkg: add API for determining operation type
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
index a56967189bba7ca7e4b7c09e55ac983a18c0a26f..ef757dec0fa724f87b490cd45fde513fed3a16d1 100644 (file)
@@ -1,7 +1,8 @@
 /** @file\r
   This is THE shell (application)\r
 \r
-  Copyright (c) 2009 - 2011, 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,18 +53,100 @@ 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
+  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 at the end of the (*String).\r
+  //\r
+  while ((*String)[StrLen((*String))-1] == L' ') {\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 (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
@@ -123,7 +208,7 @@ InternalEfiShellStartCtrlSMonitor(
       SimpleEx,\r
       &KeyData,\r
       NotificationFunction,\r
-      &ShellInfoObject.CtrlSNotifyHandle2);\r
+      &ShellInfoObject.CtrlSNotifyHandle3);\r
   }  \r
   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
   if (!EFI_ERROR(Status)) {\r
@@ -131,7 +216,7 @@ InternalEfiShellStartCtrlSMonitor(
       SimpleEx,\r
       &KeyData,\r
       NotificationFunction,\r
-      &ShellInfoObject.CtrlSNotifyHandle2);\r
+      &ShellInfoObject.CtrlSNotifyHandle4);\r
   }\r
   return (Status);\r
 }\r
@@ -155,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
@@ -295,11 +374,28 @@ UefiMain (
         0,\r
         gST->ConOut->Mode->CursorRow,\r
         NULL,\r
-        STRING_TOKEN (STR_VER_OUTPUT_MAIN),\r
+        STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL),\r
         ShellInfoObject.HiiHandle,\r
         SupportLevel[PcdGet8(PcdShellSupportLevel)],\r
         gEfiShellProtocol->MajorVersion,\r
-        gEfiShellProtocol->MinorVersion,\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
@@ -361,6 +457,18 @@ UefiMain (
         Status = InternalEfiShellStartCtrlSMonitor();\r
       }\r
 \r
+      if (!EFI_ERROR(Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
+        //\r
+        // close off the gST->ConIn\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
         // process the startup script or launch the called app.\r
@@ -368,7 +476,7 @@ UefiMain (
         Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);\r
       }\r
 \r
-      if (!ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
+      if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
         //\r
         // begin the UI waiting loop\r
         //\r
@@ -399,6 +507,11 @@ UefiMain (
           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
@@ -423,7 +536,7 @@ UefiMain (
   }\r
   if (ShellInfoObject.NewEfiShellProtocol != NULL){\r
     if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){\r
-      ShellInfoObject.NewEfiShellProtocol->SetEnv(L"cwd", L"", TRUE);\r
+      InternalEfiShellSetEnv(L"cwd", NULL, TRUE);\r
     }\r
     CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol);\r
     DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);\r
@@ -463,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
@@ -548,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
@@ -598,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
@@ -618,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
@@ -724,6 +845,7 @@ 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
   ShellInfoObject.ShellInitSettings.Delay = 5;\r
 \r
@@ -808,20 +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
+  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
+    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
@@ -838,15 +962,19 @@ DoStartupScript(
     FileStringPath = NULL;\r
     NewSize = 0;\r
     FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0);\r
-    TempSpot = StrStr(FileStringPath, L";");\r
-    if (TempSpot != NULL) {\r
-      *TempSpot = CHAR_NULL;\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
-    FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);\r
-    ChopLastSlash(FileStringPath);\r
-    FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);\r
-    Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);\r
-    FreePool(FileStringPath);\r
   }\r
   if (EFI_ERROR(Status)) {\r
     NamePath = FileDevicePath (NULL, mStartupScript);\r
@@ -1000,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
@@ -1052,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
@@ -1109,15 +1238,6 @@ ShellConvertVariables (
     }\r
   }\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
   //\r
@@ -1149,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
@@ -1196,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
@@ -1217,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
@@ -1259,6 +1430,164 @@ RunSplitCommand(
   return (Status);\r
 }\r
 \r
+/**\r
+  Take the original command line, substitute any variables, free \r
+  the original string, return the modified copy\r
+\r
+  @param[in] CmdLine  pointer to the command line to update\r
+  @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
+ShellSubstituteVariables(\r
+  IN CHAR16 **CmdLine\r
+  )\r
+{\r
+  CHAR16      *NewCmdLine;\r
+  NewCmdLine = ShellConvertVariables(*CmdLine);\r
+  SHELL_FREE_NON_NULL(*CmdLine);\r
+  if (NewCmdLine == NULL) {\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  }\r
+  *CmdLine = NewCmdLine;\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+  Take the original command line, substitute any alias in the first group of space delimited characters, free \r
+  the original string, return the modified copy\r
+\r
+  @param[in] CmdLine  pointer to the command line to update\r
+  @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
+\r
+  CommandName = NULL;\r
+  if (StrStr((*CmdLine), L" ") == NULL){\r
+    StrnCatGrow(&CommandName, NULL, (*CmdLine), 0);\r
+  } else {\r
+    StrnCatGrow(&CommandName, NULL, (*CmdLine), StrStr((*CmdLine), L" ") - (*CmdLine));\r
+  }\r
+\r
+  //\r
+  // This cannot happen 'inline' since the CmdLine can need extra space.\r
+  //\r
+  NewCmdLine = NULL;\r
+  if (!ShellCommandIsCommandOnList(CommandName)) {\r
+    //\r
+    // Convert via alias\r
+    //\r
+    Status = ShellConvertAlias(&CommandName);\r
+    if (EFI_ERROR(Status)){\r
+      return (Status);\r
+    }\r
+    PostAliasSize = 0;\r
+    NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, CommandName, 0);\r
+    if (NewCmdLine == NULL) {\r
+      SHELL_FREE_NON_NULL(CommandName);\r
+      SHELL_FREE_NON_NULL(*CmdLine);\r
+      return (EFI_OUT_OF_RESOURCES);\r
+    }\r
+    NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, StrStr((*CmdLine), L" "), 0);\r
+    if (NewCmdLine == NULL) {\r
+      SHELL_FREE_NON_NULL(CommandName);\r
+      SHELL_FREE_NON_NULL(*CmdLine);\r
+      return (EFI_OUT_OF_RESOURCES);\r
+    }\r
+  } else {\r
+    NewCmdLine = StrnCatGrow(&NewCmdLine, NULL, (*CmdLine), 0);\r
+  }\r
+\r
+  SHELL_FREE_NON_NULL(*CmdLine);\r
+  SHELL_FREE_NON_NULL(CommandName);\r
\r
+  //\r
+  // re-assign the passed in double pointer to point to our newly allocated buffer\r
+  //\r
+  *CmdLine = NewCmdLine;\r
+\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+  Takes the Argv[0] part of the command line and determine the meaning of it.\r
+**/\r
+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
+  //\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
+  //\r
+  // Test for a file\r
+  //\r
+  if ((FileWithPath = ShellFindFilePathEx(CmdName, mExecutableExtensions)) != NULL) {\r
+    //\r
+    // See if that file has a script file extension\r
+    //\r
+    if (StrLen(FileWithPath) > 4) {\r
+      TempLocation = FileWithPath+StrLen(FileWithPath)-4;\r
+      TempLocation2 = mScriptExtension;\r
+      if (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0) {\r
+        SHELL_FREE_NON_NULL(FileWithPath);\r
+        return (SCRIPT_FILE_NAME);\r
+      }\r
+    }\r
+\r
+    //\r
+    // Was a file, but not a script.  we treat this as an application.\r
+    //\r
+    SHELL_FREE_NON_NULL(FileWithPath);\r
+    return (EFI_APPLICATION);\r
+  }\r
+  \r
+  SHELL_FREE_NON_NULL(FileWithPath);\r
+  //\r
+  // No clue what this is... return invalid flag...\r
+  //\r
+  return (UNKNOWN_INVALID);\r
+}\r
+\r
 /**\r
   Function will process and run a command line.\r
 \r
@@ -1277,15 +1606,13 @@ RunCommand(
   )\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[11];\r
-  CHAR16                    *PostAliasCmdLine;\r
-  UINTN                     PostAliasSize;\r
-  CHAR16                    *PostVariableCmdLine;\r
+  CHAR16                    LeString[19];\r
   CHAR16                    *CommandWithPath;\r
   CONST EFI_DEVICE_PATH_PROTOCOL  *DevPath;\r
   CONST CHAR16              *TempLocation;\r
@@ -1294,7 +1621,6 @@ RunCommand(
   SHELL_FILE_HANDLE         OriginalStdOut;\r
   SHELL_FILE_HANDLE         OriginalStdErr;\r
   SYSTEM_TABLE_INFO         OriginalSystemTableInfo;\r
-  CHAR16                    *TempLocation3;\r
   UINTN                     Count;\r
   UINTN                     Count2;\r
   CHAR16                    *CleanOriginal;\r
@@ -1306,8 +1632,6 @@ RunCommand(
   }\r
 \r
   CommandName         = NULL;\r
-  PostVariableCmdLine = NULL;\r
-  PostAliasCmdLine    = NULL;\r
   CommandWithPath     = NULL;\r
   DevPath             = NULL;\r
   Status              = EFI_SUCCESS;\r
@@ -1315,79 +1639,39 @@ RunCommand(
   Split               = 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
-  } else {\r
-    StrnCatGrow(&CommandName, NULL, CleanOriginal, StrStr(CleanOriginal, L" ") - CleanOriginal);\r
-  }\r
-\r
-  ASSERT(PostAliasCmdLine == NULL);\r
-  if (!ShellCommandIsCommandOnList(CommandName)) {\r
-    //\r
-    // Convert via alias\r
-    //\r
-    Status = ShellConvertAlias(&CommandName);\r
-    PostAliasSize = 0;\r
-    PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, CommandName, 0);\r
-    PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, StrStr(CleanOriginal, L" "), 0);\r
-    ASSERT_EFI_ERROR(Status);\r
-  } else {\r
-    PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, NULL, CleanOriginal, 0);\r
-  }\r
-\r
-  if (CleanOriginal != NULL) {\r
-    FreePool(CleanOriginal);\r
-    CleanOriginal = NULL;\r
-  }\r
-\r
-  if (CommandName != NULL) {\r
-    FreePool(CommandName);\r
-    CommandName = NULL;\r
+  if (CleanOriginal == NULL) {\r
+    return (EFI_OUT_OF_RESOURCES);\r
   }\r
 \r
-  PostVariableCmdLine = ShellConvertVariables(PostAliasCmdLine);\r
+  TrimSpaces(&CleanOriginal);\r
 \r
   //\r
-  // we can now free the modified by alias command line\r
+  // Handle case that passed in command line is just 1 or more " " characters.\r
   //\r
-  if (PostAliasCmdLine != NULL) {\r
-    FreePool(PostAliasCmdLine);\r
-    PostAliasCmdLine = NULL;\r
+  if (StrLen (CleanOriginal) == 0) {\r
+    if (CleanOriginal != NULL) {\r
+      FreePool(CleanOriginal);\r
+      CleanOriginal = NULL;\r
+    }\r
+    return (EFI_SUCCESS);\r
   }\r
 \r
-  if (PostVariableCmdLine == NULL) {\r
-    return (EFI_OUT_OF_RESOURCES);\r
+  Status = ShellSubstituteAliases(&CleanOriginal);\r
+  if (EFI_ERROR(Status)) {\r
+    return (Status);\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
+  Status = ShellSubstituteVariables(&CleanOriginal);\r
+  if (EFI_ERROR(Status)) {\r
+    return (Status);\r
   }\r
 \r
+  TrimSpaces(&CleanOriginal);\r
+\r
   //\r
   // We dont do normal processing with a split command line (output from one command input to another)\r
   //\r
-  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
-        break;\r
-      }\r
-    }\r
-  }\r
-  if (TempLocation3 != NULL && *TempLocation3 != CHAR_NULL) {\r
+  if (ContainsSplit(CleanOriginal)) {\r
     //\r
     // are we in an existing split???\r
     //\r
@@ -1396,21 +1680,24 @@ RunCommand(
     }\r
 \r
     if (Split == NULL) {\r
-      Status = RunSplitCommand(PostVariableCmdLine, NULL, NULL);\r
+      Status = RunSplitCommand(CleanOriginal, NULL, NULL);\r
     } else {\r
-      Status = RunSplitCommand(PostVariableCmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
+      Status = RunSplitCommand(CleanOriginal, Split->SplitStdIn, Split->SplitStdOut);\r
+    }\r
+    if (EFI_ERROR(Status)) {\r
+      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CleanOriginal);\r
     }\r
   } else {\r
 \r
     //\r
     // If this is a mapped drive change handle that...\r
     //\r
-    if (PostVariableCmdLine[(StrLen(PostVariableCmdLine)-1)] == L':' && StrStr(PostVariableCmdLine, L" ") == NULL) {\r
-      Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, PostVariableCmdLine);\r
+    if (CleanOriginal[(StrLen(CleanOriginal)-1)] == L':' && StrStr(CleanOriginal, L" ") == NULL) {\r
+      Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, CleanOriginal);\r
       if (EFI_ERROR(Status)) {\r
-        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, PostVariableCmdLine);\r
+        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CleanOriginal);\r
       }\r
-      FreePool(PostVariableCmdLine);\r
+      FreePool(CleanOriginal);\r
       return (Status);\r
     }\r
 \r
@@ -1419,7 +1706,7 @@ RunCommand(
 \r
 \r
 \r
-    Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\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
@@ -1427,17 +1714,12 @@ RunCommand(
         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);\r
       }\r
     } else {\r
-      while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {\r
-        PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;\r
-      }\r
-      while (PostVariableCmdLine[0] == L' ') {\r
-        CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));\r
-      }\r
-\r
+      TrimSpaces(&CleanOriginal);\r
+    \r
       //\r
       // get the argc and argv updated for internal commands\r
       //\r
-      Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &Argv, &Argc);\r
+      Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, CleanOriginal, &Argv, &Argc);\r
       ASSERT_EFI_ERROR(Status);\r
 \r
       for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) {\r
@@ -1473,10 +1755,15 @@ RunCommand(
         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
+\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
+            InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
           }\r
           //\r
           // Pass thru the exitcode from the app.\r
@@ -1502,6 +1789,14 @@ RunCommand(
         }\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
@@ -1516,13 +1811,32 @@ RunCommand(
             Status = InternalShellExecuteDevicePath(\r
               &gImageHandle,\r
               DevPath,\r
-              PostVariableCmdLine,\r
+              CleanOriginal,\r
               NULL,\r
-              NULL\r
+              &StatusCode\r
              );\r
+\r
+            //\r
+            // Update last error status.\r
+            //\r
+            if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
+              UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", StatusCode);\r
+            } else {\r
+              UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", StatusCode);\r
+            }\r
+            DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
+            InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
           }\r
         }\r
       }\r
+\r
+      //\r
+      // Print some error info.\r
+      //\r
+      if (EFI_ERROR(Status)) {\r
+        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));\r
+      }\r
+\r
       CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
 \r
       RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);\r
@@ -1542,7 +1856,7 @@ RunCommand(
 \r
   SHELL_FREE_NON_NULL(CommandName);\r
   SHELL_FREE_NON_NULL(CommandWithPath);\r
-  SHELL_FREE_NON_NULL(PostVariableCmdLine);\r
+  SHELL_FREE_NON_NULL(CleanOriginal);\r
 \r
   return (Status);\r
 }\r
@@ -1603,8 +1917,10 @@ 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
@@ -1781,23 +2097,54 @@ RunScriptFileHandle (
         //\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
+          } 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
-            ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);\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