X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=ShellPkg%2FApplication%2FShell%2FShell.c;h=ef821a5c5d021bc5ce20dfb8abb6ba37774af35b;hp=ef757dec0fa724f87b490cd45fde513fed3a16d1;hb=6fa0da7d52815979be31b8252aae839883dc7b0c;hpb=6ba2921da3c0b7db303b6db44810158a1fefd737 diff --git a/ShellPkg/Application/Shell/Shell.c b/ShellPkg/Application/Shell/Shell.c index ef757dec0f..ef821a5c5d 100644 --- a/ShellPkg/Application/Shell/Shell.c +++ b/ShellPkg/Application/Shell/Shell.c @@ -91,9 +91,9 @@ TrimSpaces( } // - // Remove any spaces at the end of the (*String). + // Remove any spaces and tabs at the end of the (*String). // - while ((*String)[StrLen((*String))-1] == L' ') { + while ((StrLen (*String) > 0) && (((*String)[StrLen((*String))-1] == L' ') || ((*String)[StrLen((*String))-1] == L'\t'))) { (*String)[StrLen((*String))-1] = CHAR_NULL; } @@ -144,7 +144,7 @@ ContainsSplit( { CONST CHAR16 *TempSpot; TempSpot = FindSplit(CmdLine); - return (TempSpot != NULL && *TempSpot != CHAR_NULL); + return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL)); } /** @@ -1434,8 +1434,8 @@ RunSplitCommand( Take the original command line, substitute any variables, free the original string, return the modified copy - @param[in] CmdLine pointer to the command line to update - @param[out]CmdName upon successful return the name of the command to be run + @param[in,out] CmdLine pointer to the command line to update + @param[out]CmdName upon successful return the name of the command to be run @retval EFI_SUCCESS the function was successful @retval EFI_OUT_OF_RESOURCES a memory allocation failed @@ -1529,6 +1529,14 @@ ShellSubstituteAliases( /** Takes the Argv[0] part of the command line and determine the meaning of it. + + @param[in] CmdLine pointer to the command line to update + + @retval INTERNAL_COMMAND The name is an internal command + @retval FILE_SYS_CHANGE the name is a file system change + @retval SCRIPT_FILE_NAME the name is a NSH script file + @retval UNKNOWN_INVALID the name is unknown + @retval EFI_APPLICATION the name is an application (.EFI) **/ SHELL_OPERATION_TYPES EFIAPI @@ -1588,275 +1596,650 @@ GetOperationType( return (UNKNOWN_INVALID); } -/** - Function will process and run a command line. +EFI_STATUS +EFIAPI +IsValidSplit( + IN CONST CHAR16 *CmdLine + ) +{ + CHAR16 *Temp; + CHAR16 *FirstParameter; + CHAR16 *TempWalker; + EFI_STATUS Status; - This will determine if the command line represents an internal shell - command or dispatch an external application. + Temp = NULL; + + Temp = StrnCatGrow(&Temp, NULL, CmdLine, 0); + if (Temp == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + FirstParameter = StrStr(Temp, L"|"); + if (FirstParameter != NULL) { + *FirstParameter = CHAR_NULL; + } + + FirstParameter = NULL; + + // + // Process the command line + // + Status = ProcessCommandLineToFinal(&Temp); + + if (!EFI_ERROR(Status)) { + FirstParameter = AllocateZeroPool(StrSize(CmdLine)); + if (FirstParameter == NULL) { + SHELL_FREE_NON_NULL(Temp); + return (EFI_OUT_OF_RESOURCES); + } + TempWalker = (CHAR16*)Temp; + GetNextParameter(&TempWalker, &FirstParameter); + + if (GetOperationType(FirstParameter) == UNKNOWN_INVALID) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter); + SetLastError(SHELL_NOT_FOUND); + Status = EFI_NOT_FOUND; + } + } + + SHELL_FREE_NON_NULL(Temp); + SHELL_FREE_NON_NULL(FirstParameter); + return Status; +} + +/** + Determine if a command line contains with a split contains only valid commands @param[in] CmdLine The command line to parse. - @retval EFI_SUCCESS The command was completed. - @retval EFI_ABORTED The command's operation was aborted. + @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split. + @retval EFI_ABORTED CmdLine has at least one invalid command or application. **/ EFI_STATUS EFIAPI -RunCommand( - IN CONST CHAR16 *CmdLine +VerifySplit( + IN CONST CHAR16 *CmdLine ) { - EFI_STATUS Status; - EFI_STATUS StatusCode; - CHAR16 *CommandName; - SHELL_STATUS ShellStatus; - UINTN Argc; - CHAR16 **Argv; - BOOLEAN LastError; - CHAR16 LeString[19]; - CHAR16 *CommandWithPath; - CONST EFI_DEVICE_PATH_PROTOCOL *DevPath; - CONST CHAR16 *TempLocation; - CONST CHAR16 *TempLocation2; - SHELL_FILE_HANDLE OriginalStdIn; - SHELL_FILE_HANDLE OriginalStdOut; - SHELL_FILE_HANDLE OriginalStdErr; - SYSTEM_TABLE_INFO OriginalSystemTableInfo; - UINTN Count; - UINTN Count2; - CHAR16 *CleanOriginal; - SPLIT_LIST *Split; + CONST CHAR16 *TempSpot; + EFI_STATUS Status; - ASSERT(CmdLine != NULL); - if (StrLen(CmdLine) == 0) { + // + // Verify up to the pipe or end character + // + Status = IsValidSplit(CmdLine); + if (EFI_ERROR(Status)) { + return (Status); + } + + // + // If this was the only item, then get out + // + if (!ContainsSplit(CmdLine)) { return (EFI_SUCCESS); } - CommandName = NULL; - CommandWithPath = NULL; - DevPath = NULL; - Status = EFI_SUCCESS; - CleanOriginal = NULL; - Split = NULL; + // + // recurse to verify the next item + // + TempSpot = FindSplit(CmdLine)+1; + return (VerifySplit(TempSpot)); +} - CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0); - if (CleanOriginal == NULL) { - return (EFI_OUT_OF_RESOURCES); +/** + Process a split based operation. + + @param[in] CmdLine pointer to the command line to process + + @retval EFI_SUCCESS The operation was successful + @return an error occured. +**/ +EFI_STATUS +EFIAPI +ProcessNewSplitCommandLine( + IN CONST CHAR16 *CmdLine + ) +{ + SPLIT_LIST *Split; + EFI_STATUS Status; + + Status = VerifySplit(CmdLine); + if (EFI_ERROR(Status)) { + return (Status); } - TrimSpaces(&CleanOriginal); + Split = NULL; // - // Handle case that passed in command line is just 1 or more " " characters. + // are we in an existing split??? // - if (StrLen (CleanOriginal) == 0) { - if (CleanOriginal != NULL) { - FreePool(CleanOriginal); - CleanOriginal = NULL; + if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) { + Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link); + } + + if (Split == NULL) { + Status = RunSplitCommand(CmdLine, NULL, NULL); + } else { + Status = RunSplitCommand(CmdLine, Split->SplitStdIn, Split->SplitStdOut); + } + if (EFI_ERROR(Status)) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine); + } + return (Status); +} + +/** + Handle a request to change the current file system + + @param[in] CmdLine The passed in command line + + @retval EFI_SUCCESS The operation was successful +**/ +EFI_STATUS +EFIAPI +ChangeMappedDrive( + IN CONST CHAR16 *CmdLine + ) +{ + EFI_STATUS Status; + Status = EFI_SUCCESS; + + // + // make sure we are the right operation + // + ASSERT(CmdLine[(StrLen(CmdLine)-1)] == L':' && StrStr(CmdLine, L" ") == NULL); + + // + // Call the protocol API to do the work + // + Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, CmdLine); + + // + // Report any errors + // + if (EFI_ERROR(Status)) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine); + } + + return (Status); +} + +/** + Reprocess the command line to direct all -? to the help command. + + if found, will add "help" as argv[0], and move the rest later. + + @param[in,out] CmdLine pointer to the command line to update +**/ +EFI_STATUS +EFIAPI +DoHelpUpdate( + IN OUT CHAR16 **CmdLine + ) +{ + CHAR16 *CurrentParameter; + CHAR16 *Walker; + CHAR16 *LastWalker; + CHAR16 *NewCommandLine; + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + CurrentParameter = AllocateZeroPool(StrSize(*CmdLine)); + if (CurrentParameter == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + Walker = *CmdLine; + while(Walker != NULL && *Walker != CHAR_NULL) { + LastWalker = Walker; + GetNextParameter(&Walker, &CurrentParameter); + if (StrStr(CurrentParameter, L"-?") == CurrentParameter) { + LastWalker[0] = L' '; + LastWalker[1] = L' '; + NewCommandLine = AllocateZeroPool(StrSize(L"help ") + StrSize(*CmdLine)); + if (NewCommandLine == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + StrCpy(NewCommandLine, L"help "); + StrCat(NewCommandLine, *CmdLine); + SHELL_FREE_NON_NULL(*CmdLine); + *CmdLine = NewCommandLine; + break; } - return (EFI_SUCCESS); } - Status = ShellSubstituteAliases(&CleanOriginal); + SHELL_FREE_NON_NULL(CurrentParameter); + + return (Status); +} + +/** + Function to update the shell variable "lasterror" + + @param[in] ErrorCode the error code to put into lasterror +**/ +EFI_STATUS +EFIAPI +SetLastError( + IN CONST SHELL_STATUS ErrorCode + ) +{ + CHAR16 LeString[19]; + if (sizeof(EFI_STATUS) == sizeof(UINT64)) { + UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ErrorCode); + } else { + UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ErrorCode); + } + DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE);); + InternalEfiShellSetEnv(L"lasterror", LeString, TRUE); + + return (EFI_SUCCESS); +} + +/** + Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec. + + @param[in,out] CmdLine pointer to the command line to update + + @retval EFI_SUCCESS The operation was successful + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @return some other error occured +**/ +EFI_STATUS +EFIAPI +ProcessCommandLineToFinal( + IN OUT CHAR16 **CmdLine + ) +{ + EFI_STATUS Status; + TrimSpaces(CmdLine); + + Status = ShellSubstituteAliases(CmdLine); if (EFI_ERROR(Status)) { return (Status); } - Status = ShellSubstituteVariables(&CleanOriginal); + TrimSpaces(CmdLine); + + Status = ShellSubstituteVariables(CmdLine); if (EFI_ERROR(Status)) { return (Status); } - TrimSpaces(&CleanOriginal); + TrimSpaces(CmdLine); // - // We dont do normal processing with a split command line (output from one command input to another) + // update for help parsing // - if (ContainsSplit(CleanOriginal)) { + if (StrStr(*CmdLine, L"?") != NULL) { // - // are we in an existing split??? + // This may do nothing if the ? does not indicate help. + // Save all the details for in the API below. // - if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) { - Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link); - } + Status = DoHelpUpdate(CmdLine); + } - if (Split == NULL) { - Status = RunSplitCommand(CleanOriginal, NULL, NULL); - } else { - Status = RunSplitCommand(CleanOriginal, Split->SplitStdIn, Split->SplitStdOut); - } - if (EFI_ERROR(Status)) { - ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CleanOriginal); - } - } else { + TrimSpaces(CmdLine); + + return (EFI_SUCCESS); +} + +/** + Run an internal shell command. + + This API will upadate the shell's environment since these commands are libraries. + + @param[in] CmdLine the command line to run. + @param[in] FirstParameter the first parameter on the command line + @param[in] ParamProtocol the shell parameters protocol pointer + + @retval EFI_SUCCESS The command was completed. + @retval EFI_ABORTED The command's operation was aborted. +**/ +EFI_STATUS +EFIAPI +RunInternalCommand( + IN CONST CHAR16 *CmdLine, + IN CHAR16 *FirstParameter, + IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol +) +{ + EFI_STATUS Status; + UINTN Argc; + CHAR16 **Argv; + SHELL_STATUS CommandReturnedStatus; + BOOLEAN LastError; + // + // get the argc and argv updated for internal commands + // + Status = UpdateArgcArgv(ParamProtocol, CmdLine, &Argv, &Argc); + if (!EFI_ERROR(Status)) { // - // If this is a mapped drive change handle that... + // Run the internal command. // - if (CleanOriginal[(StrLen(CleanOriginal)-1)] == L':' && StrStr(CleanOriginal, L" ") == NULL) { - Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, CleanOriginal); - if (EFI_ERROR(Status)) { - ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CleanOriginal); + Status = ShellCommandRunCommandHandler(FirstParameter, &CommandReturnedStatus, &LastError); + + if (!EFI_ERROR(Status)) { + // + // Update last error status. + // some commands do not update last error. + // + if (LastError) { + SetLastError(CommandReturnedStatus); + } + + // + // Pass thru the exitcode from the app. + // + if (ShellCommandGetExit()) { + // + // An Exit was requested ("exit" command), pass its value up. + // + Status = CommandReturnedStatus; + } else if (CommandReturnedStatus != SHELL_SUCCESS && IsScriptOnlyCommand(FirstParameter)) { + // + // Always abort when a script only command fails for any reason + // + Status = EFI_ABORTED; + } else if (ShellCommandGetCurrentScriptFile() != NULL && CommandReturnedStatus == SHELL_ABORTED) { + // + // Abort when in a script and a command aborted + // + Status = EFI_ABORTED; } - FreePool(CleanOriginal); - return (Status); } + } - ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file... - /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files... + // + // This is guarenteed to be called after UpdateArgcArgv no matter what else happened. + // This is safe even if the update API failed. In this case, it may be a no-op. + // + RestoreArgcArgv(ParamProtocol, &Argv, &Argc); + // + // If a script is running and the command is not a scipt only command, then + // change return value to success so the script won't halt (unless aborted). + // + // Script only commands have to be able halt the script since the script will + // not operate if they are failing. + // + if ( ShellCommandGetCurrentScriptFile() != NULL + && !IsScriptOnlyCommand(FirstParameter) + && Status != EFI_ABORTED + ) { + Status = EFI_SUCCESS; + } + return (Status); +} - Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, CleanOriginal, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo); - if (EFI_ERROR(Status)) { - if (Status == EFI_NOT_FOUND) { - ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle); - } else { - ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle); - } - } else { - TrimSpaces(&CleanOriginal); - +/** + Function to run the command or file. + + @param[in] Type the type of operation being run. + @param[in] CmdLine the command line to run. + @param[in] FirstParameter the first parameter on the command line + @param[in] ParamProtocol the shell parameters protocol pointer + + @retval EFI_SUCCESS The command was completed. + @retval EFI_ABORTED The command's operation was aborted. +**/ +EFI_STATUS +EFIAPI +RunCommandOrFile( + IN SHELL_OPERATION_TYPES Type, + IN CONST CHAR16 *CmdLine, + IN CHAR16 *FirstParameter, + IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol +) +{ + EFI_STATUS Status; + EFI_STATUS StatusCode; + CHAR16 *CommandWithPath; + EFI_DEVICE_PATH_PROTOCOL *DevPath; + + Status = EFI_SUCCESS; + CommandWithPath = NULL; + DevPath = NULL; + + switch (Type) { + case INTERNAL_COMMAND: + Status = RunInternalCommand(CmdLine, FirstParameter, ParamProtocol); + break; + case SCRIPT_FILE_NAME: + case EFI_APPLICATION: // - // get the argc and argv updated for internal commands + // Process a fully qualified path // - Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, CleanOriginal, &Argv, &Argc); - ASSERT_EFI_ERROR(Status); - - for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) { - if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[Count], L"-?") == ShellInfoObject.NewShellParametersProtocol->Argv[Count] - || (ShellInfoObject.NewShellParametersProtocol->Argv[0][0] == L'?' && ShellInfoObject.NewShellParametersProtocol->Argv[0][1] == CHAR_NULL) - ) { - // - // We need to redo the arguments since a parameter was -? - // move them all down 1 to the end, then up one then replace the first with help - // - FreePool(ShellInfoObject.NewShellParametersProtocol->Argv[Count]); - ShellInfoObject.NewShellParametersProtocol->Argv[Count] = NULL; - for (Count2 = Count ; (Count2 + 1) < ShellInfoObject.NewShellParametersProtocol->Argc ; Count2++) { - ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2+1]; - } - ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = NULL; - for (Count2 = ShellInfoObject.NewShellParametersProtocol->Argc -1 ; Count2 > 0 ; Count2--) { - ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2-1]; - } - ShellInfoObject.NewShellParametersProtocol->Argv[0] = NULL; - ShellInfoObject.NewShellParametersProtocol->Argv[0] = StrnCatGrow(&ShellInfoObject.NewShellParametersProtocol->Argv[0], NULL, L"help", 0); - break; + if (StrStr(FirstParameter, L":") != NULL) { + ASSERT (CommandWithPath == NULL); + if (ShellIsFile(FirstParameter) == EFI_SUCCESS) { + CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, FirstParameter, 0); } } // - // command or file? + // Process a relative path and also check in the path environment variable // - if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) { - // - // Run the command (which was converted if it was an alias) - // - if (!EFI_ERROR(Status)) { - Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError); - ASSERT_EFI_ERROR(Status); + if (CommandWithPath == NULL) { + CommandWithPath = ShellFindFilePathEx(FirstParameter, mExecutableExtensions); + } - if (sizeof(EFI_STATUS) == sizeof(UINT64)) { - UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellStatus); - } else { - UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ShellStatus); - } - DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE);); - if (LastError) { - InternalEfiShellSetEnv(L"lasterror", LeString, TRUE); - } + // + // This should be impossible now. + // + ASSERT(CommandWithPath != NULL); + + // + // Make sure that path is not just a directory (or not found) + // + if (!EFI_ERROR(ShellIsDirectory(CommandWithPath))) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter); + SetLastError(SHELL_NOT_FOUND); + } + switch (Type) { + case SCRIPT_FILE_NAME: + Status = RunScriptFile (CommandWithPath); + break; + case EFI_APPLICATION: // - // Pass thru the exitcode from the app. + // Get the device path of the application image // - if (ShellCommandGetExit()) { - Status = ShellStatus; - } else if (ShellStatus != 0 && IsScriptOnlyCommand(ShellInfoObject.NewShellParametersProtocol->Argv[0])) { - Status = EFI_ABORTED; - } - } - } else { - // - // run an external file (or script) - // - if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[0], L":") != NULL) { - ASSERT (CommandWithPath == NULL); - if (ShellIsFile(ShellInfoObject.NewShellParametersProtocol->Argv[0]) == EFI_SUCCESS) { - CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0); + DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath); + if (DevPath == NULL){ + Status = EFI_OUT_OF_RESOURCES; + break; } - } - if (CommandWithPath == NULL) { - CommandWithPath = ShellFindFilePathEx(ShellInfoObject.NewShellParametersProtocol->Argv[0], mExecutableExtensions); - } - if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) { - ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]); - if (sizeof(EFI_STATUS) == sizeof(UINT64)) { - UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", EFI_NOT_FOUND); - } else { - UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", EFI_NOT_FOUND); - } - DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE);); - InternalEfiShellSetEnv(L"lasterror", LeString, TRUE); - } else { // - // Check if it's a NSH (script) file. + // Execute the device path // - TempLocation = CommandWithPath+StrLen(CommandWithPath)-4; - TempLocation2 = mScriptExtension; - if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) { - Status = RunScriptFile (CommandWithPath); - } else { - DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath); - ASSERT(DevPath != NULL); - Status = InternalShellExecuteDevicePath( - &gImageHandle, - DevPath, - CleanOriginal, - NULL, - &StatusCode - ); + Status = InternalShellExecuteDevicePath( + &gImageHandle, + DevPath, + CmdLine, + NULL, + &StatusCode + ); - // - // Update last error status. - // - if (sizeof(EFI_STATUS) == sizeof(UINT64)) { - UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", StatusCode); - } else { - UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", StatusCode); - } - DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE);); - InternalEfiShellSetEnv(L"lasterror", LeString, TRUE); - } - } - } + SHELL_FREE_NON_NULL(DevPath); + // + // Update last error status. + // + SetLastError(StatusCode); + break; + default: + // + // Do nothing. + // + break; + } + break; + default: // - // Print some error info. + // Do nothing. // - if (EFI_ERROR(Status)) { - ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status)); - } + break; + } - CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0); + SHELL_FREE_NON_NULL(CommandWithPath); - RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc); + return (Status); +} - RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo); - } - if (CommandName != NULL) { - if (ShellCommandGetCurrentScriptFile() != NULL && !IsScriptOnlyCommand(CommandName)) { - // - // if this is NOT a scipt only command return success so the script won't quit. - // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...) - // - Status = EFI_SUCCESS; - } - } +/** + Function to setup StdIn, StdErr, StdOut, and then run the command or file. + + @param[in] Type the type of operation being run. + @param[in] CmdLine the command line to run. + @param[in] FirstParameter the first parameter on the command line. + @param[in] ParamProtocol the shell parameters protocol pointer + + @retval EFI_SUCCESS The command was completed. + @retval EFI_ABORTED The command's operation was aborted. +**/ +EFI_STATUS +EFIAPI +SetupAndRunCommandOrFile( + IN SHELL_OPERATION_TYPES Type, + IN CHAR16 *CmdLine, + IN CHAR16 *FirstParameter, + IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol +) +{ + EFI_STATUS Status; + SHELL_FILE_HANDLE OriginalStdIn; + SHELL_FILE_HANDLE OriginalStdOut; + SHELL_FILE_HANDLE OriginalStdErr; + SYSTEM_TABLE_INFO OriginalSystemTableInfo; + + // + // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII + // + Status = UpdateStdInStdOutStdErr(ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo); + + // + // The StdIn, StdOut, and StdErr are set up. + // Now run the command, script, or application + // + if (!EFI_ERROR(Status)) { + Status = RunCommandOrFile(Type, CmdLine, FirstParameter, ParamProtocol); } - SHELL_FREE_NON_NULL(CommandName); - SHELL_FREE_NON_NULL(CommandWithPath); + // + // Now print errors + // + if (EFI_ERROR(Status)) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status)); + } + + // + // put back the original StdIn, StdOut, and StdErr + // + RestoreStdInStdOutStdErr(ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo); + + return (Status); +} + +/** + Function will process and run a command line. + + This will determine if the command line represents an internal shell + command or dispatch an external application. + + @param[in] CmdLine The command line to parse. + + @retval EFI_SUCCESS The command was completed. + @retval EFI_ABORTED The command's operation was aborted. +**/ +EFI_STATUS +EFIAPI +RunCommand( + IN CONST CHAR16 *CmdLine + ) +{ + EFI_STATUS Status; + CHAR16 *CleanOriginal; + CHAR16 *FirstParameter; + CHAR16 *TempWalker; + SHELL_OPERATION_TYPES Type; + + ASSERT(CmdLine != NULL); + if (StrLen(CmdLine) == 0) { + return (EFI_SUCCESS); + } + + Status = EFI_SUCCESS; + CleanOriginal = NULL; + + CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0); + if (CleanOriginal == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + TrimSpaces(&CleanOriginal); + + // + // Handle case that passed in command line is just 1 or more " " characters. + // + if (StrLen (CleanOriginal) == 0) { + SHELL_FREE_NON_NULL(CleanOriginal); + return (EFI_SUCCESS); + } + + Status = ProcessCommandLineToFinal(&CleanOriginal); + if (EFI_ERROR(Status)) { + SHELL_FREE_NON_NULL(CleanOriginal); + return (Status); + } + + // + // We dont do normal processing with a split command line (output from one command input to another) + // + if (ContainsSplit(CleanOriginal)) { + Status = ProcessNewSplitCommandLine(CleanOriginal); + SHELL_FREE_NON_NULL(CleanOriginal); + return (Status); + } + + // + // We need the first parameter information so we can determine the operation type + // + FirstParameter = AllocateZeroPool(StrSize(CleanOriginal)); + if (FirstParameter == NULL) { + SHELL_FREE_NON_NULL(CleanOriginal); + return (EFI_OUT_OF_RESOURCES); + } + TempWalker = CleanOriginal; + GetNextParameter(&TempWalker, &FirstParameter); + + // + // Depending on the first parameter we change the behavior + // + switch (Type = GetOperationType(FirstParameter)) { + case FILE_SYS_CHANGE: + Status = ChangeMappedDrive(CleanOriginal); + break; + case INTERNAL_COMMAND: + case SCRIPT_FILE_NAME: + case EFI_APPLICATION: + Status = SetupAndRunCommandOrFile(Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol); + break; + default: + // + // Whatever was typed, it was invalid. + // + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter); + SetLastError(SHELL_NOT_FOUND); + break; + } + SHELL_FREE_NON_NULL(CleanOriginal); + SHELL_FREE_NON_NULL(FirstParameter); return (Status); }