+ CONST CHAR16 *TempSpot;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Verify up to the pipe or end character\r
+ //\r
+ Status = IsValidSplit(CmdLine);\r
+ if (EFI_ERROR(Status)) {\r
+ return (Status);\r
+ }\r
+\r
+ //\r
+ // If this was the only item, then get out\r
+ //\r
+ if (!ContainsSplit(CmdLine)) {\r
+ return (EFI_SUCCESS);\r
+ }\r
+\r
+ //\r
+ // recurse to verify the next item\r
+ //\r
+ TempSpot = FindSplit(CmdLine)+1;\r
+ return (VerifySplit(TempSpot));\r
+}\r
+\r
+/**\r
+ Process a split based operation.\r
+\r
+ @param[in] CmdLine pointer to the command line to process\r
+\r
+ @retval EFI_SUCCESS The operation was successful\r
+ @return an error occured.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ProcessNewSplitCommandLine(\r
+ IN CONST CHAR16 *CmdLine\r
+ )\r
+{\r
+ SPLIT_LIST *Split;\r
+ EFI_STATUS Status;\r
+\r
+ Status = VerifySplit(CmdLine);\r
+ if (EFI_ERROR(Status)) {\r
+ return (Status);\r
+ }\r
+\r
+ Split = NULL;\r
+\r
+ //\r
+ // are we in an existing split???\r
+ //\r
+ if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {\r
+ Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);\r
+ }\r
+\r
+ if (Split == NULL) {\r
+ Status = RunSplitCommand(CmdLine, NULL, NULL);\r
+ } else {\r
+ Status = RunSplitCommand(CmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
+ }\r
+ if (EFI_ERROR(Status)) {\r
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);\r
+ }\r
+ return (Status);\r
+}\r
+\r
+/**\r
+ Handle a request to change the current file system.\r
+\r
+ @param[in] CmdLine The passed in command line.\r
+\r
+ @retval EFI_SUCCESS The operation was successful.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ChangeMappedDrive(\r
+ IN CONST CHAR16 *CmdLine\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // make sure we are the right operation\r
+ //\r
+ ASSERT(CmdLine[(StrLen(CmdLine)-1)] == L':' && StrStr(CmdLine, L" ") == NULL);\r
+ \r
+ //\r
+ // Call the protocol API to do the work\r
+ //\r
+ Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, CmdLine);\r
+\r
+ //\r
+ // Report any errors\r
+ //\r
+ if (EFI_ERROR(Status)) {\r
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine);\r
+ }\r
+\r
+ return (Status);\r
+}\r
+\r
+/**\r
+ Reprocess the command line to direct all -? to the help command.\r
+\r
+ if found, will add "help" as argv[0], and move the rest later.\r
+\r
+ @param[in,out] CmdLine pointer to the command line to update\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DoHelpUpdate(\r
+ IN OUT CHAR16 **CmdLine\r
+ )\r
+{\r
+ CHAR16 *CurrentParameter;\r
+ CHAR16 *Walker;\r
+ CHAR16 *LastWalker;\r
+ CHAR16 *NewCommandLine;\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ CurrentParameter = AllocateZeroPool(StrSize(*CmdLine));\r
+ if (CurrentParameter == NULL) {\r
+ return (EFI_OUT_OF_RESOURCES);\r
+ }\r
+\r
+ Walker = *CmdLine;\r
+ while(Walker != NULL && *Walker != CHAR_NULL) {\r
+ LastWalker = Walker;\r
+ GetNextParameter(&Walker, &CurrentParameter, StrSize(*CmdLine));\r
+ if (StrStr(CurrentParameter, L"-?") == CurrentParameter) {\r
+ LastWalker[0] = L' ';\r
+ LastWalker[1] = L' ';\r
+ NewCommandLine = AllocateZeroPool(StrSize(L"help ") + StrSize(*CmdLine));\r
+ if (NewCommandLine == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ break;\r
+ }\r
+\r
+ //\r
+ // We know the space is sufficient since we just calculated it.\r
+ //\r
+ StrnCpy(NewCommandLine, L"help ", 5);\r
+ StrnCat(NewCommandLine, *CmdLine, StrLen(*CmdLine));\r
+ SHELL_FREE_NON_NULL(*CmdLine);\r
+ *CmdLine = NewCommandLine;\r
+ break;\r
+ }\r
+ }\r
+\r
+ SHELL_FREE_NON_NULL(CurrentParameter);\r
+\r
+ return (Status);\r
+}\r
+\r
+/**\r
+ Function to update the shell variable "lasterror".\r
+\r
+ @param[in] ErrorCode the error code to put into lasterror.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SetLastError(\r
+ IN CONST SHELL_STATUS ErrorCode\r
+ )\r
+{\r
+ CHAR16 LeString[19];\r
+ if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
+ UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ErrorCode);\r
+ } else {\r
+ UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ErrorCode);\r
+ }\r
+ DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
+ InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
+\r
+ return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+ Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.\r
+\r
+ @param[in,out] CmdLine pointer to the command line to update\r
+\r
+ @retval EFI_SUCCESS The operation was successful\r
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
+ @return some other error occured\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ProcessCommandLineToFinal(\r
+ IN OUT CHAR16 **CmdLine\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ TrimSpaces(CmdLine);\r
+\r
+ Status = ShellSubstituteAliases(CmdLine);\r
+ if (EFI_ERROR(Status)) {\r
+ return (Status);\r
+ }\r
+\r
+ TrimSpaces(CmdLine);\r
+\r
+ Status = ShellSubstituteVariables(CmdLine);\r
+ if (EFI_ERROR(Status)) {\r
+ return (Status);\r
+ }\r
+ ASSERT (*CmdLine != NULL);\r
+\r
+ TrimSpaces(CmdLine);\r
+\r
+ //\r
+ // update for help parsing\r
+ //\r
+ if (StrStr(*CmdLine, L"?") != NULL) {\r
+ //\r
+ // This may do nothing if the ? does not indicate help.\r
+ // Save all the details for in the API below.\r
+ //\r
+ Status = DoHelpUpdate(CmdLine);\r
+ }\r
+\r
+ TrimSpaces(CmdLine);\r
+\r
+ 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
+ CHAR16 *Walker;\r
+ CHAR16 *NewCmdLine; \r
+\r
+ NewCmdLine = AllocateCopyPool (StrSize (CmdLine), CmdLine);\r
+ if (NewCmdLine == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL ; Walker++) {\r
+ if (*Walker == L'^' && *(Walker+1) == L'#') {\r
+ CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0]));\r
+ }\r
+ }\r
+\r
+ //\r
+ // get the argc and argv updated for internal commands\r
+ //\r
+ Status = UpdateArgcArgv(ParamProtocol, NewCmdLine, &Argv, &Argc);\r
+ if (!EFI_ERROR(Status)) {\r
+ //\r
+ // Run the internal command.\r
+ //\r
+ Status = ShellCommandRunCommandHandler(FirstParameter, &CommandReturnedStatus, &LastError);\r
+\r
+ if (!EFI_ERROR(Status)) {\r
+ //\r
+ // Update last error status.\r
+ // some commands do not update last error.\r
+ //\r
+ if (LastError) {\r
+ SetLastError(CommandReturnedStatus);\r
+ }\r
+\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
+ }\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
+ //\r
+ // If a script is running and the command is not a scipt only command, then\r
+ // change return value to success so the script won't halt (unless aborted).\r
+ //\r
+ // Script only commands have to be able halt the script since the script will\r
+ // not operate if they are failing.\r
+ //\r
+ if ( ShellCommandGetCurrentScriptFile() != NULL\r
+ && !IsScriptOnlyCommand(FirstParameter)\r
+ && Status != EFI_ABORTED\r
+ ) {\r
+ Status = EFI_SUCCESS;\r
+ }\r
+\r
+ FreePool (NewCmdLine);\r
+ return (Status);\r
+}\r
+\r
+/**\r
+ Function to run the command or file.\r
+\r
+ @param[in] Type the type of operation being run.\r
+ @param[in] CmdLine the command line to run.\r
+ @param[in] FirstParameter the first parameter on the command line\r
+ @param[in] ParamProtocol the shell parameters protocol pointer\r
+\r
+ @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 StartStatus;\r
+ CHAR16 *CommandWithPath;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevPath;\r
+ SHELL_STATUS CalleeExitStatus;\r
+\r
+ Status = EFI_SUCCESS;\r
+ CommandWithPath = NULL;\r
+ DevPath = NULL;\r
+ CalleeExitStatus = SHELL_INVALID_PARAMETER;\r
+\r
+ switch (Type) {\r
+ case Internal_Command:\r
+ Status = RunInternalCommand(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(SHELL_NOT_FOUND);\r
+ }\r
+ switch (Type) {\r
+ case Script_File_Name:\r
+ Status = RunScriptFile (CommandWithPath, NULL, CmdLine, ParamProtocol);\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
+ &StartStatus\r
+ );\r
+\r
+ SHELL_FREE_NON_NULL(DevPath);\r
+\r
+ if(EFI_ERROR (Status)) {\r
+ CalleeExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));\r
+ } else {\r
+ CalleeExitStatus = (SHELL_STATUS) StartStatus;\r
+ }\r
+\r
+ //\r
+ // Update last error status.\r
+ //\r
+ // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS\r
+ SetLastError(CalleeExitStatus);\r
+ break;\r
+ default:\r
+ //\r
+ // Do nothing.\r
+ //\r
+ break;\r
+ }\r
+ break;\r
+ default:\r
+ //\r
+ // Do nothing.\r
+ //\r
+ break;\r
+ }\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
+ TrimSpaces(&CmdLine);\r
+ Status = RunCommandOrFile(Type, CmdLine, FirstParameter, ParamProtocol);\r
+ }\r
+\r
+ //\r
+ // Now print errors\r
+ //\r
+ if (EFI_ERROR(Status)) {\r
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));\r
+ }\r
+\r
+ //\r
+ // put back the original StdIn, StdOut, and StdErr\r
+ //\r
+ RestoreStdInStdOutStdErr(ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
+\r
+ return (Status);\r
+}\r
+\r
+/**\r
+ Function will process and run a command line.\r
+\r
+ This will determine if the command line represents an internal shell \r
+ command or dispatch an external application.\r
+\r
+ @param[in] CmdLine The command line to parse.\r
+\r
+ @retval EFI_SUCCESS The command was completed.\r
+ @retval EFI_ABORTED The command's operation was aborted.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RunCommand(\r
+ IN CONST CHAR16 *CmdLine\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CHAR16 *CleanOriginal;\r
+ CHAR16 *FirstParameter;\r
+ CHAR16 *TempWalker;\r
+ SHELL_OPERATION_TYPES Type;\r
+\r
+ ASSERT(CmdLine != NULL);\r
+ if (StrLen(CmdLine) == 0) {\r
+ return (EFI_SUCCESS);\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+ CleanOriginal = NULL;\r
+\r
+ CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);\r
+ if (CleanOriginal == NULL) {\r
+ return (EFI_OUT_OF_RESOURCES);\r
+ }\r
+\r
+ TrimSpaces(&CleanOriginal);\r
+\r
+ //\r
+ // NULL out comments (leveraged from RunScriptFileHandle() ).\r
+ // The # character on a line is used to denote that all characters on the same line\r
+ // and to the right of the # are to be ignored by the shell.\r
+ // Afterward, again remove spaces, in case any were between the last command-parameter and '#'.\r
+ //\r
+ for (TempWalker = CleanOriginal; TempWalker != NULL && *TempWalker != CHAR_NULL; TempWalker++) {\r
+ if (*TempWalker == L'^') {\r
+ if (*(TempWalker + 1) == L'#') {\r
+ TempWalker++;\r
+ }\r
+ } else if (*TempWalker == L'#') {\r
+ *TempWalker = CHAR_NULL;\r
+ }\r
+ }\r
+\r
+ 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, StrSize(CleanOriginal));\r
+\r
+ //\r
+ // Depending on the first parameter we change the behavior\r
+ //\r
+ switch (Type = GetOperationType(FirstParameter)) {\r
+ case File_Sys_Change:\r
+ Status = ChangeMappedDrive (FirstParameter);\r
+ break;\r
+ case Internal_Command:\r
+ case Script_File_Name:\r
+ case Efi_Application:\r
+ Status = SetupAndRunCommandOrFile(Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol);\r
+ break;\r
+ default:\r
+ //\r
+ // Whatever was typed, it was invalid.\r
+ //\r
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
+ SetLastError(SHELL_NOT_FOUND);\r
+ break;\r
+ }\r
+ \r
+ SHELL_FREE_NON_NULL(CleanOriginal);\r
+ SHELL_FREE_NON_NULL(FirstParameter);\r
+\r
+ return (Status);\r
+}\r
+\r
+STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};\r
+/**\r
+ Function determins if the CommandName COULD be a valid command. It does not determine whether\r
+ this is a valid command. It only checks for invalid characters.\r
+\r
+ @param[in] CommandName The name to check\r
+\r
+ @retval TRUE CommandName could be a command name\r
+ @retval FALSE CommandName could not be a valid command name\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+IsValidCommandName(\r
+ IN CONST CHAR16 *CommandName\r
+ )\r
+{\r
+ UINTN Count;\r
+ if (CommandName == NULL) {\r
+ ASSERT(FALSE);\r