]> 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 ef757dec0fa724f87b490cd45fde513fed3a16d1..ef821a5c5d021bc5ce20dfb8abb6ba37774af35b 100644 (file)
@@ -91,9 +91,9 @@ TrimSpaces(
   }\r
 \r
   //\r
   }\r
 \r
   //\r
-  // Remove any spaces at the end of the (*String).\r
+  // Remove any spaces and tabs at the end of the (*String).\r
   //\r
   //\r
-  while ((*String)[StrLen((*String))-1] == L' ') {\r
+  while ((StrLen (*String) > 0) && (((*String)[StrLen((*String))-1] == L' ') || ((*String)[StrLen((*String))-1] == L'\t'))) {\r
     (*String)[StrLen((*String))-1] = CHAR_NULL;\r
   }\r
 \r
     (*String)[StrLen((*String))-1] = CHAR_NULL;\r
   }\r
 \r
@@ -144,7 +144,7 @@ ContainsSplit(
 {\r
   CONST CHAR16 *TempSpot;\r
   TempSpot = FindSplit(CmdLine);\r
 {\r
   CONST CHAR16 *TempSpot;\r
   TempSpot = FindSplit(CmdLine);\r
-  return (TempSpot != NULL && *TempSpot != CHAR_NULL);\r
+  return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));\r
 }\r
 \r
 /**\r
 }\r
 \r
 /**\r
@@ -1434,8 +1434,8 @@ RunSplitCommand(
   Take the original command line, substitute any variables, free \r
   the original string, return the modified copy\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
+  @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 function was successful\r
   @retval EFI_OUT_OF_RESOURCES  a memory allocation failed\r
 \r
   @retval EFI_SUCCESS           the function was successful\r
   @retval EFI_OUT_OF_RESOURCES  a memory allocation failed\r
@@ -1529,6 +1529,14 @@ ShellSubstituteAliases(
 \r
 /**\r
   Takes the Argv[0] part of the command line and determine the meaning of it.\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
 **/\r
 SHELL_OPERATION_TYPES\r
 EFIAPI\r
@@ -1588,275 +1596,650 @@ GetOperationType(
   return (UNKNOWN_INVALID);\r
 }\r
 \r
   return (UNKNOWN_INVALID);\r
 }\r
 \r
-/**\r
-  Function will process and run a command line.\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
 \r
-  This will determine if the command line represents an internal shell \r
-  command or dispatch an external application.\r
+  Temp           = NULL;\r
+\r
+  Temp = StrnCatGrow(&Temp, NULL, CmdLine, 0);\r
+  if (Temp == NULL) {\r
+    return (EFI_OUT_OF_RESOURCES);\r
+  }\r
+\r
+  FirstParameter = StrStr(Temp, L"|");\r
+  if (FirstParameter != NULL) {\r
+    *FirstParameter = CHAR_NULL;\r
+  }\r
+\r
+  FirstParameter = NULL;\r
+\r
+  //\r
+  // Process the command line\r
+  //\r
+  Status = ProcessCommandLineToFinal(&Temp);\r
+\r
+  if (!EFI_ERROR(Status)) {\r
+    FirstParameter = AllocateZeroPool(StrSize(CmdLine));\r
+    if (FirstParameter == NULL) {\r
+      SHELL_FREE_NON_NULL(Temp);\r
+      return (EFI_OUT_OF_RESOURCES);\r
+    }\r
+    TempWalker = (CHAR16*)Temp;\r
+    GetNextParameter(&TempWalker, &FirstParameter);\r
+\r
+    if (GetOperationType(FirstParameter) == UNKNOWN_INVALID) {\r
+      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
+      SetLastError(SHELL_NOT_FOUND);\r
+      Status = EFI_NOT_FOUND;\r
+    }\r
+  }\r
+\r
+  SHELL_FREE_NON_NULL(Temp);\r
+  SHELL_FREE_NON_NULL(FirstParameter);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Determine if a command line contains with a split contains only valid commands\r
 \r
   @param[in] CmdLine      The command line to parse.\r
 \r
 \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
+  @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
 **/\r
 EFI_STATUS\r
 EFIAPI\r
-RunCommand(\r
-  IN CONST CHAR16   *CmdLine\r
+VerifySplit(\r
+  IN CONST CHAR16 *CmdLine\r
   )\r
 {\r
   )\r
 {\r
-  EFI_STATUS                Status;\r
-  EFI_STATUS                StatusCode;\r
-  CHAR16                    *CommandName;\r
-  SHELL_STATUS              ShellStatus;\r
-  UINTN                     Argc;\r
-  CHAR16                    **Argv;\r
-  BOOLEAN                   LastError;\r
-  CHAR16                    LeString[19];\r
-  CHAR16                    *CommandWithPath;\r
-  CONST EFI_DEVICE_PATH_PROTOCOL  *DevPath;\r
-  CONST CHAR16              *TempLocation;\r
-  CONST CHAR16              *TempLocation2;\r
-  SHELL_FILE_HANDLE         OriginalStdIn;\r
-  SHELL_FILE_HANDLE         OriginalStdOut;\r
-  SHELL_FILE_HANDLE         OriginalStdErr;\r
-  SYSTEM_TABLE_INFO         OriginalSystemTableInfo;\r
-  UINTN                     Count;\r
-  UINTN                     Count2;\r
-  CHAR16                    *CleanOriginal;\r
-  SPLIT_LIST                *Split;\r
+  CONST CHAR16  *TempSpot;\r
+  EFI_STATUS    Status;\r
 \r
 \r
-  ASSERT(CmdLine != NULL);\r
-  if (StrLen(CmdLine) == 0) {\r
+  //\r
+  // Verify up to the pipe or end character\r
+  //\r
+  Status = IsValidSplit(CmdLine);\r
+  if (EFI_ERROR(Status)) {\r
+    return (Status);\r
+  }\r
+\r
+  //\r
+  // If this was the only item, then get out\r
+  //\r
+  if (!ContainsSplit(CmdLine)) {\r
     return (EFI_SUCCESS);\r
   }\r
 \r
     return (EFI_SUCCESS);\r
   }\r
 \r
-  CommandName         = NULL;\r
-  CommandWithPath     = NULL;\r
-  DevPath             = NULL;\r
-  Status              = EFI_SUCCESS;\r
-  CleanOriginal       = NULL;\r
-  Split               = NULL;\r
+  //\r
+  // recurse to verify the next item\r
+  //\r
+  TempSpot = FindSplit(CmdLine)+1;\r
+  return (VerifySplit(TempSpot));\r
+}\r
 \r
 \r
-  CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);\r
-  if (CleanOriginal == NULL) {\r
-    return (EFI_OUT_OF_RESOURCES);\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
   }\r
 \r
-  TrimSpaces(&CleanOriginal);\r
+  Split = NULL;\r
 \r
   //\r
 \r
   //\r
-  // Handle case that passed in command line is just 1 or more " " characters.\r
+  // are we in an existing split???\r
   //\r
   //\r
-  if (StrLen (CleanOriginal) == 0) {\r
-    if (CleanOriginal != NULL) {\r
-      FreePool(CleanOriginal);\r
-      CleanOriginal = NULL;\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
-    return (EFI_SUCCESS);\r
   }\r
 \r
   }\r
 \r
-  Status = ShellSubstituteAliases(&CleanOriginal);\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
   if (EFI_ERROR(Status)) {\r
     return (Status);\r
   }\r
 \r
-  Status = ShellSubstituteVariables(&CleanOriginal);\r
+  TrimSpaces(CmdLine);\r
+\r
+  Status = ShellSubstituteVariables(CmdLine);\r
   if (EFI_ERROR(Status)) {\r
     return (Status);\r
   }\r
 \r
   if (EFI_ERROR(Status)) {\r
     return (Status);\r
   }\r
 \r
-  TrimSpaces(&CleanOriginal);\r
+  TrimSpaces(CmdLine);\r
 \r
   //\r
 \r
   //\r
-  // We dont do normal processing with a split command line (output from one command input to another)\r
+  // update for help parsing\r
   //\r
   //\r
-  if (ContainsSplit(CleanOriginal)) {\r
+  if (StrStr(*CmdLine, L"?") != NULL) {\r
     //\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
     //\r
-    if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {\r
-      Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);\r
-    }\r
+    Status = DoHelpUpdate(CmdLine);\r
+  }\r
 \r
 \r
-    if (Split == NULL) {\r
-      Status = RunSplitCommand(CleanOriginal, NULL, NULL);\r
-    } else {\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
+  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
+  //\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
     //\r
-    // If this is a mapped drive change handle that...\r
+    // Run the internal command.\r
     //\r
     //\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, CleanOriginal);\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
       }\r
-      FreePool(CleanOriginal);\r
-      return (Status);\r
     }\r
     }\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
+  //\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
 \r
+  return (Status);\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
+  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
       //\r
-      // get the argc and argv updated for internal commands\r
+      // Process a fully qualified path\r
       //\r
       //\r
-      Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, CleanOriginal, &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
+      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
         }\r
       }\r
 \r
       //\r
-      // command or file?\r
+      // Process a relative path and also check in the path environment variable\r
       //\r
       //\r
-      if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {\r
-        //\r
-        // Run the command (which was converted if it was an alias)\r
-        //\r
-        if (!EFI_ERROR(Status))  {\r
-          Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError);\r
-          ASSERT_EFI_ERROR(Status);\r
+      if (CommandWithPath == NULL) {\r
+        CommandWithPath = ShellFindFilePathEx(FirstParameter, mExecutableExtensions);\r
+      }\r
 \r
 \r
-          if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
-            UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellStatus);\r
-          } else {\r
-            UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ShellStatus);\r
-          }\r
-          DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
-          if (LastError) {\r
-            InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
-          }\r
+      //\r
+      // This should be impossible now.\r
+      //\r
+      ASSERT(CommandWithPath != NULL);\r
+\r
+      //\r
+      // Make sure that path is not just a directory (or not found)\r
+      //\r
+      if (!EFI_ERROR(ShellIsDirectory(CommandWithPath))) {\r
+        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
+        SetLastError(SHELL_NOT_FOUND);\r
+      }\r
+      switch (Type) {\r
+        case   SCRIPT_FILE_NAME:\r
+          Status = RunScriptFile (CommandWithPath);\r
+          break;\r
+        case   EFI_APPLICATION:\r
           //\r
           //\r
-          // Pass thru the exitcode from the app.\r
+          // Get the device path of the application image\r
           //\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
-        }\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
 \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
           //\r
-          // Check if it's a NSH (script) file.\r
+          // Execute the device path\r
           //\r
           //\r
-          TempLocation = CommandWithPath+StrLen(CommandWithPath)-4;\r
-          TempLocation2 = mScriptExtension;\r
-          if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) {\r
-            Status = RunScriptFile (CommandWithPath);\r
-          } else {\r
-            DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
-            ASSERT(DevPath != NULL);\r
-            Status = InternalShellExecuteDevicePath(\r
-              &gImageHandle,\r
-              DevPath,\r
-              CleanOriginal,\r
-              NULL,\r
-              &StatusCode\r
-             );\r
+          Status = InternalShellExecuteDevicePath(\r
+            &gImageHandle,\r
+            DevPath,\r
+            CmdLine,\r
+            NULL,\r
+            &StatusCode\r
+           );\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
+          SHELL_FREE_NON_NULL(DevPath);\r
 \r
 \r
+          //\r
+          // Update last error status.\r
+          //\r
+          SetLastError(StatusCode);\r
+          break;\r
+        default:\r
+          //\r
+          // Do nothing.\r
+          //\r
+          break;\r
+      }\r
+      break;\r
+    default:\r
       //\r
       //\r
-      // Print some error info.\r
+      // Do nothing.\r
       //\r
       //\r
-      if (EFI_ERROR(Status)) {\r
-        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));\r
-      }\r
+      break;\r
+  }\r
 \r
 \r
-      CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
+  SHELL_FREE_NON_NULL(CommandWithPath);\r
 \r
 \r
-      RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);\r
+  return (Status);\r
+}\r
 \r
 \r
-      RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
-    }\r
-    if (CommandName != NULL) {\r
-      if (ShellCommandGetCurrentScriptFile() != NULL &&  !IsScriptOnlyCommand(CommandName)) {\r
-        //\r
-        // if this is NOT a scipt only command return success so the script won't quit.\r
-        // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)\r
-        //\r
-        Status = EFI_SUCCESS;\r
-      }\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
 \r
-  SHELL_FREE_NON_NULL(CommandName);\r
-  SHELL_FREE_NON_NULL(CommandWithPath);\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(CleanOriginal);\r
+  SHELL_FREE_NON_NULL(FirstParameter);\r
 \r
   return (Status);\r
 }\r
 \r
   return (Status);\r
 }\r