]> git.proxmox.com Git - mirror_edk2.git/commitdiff
ShellPkg: Refactor the RunCommand API
authorJaben Carsey <jaben.carsey@intel.com>
Thu, 19 Dec 2013 16:05:34 +0000 (16:05 +0000)
committerjcarsey <jcarsey@6f19259b-4bc3-4df7-8a09-765794883524>
Thu, 19 Dec 2013 16:05:34 +0000 (16:05 +0000)
This almost completely splits the RunCommand API into sub-routines.

 - the ProcessCommandLineToFinal API handles replacing the a found alias and any found environment variables.  This will redirect "-?" to "help", if necessary.  Upon return, the command line is complete and finalized.  It may still have redirection in it, and those will get chopped off later (but no further modifications occur).
 - the SetupAndRunCommandOrFile API handles updating and then later restoring StdIn, StdOut, and StdErr (and removing their information from the command line).  It will call into RunCommandOrFile.
 - the RunCommandOrFile API divides the logic to RunInternalCommand, RunScriptFile, or running an .EFI file directly.
 - the RunInternalCommand API handles updating and then restoring Argc and Argv.  It will run the internal command in between.
 - the SetLastError API handles updating of the environment variable "lasterror"
 - the DoHelpUpdateArgcArgv was changed to DoHelpUpdate and now works on the raw command line and not the argc/argv.  This allows the processing to be moved earlier.

Note this change has the following positive side effects (this eliminates unnecessary step):
 - Argc/Argv are only updated for internal commands (as they are library based)
 - no Argv/Argc/StdIn/StdOut/StdErr processing is done for file system changes.
 - The ProcessCommandLineToFinal API exists and it's critical to the ability to correctly pre-process split ("|") command lines ahead of time to verify their correctness.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Jaben Carsey <jaben.carsey@intel.com>
Reviewed-by: Erik Bjorge <erik.c.bjorge@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15007 6f19259b-4bc3-4df7-8a09-765794883524

ShellPkg/Application/Shell/Shell.c

index 53f1bc3059aed2aedf75d9885ec0217087b3baa8..fe722e955448bd0c60d6901da4bf3f9d350e85fc 100644 (file)
@@ -1674,50 +1674,353 @@ ChangeMappedDrive(
 \r
   if found, will add "help" as argv[0], and move the rest later.\r
 \r
-  @param[in,out] Argc  The pointer to argc to update \r
-  @param[in,out] Argv  The pointer to argv to update (this is a pointer to an array of string pointers)\r
+  @param[in,out] CmdLine        pointer to the command line to update\r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
-DoHelpUpdateArgcArgv(\r
-  IN OUT UINTN *Argc,\r
-  IN OUT CHAR16 ***Argv\r
+DoHelpUpdate(\r
+  IN OUT CHAR16 **CmdLine\r
   )\r
 {\r
-  UINTN Count;\r
-  UINTN Count2;\r
+  CHAR16 *CurrentParameter;\r
+  CHAR16 *Walker;\r
+  CHAR16 *LastWalker;\r
+  CHAR16 *NewCommandLine;\r
+  EFI_STATUS Status;\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  CurrentParameter = AllocateZeroPool(StrSize(*CmdLine));\r
+  if (CurrentParameter == NULL) {\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  }\r
+\r
+  Walker = *CmdLine;\r
+  while(Walker != NULL && *Walker != CHAR_NULL) {\r
+    LastWalker = Walker;\r
+    GetNextParameter(&Walker, &CurrentParameter);\r
+    if (StrStr(CurrentParameter, L"-?") == CurrentParameter) {\r
+      LastWalker[0] = L' ';\r
+      LastWalker[1] = L' ';\r
+      NewCommandLine = AllocateZeroPool(StrSize(L"help ") + StrSize(*CmdLine));\r
+      if (NewCommandLine == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        break;\r
+      }\r
+      StrCpy(NewCommandLine, L"help ");\r
+      StrCat(NewCommandLine, *CmdLine);\r
+      SHELL_FREE_NON_NULL(*CmdLine);\r
+      *CmdLine = NewCommandLine;\r
+      break;\r
+    }\r
+  }\r
+\r
+  SHELL_FREE_NON_NULL(CurrentParameter);\r
+\r
+  return (Status);\r
+}\r
+\r
+/**\r
+  Function to update the shell variable "lasterror"\r
+\r
+  @param[in] ErrorCode      the error code to put into lasterror\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SetLastError(\r
+  IN CONST UINT64   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
+ProcessCommandLineAliasVariable(\r
+  IN OUT CHAR16 **CmdLine\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  TrimSpaces(CmdLine);\r
+\r
+  Status = ShellSubstituteAliases(CmdLine);\r
+  if (EFI_ERROR(Status)) {\r
+    return (Status);\r
+  }\r
+\r
+  TrimSpaces(CmdLine);\r
+\r
+  Status = ShellSubstituteVariables(CmdLine);\r
+  if (EFI_ERROR(Status)) {\r
+    return (Status);\r
+  }\r
+\r
+  TrimSpaces(CmdLine);\r
+\r
   //\r
-  // Check each parameter\r
+  // update for help parsing\r
   //\r
-  for (Count = 0 ; Count < (*Argc) ; Count++) {\r
+  if (StrStr(*CmdLine, L"?") != NULL) {\r
     //\r
-    // if it's "-?" or if the first parameter is "?"\r
+    // This may do nothing if the ? does not indicate help.\r
+    // Save all the details for in the API below.\r
     //\r
-    if (StrStr((*Argv)[Count], L"-?") == (*Argv)[Count] \r
-    ||  ((*Argv)[0][0] == L'?' && (*Argv)[0][1] == CHAR_NULL)\r
-      ) {\r
+    Status = DoHelpUpdate(CmdLine);\r
+  }\r
+\r
+  TrimSpaces(CmdLine);\r
+\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+  Run an internal shell command.\r
+\r
+  This API will upadate the shell's environment since these commands are libraries.\r
+  \r
+  @param[in] CmdLine          the command line to run.\r
+  @param[in] FirstParameter   the first parameter on the command line\r
+  @param[in] ParamProtocol    the shell parameters protocol pointer\r
+\r
+  @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
+    // Run the internal command.\r
+    //\r
+    Status = ShellCommandRunCommandHandler(FirstParameter, &CommandReturnedStatus, &LastError);\r
+\r
+    if (!EFI_ERROR(Status)) {\r
       //\r
-      // We need to redo the arguments since a parameter was -?\r
-      // move them all down 1 to the end, then up one then replace the first with help\r
+      // Update last error status.\r
+      // some commands do not update last error.\r
       //\r
-      FreePool((*Argv)[Count]);\r
-      (*Argv)[Count] = NULL;\r
-      for (Count2 = Count ; (Count2 + 1) < (*Argc) ; Count2++) {\r
-        (*Argv)[Count2] = (*Argv)[Count2+1];\r
+      if (LastError) {\r
+        SetLastError(CommandReturnedStatus);\r
       }\r
-      (*Argv)[Count2] = NULL;\r
-      for (Count2 = (*Argc) -1 ; Count2 > 0 ; Count2--) {\r
-        (*Argv)[Count2] = (*Argv)[Count2-1];\r
+\r
+      //\r
+      // Pass thru the exitcode from the app.\r
+      //\r
+      if (ShellCommandGetExit()) {\r
+        Status = CommandReturnedStatus;\r
+      } else if (CommandReturnedStatus != 0 && IsScriptOnlyCommand(FirstParameter)) {\r
+        Status = EFI_ABORTED;\r
       }\r
-      (*Argv)[0] = NULL;\r
-      (*Argv)[0] = StrnCatGrow(&(*Argv)[0], NULL, L"help", 0);\r
-      if ((*Argv)[0] == NULL) {\r
-        return (EFI_OUT_OF_RESOURCES);\r
+    }\r
+  }\r
+\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
+  if (ShellCommandGetCurrentScriptFile() != NULL &&  !IsScriptOnlyCommand(FirstParameter)) {\r
+    //\r
+    // if this is NOT a scipt only command return success so the script won't quit.\r
+    // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)\r
+    //\r
+    Status = EFI_SUCCESS;\r
+  }\r
+\r
+  return (Status);\r
+}\r
+\r
+/**\r
+  Function to run the command or file.\r
+\r
+  @param[in] Type             the type of operation being run.\r
+  @param[in] CmdLine          the command line to run.\r
+  @param[in] FirstParameter   the first parameter on the command line\r
+  @param[in] ParamProtocol    the shell parameters protocol pointer\r
+\r
+  @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
+      // Process a fully qualified path\r
+      //\r
+      if (StrStr(FirstParameter, L":") != NULL) {\r
+        ASSERT (CommandWithPath == NULL);\r
+        if (ShellIsFile(FirstParameter) == EFI_SUCCESS) {\r
+          CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, FirstParameter, 0);\r
+        }\r
+      }\r
+\r
+      //\r
+      // Process a relative path and also check in the path environment variable\r
+      //\r
+      if (CommandWithPath == NULL) {\r
+        CommandWithPath = ShellFindFilePathEx(FirstParameter, mExecutableExtensions);\r
+      }\r
+\r
+      //\r
+      // This should be impossible now.\r
+      //\r
+      ASSERT(CommandWithPath != NULL);\r
+\r
+      //\r
+      // Make sure that path is not just a directory (or not found)\r
+      //\r
+      if (!EFI_ERROR(ShellIsDirectory(CommandWithPath))) {\r
+        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
+        SetLastError(EFI_NOT_FOUND);\r
+      }\r
+      switch (Type) {\r
+        case   SCRIPT_FILE_NAME:\r
+          Status = RunScriptFile (CommandWithPath);\r
+          break;\r
+        case   EFI_APPLICATION:\r
+          //\r
+          // Get the device path of the application image\r
+          //\r
+          DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
+          if (DevPath == NULL){\r
+            Status = EFI_OUT_OF_RESOURCES;\r
+            break;\r
+          }\r
+\r
+          //\r
+          // Execute the device path\r
+          //\r
+          Status = InternalShellExecuteDevicePath(\r
+            &gImageHandle,\r
+            DevPath,\r
+            CmdLine,\r
+            NULL,\r
+            &StatusCode\r
+           );\r
+\r
+          SHELL_FREE_NON_NULL(DevPath);\r
+\r
+          //\r
+          // Update last error status.\r
+          //\r
+          SetLastError(StatusCode);\r
+          break;\r
       }\r
       break;\r
-    }\r
   }\r
-  return (EFI_SUCCESS);\r
+\r
+  SHELL_FREE_NON_NULL(CommandWithPath);\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
@@ -1738,31 +2041,16 @@ 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[19];\r
-  CHAR16                    *CommandWithPath;\r
-  CONST EFI_DEVICE_PATH_PROTOCOL  *DevPath;\r
-  CONST CHAR16              *TempLocation;\r
-  CONST CHAR16              *TempLocation2;\r
-  SHELL_FILE_HANDLE         OriginalStdIn;\r
-  SHELL_FILE_HANDLE         OriginalStdOut;\r
-  SHELL_FILE_HANDLE         OriginalStdErr;\r
-  SYSTEM_TABLE_INFO         OriginalSystemTableInfo;\r
   CHAR16                    *CleanOriginal;\r
+  CHAR16                    *FirstParameter;\r
+  CHAR16                    *TempWalker;\r
+  SHELL_OPERATION_TYPES     Type;\r
 \r
   ASSERT(CmdLine != NULL);\r
   if (StrLen(CmdLine) == 0) {\r
     return (EFI_SUCCESS);\r
   }\r
 \r
-  CommandName         = NULL;\r
-  CommandWithPath     = NULL;\r
-  DevPath             = NULL;\r
   Status              = EFI_SUCCESS;\r
   CleanOriginal       = NULL;\r
 \r
@@ -1777,180 +2065,59 @@ RunCommand(
   // Handle case that passed in command line is just 1 or more " " characters.\r
   //\r
   if (StrLen (CleanOriginal) == 0) {\r
-    if (CleanOriginal != NULL) {\r
-      FreePool(CleanOriginal);\r
-      CleanOriginal = NULL;\r
-    }\r
+    SHELL_FREE_NON_NULL(CleanOriginal);\r
     return (EFI_SUCCESS);\r
   }\r
 \r
-  Status = ShellSubstituteAliases(&CleanOriginal);\r
-  if (EFI_ERROR(Status)) {\r
-    return (Status);\r
-  }\r
-\r
-  Status = ShellSubstituteVariables(&CleanOriginal);\r
+  Status = ProcessCommandLineAliasVariable(&CleanOriginal);\r
   if (EFI_ERROR(Status)) {\r
+    SHELL_FREE_NON_NULL(CleanOriginal);\r
     return (Status);\r
   }\r
 \r
-  TrimSpaces(&CleanOriginal);\r
-\r
   //\r
   // We dont do normal processing with a split command line (output from one command input to another)\r
   //\r
   if (ContainsSplit(CleanOriginal)) {\r
     Status = ProcessNewSplitCommandLine(CleanOriginal);\r
-  } else {\r
-    //\r
-    // If this is a mapped drive change handle that...\r
-    //\r
-    if (CleanOriginal[(StrLen(CleanOriginal)-1)] == L':' && StrStr(CleanOriginal, L" ") == NULL) {\r
-      Status = ChangeMappedDrive(CleanOriginal);\r
-      SHELL_FREE_NON_NULL(CleanOriginal);\r
-      return (Status);\r
-    }\r
-\r
-\r
-    ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...\r
-    ///      We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...\r
-\r
-\r
-\r
-    Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, CleanOriginal, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
-    if (EFI_ERROR(Status)) {\r
-      if (Status == EFI_NOT_FOUND) {\r
-        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle);\r
-      } else {\r
-        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);\r
-      }\r
-    } else {\r
-      TrimSpaces(&CleanOriginal);\r
-    \r
-      //\r
-      // get the argc and argv updated for internal commands\r
-      //\r
-      Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, CleanOriginal, &Argv, &Argc);\r
-      ASSERT_EFI_ERROR(Status);\r
-\r
-      if (StrStr(CleanOriginal, L"?") != NULL) {\r
-        Status = DoHelpUpdateArgcArgv(\r
-          &ShellInfoObject.NewShellParametersProtocol->Argc,\r
-          &ShellInfoObject.NewShellParametersProtocol->Argv);\r
-      }\r
-\r
-      //\r
-      // command or file?\r
-      //\r
-      if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {\r
-        //\r
-        // Run the command (which was converted if it was an alias)\r
-        //\r
-        if (!EFI_ERROR(Status))  {\r
-          Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError);\r
-          ASSERT_EFI_ERROR(Status);\r
-\r
-          if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
-            UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellStatus);\r
-          } else {\r
-            UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ShellStatus);\r
-          }\r
-          DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
-          if (LastError) {\r
-            InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
-          }\r
-          //\r
-          // Pass thru the exitcode from the app.\r
-          //\r
-          if (ShellCommandGetExit()) {\r
-            Status = ShellStatus;\r
-          } else if (ShellStatus != 0 && IsScriptOnlyCommand(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {\r
-            Status = EFI_ABORTED;\r
-          }\r
-        }\r
-      } else {\r
-        //\r
-        // run an external file (or script)\r
-        //\r
-        if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[0], L":") != NULL) {\r
-          ASSERT (CommandWithPath == NULL);\r
-          if (ShellIsFile(ShellInfoObject.NewShellParametersProtocol->Argv[0]) == EFI_SUCCESS) {\r
-            CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
-          }\r
-        }\r
-        if (CommandWithPath == NULL) {\r
-          CommandWithPath = ShellFindFilePathEx(ShellInfoObject.NewShellParametersProtocol->Argv[0], mExecutableExtensions);\r
-        }\r
-        if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) {\r
-          ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]);\r
-\r
-          if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
-            UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", EFI_NOT_FOUND);\r
-          } else {\r
-            UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", EFI_NOT_FOUND);\r
-          }\r
-          DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
-          InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
-        } else {\r
-          //\r
-          // Check if it's a NSH (script) file.\r
-          //\r
-          TempLocation = CommandWithPath+StrLen(CommandWithPath)-4;\r
-          TempLocation2 = mScriptExtension;\r
-          if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) {\r
-            Status = RunScriptFile (CommandWithPath);\r
-          } else {\r
-            DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
-            ASSERT(DevPath != NULL);\r
-            Status = InternalShellExecuteDevicePath(\r
-              &gImageHandle,\r
-              DevPath,\r
-              CleanOriginal,\r
-              NULL,\r
-              &StatusCode\r
-             );\r
+    SHELL_FREE_NON_NULL(CleanOriginal);\r
+    return (Status);\r
+  } \r
 \r
-            //\r
-            // Update last error status.\r
-            //\r
-            if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
-              UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", StatusCode);\r
-            } else {\r
-              UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", StatusCode);\r
-            }\r
-            DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
-            InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
-          }\r
-        }\r
-      }\r
+  //\r
+  // We need the first parameter information so we can determine the operation type\r
+  //\r
+  FirstParameter = AllocateZeroPool(StrSize(CleanOriginal));\r
+  if (FirstParameter == NULL) {\r
+    SHELL_FREE_NON_NULL(CleanOriginal);\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  }\r
+  TempWalker = CleanOriginal;\r
+  GetNextParameter(&TempWalker, &FirstParameter);\r
 \r
+  //\r
+  // Depending on the first parameter we change the behavior\r
+  //\r
+  switch (Type = GetOperationType(FirstParameter)) {\r
+    case   FILE_SYS_CHANGE:\r
+      Status = ChangeMappedDrive(CleanOriginal);\r
+      break;\r
+    case   INTERNAL_COMMAND:\r
+    case   SCRIPT_FILE_NAME:\r
+    case   EFI_APPLICATION:\r
+      Status = SetupAndRunCommandOrFile(Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol);\r
+      break;\r
+    default:\r
       //\r
-      // Print some error info.\r
+      // Whatever was typed, it was invalid.\r
       //\r
-      if (EFI_ERROR(Status)) {\r
-        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));\r
-      }\r
-\r
-      CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
-\r
-      RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);\r
-\r
-      RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
-    }\r
-    if (CommandName != NULL) {\r
-      if (ShellCommandGetCurrentScriptFile() != NULL &&  !IsScriptOnlyCommand(CommandName)) {\r
-        //\r
-        // if this is NOT a scipt only command return success so the script won't quit.\r
-        // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)\r
-        //\r
-        Status = EFI_SUCCESS;\r
-      }\r
-    }\r
+      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
+      SetLastError(EFI_NOT_FOUND);\r
+      break;\r
   }\r
-\r
-  SHELL_FREE_NON_NULL(CommandName);\r
-  SHELL_FREE_NON_NULL(CommandWithPath);\r
\r
   SHELL_FREE_NON_NULL(CleanOriginal);\r
+  SHELL_FREE_NON_NULL(FirstParameter);\r
 \r
   return (Status);\r
 }\r