X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ShellPkg%2FApplication%2FShell%2FShell.c;h=ba3be3f6c344236e393bc9a9174ddfa1a06d5725;hb=d5b5440bf205a37c8ae0ac4b873cd976a9598326;hp=7a7bb1e736230b90bcbd37d0a23bd59010e6c8ff;hpb=e7831c9070bc832f7ef8eee58c99a66653a17d56;p=mirror_edk2.git
diff --git a/ShellPkg/Application/Shell/Shell.c b/ShellPkg/Application/Shell/Shell.c
index 7a7bb1e736..ba3be3f6c3 100644
--- a/ShellPkg/Application/Shell/Shell.c
+++ b/ShellPkg/Application/Shell/Shell.c
@@ -1,7 +1,7 @@
/** @file
This is THE shell (application)
- Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.
+ Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
Copyright (c) 2013, Hewlett-Packard Development Company, L.P.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
@@ -71,9 +71,9 @@ STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";
STATIC CONST CHAR16 mStartupScript[] = L"startup.nsh";
/**
- Cleans off leading and trailing spaces and tabs
+ Cleans off leading and trailing spaces and tabs.
- @param[in] String pointer to the string to trim them off
+ @param[in] String pointer to the string to trim them off.
**/
EFI_STATUS
EFIAPI
@@ -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;
}
@@ -107,8 +107,7 @@ TrimSpaces(
@retval A pointer to the | character in CmdLine or NULL if not present.
**/
-CONST
-CHAR16*
+CONST CHAR16*
EFIAPI
FindSplit(
IN CONST CHAR16 *CmdLine
@@ -144,7 +143,7 @@ ContainsSplit(
{
CONST CHAR16 *TempSpot;
TempSpot = FindSplit(CmdLine);
- return (TempSpot != NULL && *TempSpot != CHAR_NULL);
+ return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));
}
/**
@@ -245,6 +244,9 @@ UefiMain (
UINTN Size;
EFI_HANDLE ConInHandle;
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *OldConIn;
+ UINTN ExitDataSize;
+ CHAR16 *ExitData;
+ SHELL_STATUS ExitStatus;
if (PcdGet8(PcdShellSupportLevel) > 3) {
return (EFI_UNSUPPORTED);
@@ -298,6 +300,12 @@ UefiMain (
// install our console logger. This will keep a log of the output for back-browsing
//
Status = ConsoleLoggerInstall(ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);
+ if(EFI_ERROR (Status)) {
+ ExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));
+ } else {
+ ExitStatus = SHELL_SUCCESS;
+ }
+
if (!EFI_ERROR(Status)) {
//
// Enable the cursor to be visible
@@ -320,7 +328,8 @@ UefiMain (
///@todo Add our package into Framework HII
}
if (ShellInfoObject.HiiHandle == NULL) {
- return (EFI_NOT_STARTED);
+ Status = EFI_NOT_STARTED;
+ goto FreeResources;
}
}
@@ -350,7 +359,10 @@ UefiMain (
//
// Check the command line
//
- Status = ProcessCommandLine();
+ Status = ProcessCommandLine ();
+ if (EFI_ERROR (Status)) {
+ goto FreeResources;
+ }
//
// If shell support level is >= 1 create the mappings and paths
@@ -407,7 +419,7 @@ UefiMain (
// Display the mapping
//
if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {
- Status = RunCommand(L"map");
+ Status = RunCommand(L"map", NULL);
ASSERT_EFI_ERROR(Status);
}
@@ -473,7 +485,11 @@ UefiMain (
//
// process the startup script or launch the called app.
//
- Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
+ Status = DoStartupScript(
+ ShellInfoObject.ImageDevPath,
+ ShellInfoObject.FileDevPath,
+ &ExitStatus
+ );
}
if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
@@ -506,6 +522,7 @@ UefiMain (
//
Status = DoShellPrompt();
} while (!ShellCommandGetExit());
+ ExitStatus = (SHELL_STATUS) ShellCommandGetExitCode();
}
if (OldConIn != NULL && ConInHandle != NULL) {
CloseSimpleTextInOnFile (gST->ConIn);
@@ -515,6 +532,7 @@ UefiMain (
}
}
+FreeResources:
//
// uninstall protocols / free memory / etc...
//
@@ -576,10 +594,33 @@ UefiMain (
DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);
}
- if (ShellCommandGetExit()) {
- return ((EFI_STATUS)ShellCommandGetExitCode());
+ if (!EFI_ERROR (Status)) {
+ // If the command exited with an error, we pass this error out in the ExitData
+ // so that it can be retrieved by the EfiShellExecute function (which may
+ // start the shell with gBS->StartImage)
+ if (ExitStatus != SHELL_SUCCESS) {
+ // Allocate a buffer for exit data to pass to gBS->Exit().
+ // This buffer will contain the empty string immediately followed by
+ // the shell's exit status. (The empty string is required by the UEFI spec)
+ ExitDataSize = (sizeof (CHAR16) + sizeof (SHELL_STATUS));
+ ExitData = AllocatePool (ExitDataSize);
+ if (ExitData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ ExitData[0] = '\0';
+ // Use CopyMem to avoid alignment faults
+ CopyMem ((ExitData + 1), &ExitStatus, sizeof (ExitStatus));
+
+ gBS->Exit (ImageHandle, EFI_ABORTED, ExitDataSize, ExitData);
+
+ ASSERT (FALSE);
+ return EFI_SUCCESS;
+ } else {
+ return EFI_SUCCESS;
+ }
+ } else {
+ return Status;
}
- return (Status);
}
/**
@@ -727,20 +768,6 @@ GetDevicePathsForImageAndFile (
return (Status);
}
-STATIC CONST SHELL_PARAM_ITEM mShellParamList[] = {
- {L"-nostartup", TypeFlag},
- {L"-startup", TypeFlag},
- {L"-noconsoleout", TypeFlag},
- {L"-noconsolein", TypeFlag},
- {L"-nointerrupt", TypeFlag},
- {L"-nomap", TypeFlag},
- {L"-noversion", TypeFlag},
- {L"-startup", TypeFlag},
- {L"-delay", TypeValue},
- {L"-_exit", TypeFlag},
- {NULL, TypeMax}
- };
-
/**
Process all Uefi Shell 2.0 command line options.
@@ -774,95 +801,171 @@ ProcessCommandLine(
VOID
)
{
- EFI_STATUS Status;
- LIST_ENTRY *Package;
- UINTN Size;
- CONST CHAR16 *TempConst;
- UINTN Count;
- UINTN LoopVar;
- CHAR16 *ProblemParam;
- UINT64 Intermediate;
-
- Package = NULL;
- ProblemParam = NULL;
-
- Status = ShellCommandLineParse (mShellParamList, &Package, NULL, FALSE);
-
- Count = 1;
- Size = 0;
- TempConst = ShellCommandLineGetRawValue(Package, Count++);
- if (TempConst != NULL && StrLen(TempConst)) {
- ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(TempConst));
- if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
- return (EFI_OUT_OF_RESOURCES);
+ UINTN Size;
+ UINTN LoopVar;
+ CHAR16 *CurrentArg;
+ CHAR16 *DelayValueStr;
+ UINT64 DelayValue;
+ EFI_STATUS Status;
+ EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation;
+
+ // `file-name-options` will contain arguments to `file-name` that we don't
+ // know about. This would cause ShellCommandLineParse to error, so we parse
+ // arguments manually, ignoring those after the first thing that doesn't look
+ // like a shell option (which is assumed to be `file-name`).
+
+ Status = gBS->LocateProtocol (
+ &gEfiUnicodeCollationProtocolGuid,
+ NULL,
+ (VOID **) &UnicodeCollation
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // Set default options
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = FALSE;
+ ShellInfoObject.ShellInitSettings.Delay = 5;
+
+ // Start LoopVar at 1 to ignore Argv[0] which is the name of this binary
+ // (probably "Shell.efi")
+ for (LoopVar = 1 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
+ CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar];
+ if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-startup",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = TRUE;
}
- StrCpy(ShellInfoObject.ShellInitSettings.FileName, TempConst);
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;
- for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
- if (StrCmp(gEfiShellParametersProtocol->Argv[LoopVar], ShellInfoObject.ShellInitSettings.FileName)==0) {
- LoopVar++;
- //
- // We found the file... add the rest of the params...
- //
- for ( ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
- ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));
- StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
- &Size,
- L" ",
- 0);
- if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
- SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
- return (EFI_OUT_OF_RESOURCES);
- }
- StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
- &Size,
- gEfiShellParametersProtocol->Argv[LoopVar],
- 0);
- if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
- SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
- return (EFI_OUT_OF_RESOURCES);
- }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-nostartup",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-noconsoleout",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-noconsolein",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-nointerrupt",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-nomap",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-noversion",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-delay",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = TRUE;
+ // Check for optional delay value following "-delay"
+ DelayValueStr = gEfiShellParametersProtocol->Argv[LoopVar + 1];
+ if (DelayValueStr != NULL){
+ if (*DelayValueStr == L':') {
+ DelayValueStr++;
+ }
+ if (!EFI_ERROR(ShellConvertStringToUint64 (
+ DelayValueStr,
+ &DelayValue,
+ FALSE,
+ FALSE
+ ))) {
+ ShellInfoObject.ShellInitSettings.Delay = (UINTN)DelayValue;
+ LoopVar++;
+ }
+ }
+ } else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-_exit",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = TRUE;
+ } else if (StrnCmp (L"-", CurrentArg, 1) == 0) {
+ // Unrecognised option
+ ShellPrintHiiEx(-1, -1, NULL,
+ STRING_TOKEN (STR_GEN_PROBLEM),
+ ShellInfoObject.HiiHandle,
+ CurrentArg
+ );
+ return EFI_INVALID_PARAMETER;
+ } else {
+ ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(CurrentArg));
+ if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ //
+ // We found `file-name`.
+ //
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;
+
+ StrCpy (ShellInfoObject.ShellInitSettings.FileName, CurrentArg);
+ LoopVar++;
+
+ // Add `file-name-options`
+ for (Size = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
+ ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));
+ StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
+ &Size,
+ L" ",
+ 0);
+ if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
+ SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
+ &Size,
+ gEfiShellParametersProtocol->Argv[LoopVar],
+ 0);
+ if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
+ SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
+ return (EFI_OUT_OF_RESOURCES);
}
}
- }
- } else {
- ShellCommandLineFreeVarList(Package);
- Package = NULL;
- Status = ShellCommandLineParse (mShellParamList, &Package, &ProblemParam, FALSE);
- if (EFI_ERROR(Status)) {
- ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), ShellInfoObject.HiiHandle, ProblemParam);
- FreePool(ProblemParam);
- ShellCommandLineFreeVarList(Package);
- return (EFI_INVALID_PARAMETER);
}
}
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = ShellCommandLineGetFlag(Package, L"-startup");
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = ShellCommandLineGetFlag(Package, L"-nostartup");
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = ShellCommandLineGetFlag(Package, L"-noconsoleout");
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = ShellCommandLineGetFlag(Package, L"-noconsolein");
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = ShellCommandLineGetFlag(Package, L"-nointerrupt");
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = ShellCommandLineGetFlag(Package, L"-nomap");
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = ShellCommandLineGetFlag(Package, L"-noversion");
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = ShellCommandLineGetFlag(Package, L"-delay");
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = ShellCommandLineGetFlag(Package, L"-_exit");
-
- ShellInfoObject.ShellInitSettings.Delay = 5;
-
+ // "-nointerrupt" overrides "-delay"
if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
ShellInfoObject.ShellInitSettings.Delay = 0;
- } else if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay) {
- TempConst = ShellCommandLineGetValue(Package, L"-delay");
- if (TempConst != NULL && *TempConst == L':') {
- TempConst++;
- }
- if (TempConst != NULL && !EFI_ERROR(ShellConvertStringToUint64(TempConst, &Intermediate, FALSE, FALSE))) {
- ShellInfoObject.ShellInitSettings.Delay = (UINTN)Intermediate;
- }
}
- ShellCommandLineFreeVarList(Package);
- return (Status);
+ return EFI_SUCCESS;
}
/**
@@ -873,13 +976,16 @@ ProcessCommandLine(
@param ImagePath the path to the image for shell. first place to look for the startup script
@param FilePath the path to the file for shell. second place to look for the startup script.
+ @param[out] ExitStatus The exit code of the script. Ignored if NULL.
+
@retval EFI_SUCCESS the variable is initialized.
**/
EFI_STATUS
EFIAPI
DoStartupScript(
- EFI_DEVICE_PATH_PROTOCOL *ImagePath,
- EFI_DEVICE_PATH_PROTOCOL *FilePath
+ IN EFI_DEVICE_PATH_PROTOCOL *ImagePath,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ OUT SHELL_STATUS *ExitStatus
)
{
EFI_STATUS Status;
@@ -914,7 +1020,7 @@ DoStartupScript(
StrCat(FileStringPath, L" ");
StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);
}
- Status = RunCommand(FileStringPath);
+ Status = RunCommand(FileStringPath, ExitStatus);
FreePool(FileStringPath);
return (Status);
@@ -991,7 +1097,13 @@ DoStartupScript(
// If we got a file, run it
//
if (!EFI_ERROR(Status) && FileHandle != NULL) {
- Status = RunScriptFileHandle (FileHandle, mStartupScript);
+ Status = RunScriptFile (
+ mStartupScript,
+ FileHandle,
+ L"",
+ ShellInfoObject.NewShellParametersProtocol,
+ ExitStatus
+ );
ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
} else {
FileStringPath = ShellFindFilePath(mStartupScript);
@@ -1002,7 +1114,13 @@ DoStartupScript(
Status = EFI_SUCCESS;
ASSERT(FileHandle == NULL);
} else {
- Status = RunScriptFile(FileStringPath);
+ Status = RunScriptFile(
+ FileStringPath,
+ NULL,
+ L"",
+ ShellInfoObject.NewShellParametersProtocol,
+ ExitStatus
+ );
FreePool(FileStringPath);
}
}
@@ -1067,8 +1185,8 @@ DoShellPrompt (
//
if (!EFI_ERROR (Status)) {
CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
- Status = RunCommand(CmdLine);
- }
+ Status = RunCommand(CmdLine, NULL);
+ }
//
// Done with this command
@@ -1158,6 +1276,113 @@ ShellConvertAlias(
return (EFI_SUCCESS);
}
+/**
+ Parse for the next instance of one string within another string. Can optionally make sure that
+ the string was not escaped (^ character) per the shell specification.
+
+ @param[in] SourceString The string to search within
+ @param[in] FindString The string to look for
+ @param[in] CheckForEscapeCharacter TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances
+**/
+CHAR16*
+EFIAPI
+FindNextInstance(
+ IN CONST CHAR16 *SourceString,
+ IN CONST CHAR16 *FindString,
+ IN CONST BOOLEAN CheckForEscapeCharacter
+ )
+{
+ CHAR16 *Temp;
+ if (SourceString == NULL) {
+ return (NULL);
+ }
+ Temp = StrStr(SourceString, FindString);
+
+ //
+ // If nothing found, or we dont care about escape characters
+ //
+ if (Temp == NULL || !CheckForEscapeCharacter) {
+ return (Temp);
+ }
+
+ //
+ // If we found an escaped character, try again on the remainder of the string
+ //
+ if ((Temp > (SourceString)) && *(Temp-1) == L'^') {
+ return FindNextInstance(Temp+1, FindString, CheckForEscapeCharacter);
+ }
+
+ //
+ // we found the right character
+ //
+ return (Temp);
+}
+
+/**
+ This function will eliminate unreplaced (and therefore non-found) environment variables.
+
+ @param[in,out] CmdLine The command line to update.
+**/
+EFI_STATUS
+EFIAPI
+StripUnreplacedEnvironmentVariables(
+ IN OUT CHAR16 *CmdLine
+ )
+{
+ CHAR16 *FirstPercent;
+ CHAR16 *FirstQuote;
+ CHAR16 *SecondPercent;
+ CHAR16 *SecondQuote;
+ CHAR16 *CurrentLocator;
+
+ for (CurrentLocator = CmdLine ; CurrentLocator != NULL ; ) {
+ FirstQuote = FindNextInstance(CurrentLocator, L"\"", TRUE);
+ FirstPercent = FindNextInstance(CurrentLocator, L"%", TRUE);
+ SecondPercent = FirstPercent!=NULL?FindNextInstance(FirstPercent+1, L"%", TRUE):NULL;
+ if (FirstPercent == NULL || SecondPercent == NULL) {
+ //
+ // If we ever dont have 2 % we are done.
+ //
+ break;
+ }
+
+ if (FirstQuote < FirstPercent) {
+ SecondQuote = FirstQuote!= NULL?FindNextInstance(FirstQuote+1, L"\"", TRUE):NULL;
+ //
+ // Quote is first found
+ //
+
+ if (SecondQuote < FirstPercent) {
+ //
+ // restart after the pair of "
+ //
+ CurrentLocator = SecondQuote + 1;
+ } else /* FirstPercent < SecondQuote */{
+ //
+ // Restart on the first percent
+ //
+ CurrentLocator = FirstPercent;
+ }
+ continue;
+ }
+ ASSERT(FirstPercent < FirstQuote);
+ if (SecondPercent < FirstQuote) {
+ //
+ // We need to remove from FirstPercent to SecondPercent
+ //
+ CopyMem(FirstPercent, SecondPercent + 1, StrSize(SecondPercent + 1));
+
+ //
+ // dont need to update the locator. both % characters are gone.
+ //
+ continue;
+ }
+ ASSERT(FirstQuote < SecondPercent);
+ CurrentLocator = FirstQuote;
+ }
+ return (EFI_SUCCESS);
+}
+
/**
Function allocates a new command line and replaces all instances of environment
variable names that are correctly preset to their values.
@@ -1180,7 +1405,6 @@ ShellConvertVariables (
CHAR16 *NewCommandLine1;
CHAR16 *NewCommandLine2;
CHAR16 *Temp;
- CHAR16 *Temp2;
UINTN ItemSize;
CHAR16 *ItemTemp;
SCRIPT_FILE *CurrentScriptFile;
@@ -1273,39 +1497,7 @@ ShellConvertVariables (
//
// Remove non-existant environment variables in scripts only
//
- for (Temp = NewCommandLine1 ; Temp != NULL ; ) {
- Temp = StrStr(Temp, L"%");
- if (Temp == NULL) {
- break;
- }
- while (*(Temp - 1) == L'^') {
- Temp = StrStr(Temp + 1, L"%");
- if (Temp == NULL) {
- break;
- }
- }
- if (Temp == NULL) {
- break;
- }
-
- Temp2 = StrStr(Temp + 1, L"%");
- if (Temp2 == NULL) {
- break;
- }
- while (*(Temp2 - 1) == L'^') {
- Temp2 = StrStr(Temp2 + 1, L"%");
- if (Temp2 == NULL) {
- break;
- }
- }
- if (Temp2 == NULL) {
- break;
- }
-
- Temp2++;
- CopyMem(Temp, Temp2, StrSize(Temp2));
- }
-
+ StripUnreplacedEnvironmentVariables(NewCommandLine1);
}
//
@@ -1327,6 +1519,9 @@ ShellConvertVariables (
@param[in] StdIn The pointer to the Standard input.
@param[in] StdOut The pointer to the Standard output.
+ @param[out] ExitStatus The exit code of the last command in the pipeline.
+ Ignored if NULL.
+
@retval EFI_SUCCESS The split command is executed successfully.
@retval other Some error occurs when executing the split command.
**/
@@ -1335,7 +1530,8 @@ EFIAPI
RunSplitCommand(
IN CONST CHAR16 *CmdLine,
IN SHELL_FILE_HANDLE *StdIn,
- IN SHELL_FILE_HANDLE *StdOut
+ IN SHELL_FILE_HANDLE *StdOut,
+ OUT SHELL_STATUS *ExitStatus
)
{
EFI_STATUS Status;
@@ -1389,7 +1585,7 @@ RunSplitCommand(
ASSERT(Split->SplitStdOut != NULL);
InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);
- Status = RunCommand(OurCommandLine);
+ Status = RunCommand(OurCommandLine, NULL);
//
// move the output from the first to the in to the second.
@@ -1404,7 +1600,7 @@ RunSplitCommand(
ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);
if (!EFI_ERROR(Status)) {
- Status = RunCommand(NextCommandLine);
+ Status = RunCommand(NextCommandLine, ExitStatus);
}
//
@@ -1432,13 +1628,12 @@ RunSplitCommand(
/**
Take the original command line, substitute any variables, free
- the original string, return the modified copy
+ the original string, return the modified copy.
- @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
+ @param[in] CmdLine pointer to the command line to update.
- @retval EFI_SUCCESS the function was successful
- @retval EFI_OUT_OF_RESOURCES a memory allocation failed
+ @retval EFI_SUCCESS the function was successful.
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
**/
EFI_STATUS
EFIAPI
@@ -1458,13 +1653,12 @@ ShellSubstituteVariables(
/**
Take the original command line, substitute any alias in the first group of space delimited characters, free
- the original string, return the modified copy
+ 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] CmdLine pointer to the command line to update.
- @retval EFI_SUCCESS the function was successful
- @retval EFI_OUT_OF_RESOURCES a memory allocation failed
+ @retval EFI_SUCCESS the function was successful.
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
**/
EFI_STATUS
EFIAPI
@@ -1530,13 +1724,13 @@ 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
+ @param[in] CmdName 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)
+ @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
@@ -1553,17 +1747,19 @@ GetOperationType(
// test for an internal command.
//
if (ShellCommandIsCommandOnList(CmdName)) {
- return (INTERNAL_COMMAND);
+ return (Internal_Command);
}
//
- // Test for file system change request. anything ending with : and cant have spaces.
+ // Test for file system change request. anything ending with first : and cant have spaces.
//
if (CmdName[(StrLen(CmdName)-1)] == L':') {
- if (StrStr(CmdName, L" ") != NULL) {
- return (UNKNOWN_INVALID);
+ if ( StrStr(CmdName, L" ") != NULL
+ || StrLen(StrStr(CmdName, L":")) > 1
+ ) {
+ return (Unknown_Invalid);
}
- return (FILE_SYS_CHANGE);
+ return (File_Sys_Change);
}
//
@@ -1578,7 +1774,7 @@ GetOperationType(
TempLocation2 = mScriptExtension;
if (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0) {
SHELL_FREE_NON_NULL(FileWithPath);
- return (SCRIPT_FILE_NAME);
+ return (Script_File_Name);
}
}
@@ -1586,20 +1782,121 @@ GetOperationType(
// Was a file, but not a script. we treat this as an application.
//
SHELL_FREE_NON_NULL(FileWithPath);
- return (EFI_APPLICATION);
+ return (Efi_Application);
}
SHELL_FREE_NON_NULL(FileWithPath);
//
// No clue what this is... return invalid flag...
//
- return (UNKNOWN_INVALID);
+ return (Unknown_Invalid);
+}
+
+/**
+ Determine if the first item in a command line is valid.
+
+ @param[in] CmdLine The command line to parse.
+
+ @retval EFI_SUCCESS The item is valid.
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
+ @retval EFI_NOT_FOUND The operation type is unknown or invalid.
+**/
+EFI_STATUS
+EFIAPI
+IsValidSplit(
+ IN CONST CHAR16 *CmdLine
+ )
+{
+ CHAR16 *Temp;
+ CHAR16 *FirstParameter;
+ CHAR16 *TempWalker;
+ EFI_STATUS Status;
+
+ 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 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
+VerifySplit(
+ IN CONST CHAR16 *CmdLine
+ )
+{
+ CONST CHAR16 *TempSpot;
+ EFI_STATUS Status;
+
+ //
+ // 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);
+ }
+
+ //
+ // recurse to verify the next item
+ //
+ TempSpot = FindSplit(CmdLine)+1;
+ return (VerifySplit(TempSpot));
}
/**
Process a split based operation.
- @param[in] CmdLine pointer to the command line to process
+ @param[in] CmdLine Pointer to the command line to process
+ @param[out] ExitStatus The exit status of the command. Ignored if NULL.
+ Invalid if this function returns an error.
@retval EFI_SUCCESS The operation was successful
@return an error occured.
@@ -1607,12 +1904,18 @@ GetOperationType(
EFI_STATUS
EFIAPI
ProcessNewSplitCommandLine(
- IN CONST CHAR16 *CmdLine
+ IN CONST CHAR16 *CmdLine,
+ OUT SHELL_STATUS *ExitStatus
)
{
SPLIT_LIST *Split;
EFI_STATUS Status;
+ Status = VerifySplit(CmdLine);
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+
Split = NULL;
//
@@ -1623,9 +1926,14 @@ ProcessNewSplitCommandLine(
}
if (Split == NULL) {
- Status = RunSplitCommand(CmdLine, NULL, NULL);
+ Status = RunSplitCommand(CmdLine, NULL, NULL, ExitStatus);
} else {
- Status = RunSplitCommand(CmdLine, Split->SplitStdIn, Split->SplitStdOut);
+ Status = RunSplitCommand(
+ CmdLine,
+ Split->SplitStdIn,
+ Split->SplitStdOut,
+ ExitStatus
+ );
}
if (EFI_ERROR(Status)) {
ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);
@@ -1634,11 +1942,11 @@ ProcessNewSplitCommandLine(
}
/**
- Handle a request to change the current file system
+ Handle a request to change the current file system.
- @param[in] CmdLine The passed in command line
+ @param[in] CmdLine The passed in command line.
- @retval EFI_SUCCESS The operation was successful
+ @retval EFI_SUCCESS The operation was successful.
**/
EFI_STATUS
EFIAPI
@@ -1674,50 +1982,424 @@ ChangeMappedDrive(
if found, will add "help" as argv[0], and move the rest later.
- @param[in,out] Argc The pointer to argc to update
- @param[in,out] Argv The pointer to argv to update (this is a pointer to an array of string pointers)
+ @param[in,out] CmdLine pointer to the command line to update
**/
EFI_STATUS
EFIAPI
-DoHelpUpdateArgcArgv(
- IN OUT UINTN *Argc,
- IN OUT CHAR16 ***Argv
+DoHelpUpdate(
+ IN OUT CHAR16 **CmdLine
)
{
- UINTN Count;
- UINTN Count2;
+ 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;
+ }
+ }
+
+ 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);
+ }
+
+ TrimSpaces(CmdLine);
+
+ Status = ShellSubstituteVariables(CmdLine);
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+
+ TrimSpaces(CmdLine);
+
//
- // Check each parameter
+ // update for help parsing
//
- for (Count = 0 ; Count < (*Argc) ; Count++) {
+ if (StrStr(*CmdLine, L"?") != NULL) {
//
- // if it's "-?" or if the first parameter is "?"
+ // This may do nothing if the ? does not indicate help.
+ // Save all the details for in the API below.
//
- if (StrStr((*Argv)[Count], L"-?") == (*Argv)[Count]
- || ((*Argv)[0][0] == L'?' && (*Argv)[0][1] == CHAR_NULL)
- ) {
+ Status = DoHelpUpdate(CmdLine);
+ }
+
+ 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
+
+ @param[out] ExitStatus The exit code of the command. Ignored if NULL.
+
+ @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,
+ OUT SHELL_STATUS *ExitStatus OPTIONAL
+)
+{
+ 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)) {
+ //
+ // Run the internal command.
+ //
+ Status = ShellCommandRunCommandHandler(FirstParameter, &CommandReturnedStatus, &LastError);
+
+ if (!EFI_ERROR(Status)) {
//
- // 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
+ // Update last error status.
+ // some commands do not update last error.
//
- FreePool((*Argv)[Count]);
- (*Argv)[Count] = NULL;
- for (Count2 = Count ; (Count2 + 1) < (*Argc) ; Count2++) {
- (*Argv)[Count2] = (*Argv)[Count2+1];
+ if (LastError) {
+ SetLastError(CommandReturnedStatus);
}
- (*Argv)[Count2] = NULL;
- for (Count2 = (*Argc) -1 ; Count2 > 0 ; Count2--) {
- (*Argv)[Count2] = (*Argv)[Count2-1];
+ if (ExitStatus != NULL) {
+ *ExitStatus = CommandReturnedStatus;
}
- (*Argv)[0] = NULL;
- (*Argv)[0] = StrnCatGrow(&(*Argv)[0], NULL, L"help", 0);
- if ((*Argv)[0] == NULL) {
- return (EFI_OUT_OF_RESOURCES);
+
+ //
+ // 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;
}
- break;
}
}
- return (EFI_SUCCESS);
+
+ //
+ // 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);
+}
+
+/**
+ 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
+
+ @param[out] ExitStatus The exit code of the command or file.
+ Ignored if NULL.
+
+ @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,
+ OUT SHELL_STATUS *ExitStatus
+)
+{
+ EFI_STATUS Status;
+ CHAR16 *CommandWithPath;
+ EFI_DEVICE_PATH_PROTOCOL *DevPath;
+ SHELL_STATUS CalleeExitStatus;
+
+ Status = EFI_SUCCESS;
+ CommandWithPath = NULL;
+ DevPath = NULL;
+ CalleeExitStatus = SHELL_INVALID_PARAMETER;
+
+ switch (Type) {
+ case Internal_Command:
+ Status = RunInternalCommand(
+ CmdLine,
+ FirstParameter,
+ ParamProtocol,
+ &CalleeExitStatus
+ );
+ break;
+ case Script_File_Name:
+ case Efi_Application:
+ //
+ // Process a fully qualified path
+ //
+ if (StrStr(FirstParameter, L":") != NULL) {
+ ASSERT (CommandWithPath == NULL);
+ if (ShellIsFile(FirstParameter) == EFI_SUCCESS) {
+ CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, FirstParameter, 0);
+ }
+ }
+
+ //
+ // Process a relative path and also check in the path environment variable
+ //
+ if (CommandWithPath == NULL) {
+ CommandWithPath = ShellFindFilePathEx(FirstParameter, mExecutableExtensions);
+ }
+
+ //
+ // 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,
+ NULL,
+ CmdLine,
+ ParamProtocol,
+ &CalleeExitStatus
+ );
+ break;
+ case Efi_Application:
+ //
+ // Get the device path of the application image
+ //
+ DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);
+ if (DevPath == NULL){
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+
+ //
+ // Execute the device path
+ //
+ Status = InternalShellExecuteDevicePath(
+ &gImageHandle,
+ DevPath,
+ CmdLine,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ SHELL_FREE_NON_NULL(DevPath);
+
+ if(EFI_ERROR (Status)) {
+ CalleeExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));
+ } else {
+ CalleeExitStatus = SHELL_SUCCESS;
+ }
+
+ //
+ // Update last error status.
+ //
+ // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
+ SetLastError(CalleeExitStatus);
+ break;
+ default:
+ //
+ // Do nothing.
+ //
+ break;
+ }
+ break;
+ default:
+ //
+ // Do nothing.
+ //
+ break;
+ }
+
+ SHELL_FREE_NON_NULL(CommandWithPath);
+
+ if (ExitStatus != NULL) {
+ *ExitStatus = CalleeExitStatus;
+ }
+
+ return (Status);
+}
+
+/**
+ 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
+
+ @param[out] ExitStatus The exit code of the command or file.
+ Ignored if NULL.
+
+ @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,
+ OUT SHELL_STATUS *ExitStatus
+)
+{
+ 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,
+ ExitStatus
+ );
+ }
+
+ //
+ // 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);
}
/**
@@ -1727,6 +2409,7 @@ DoHelpUpdateArgcArgv(
command or dispatch an external application.
@param[in] CmdLine The command line to parse.
+ @param[out] ExitStatus The exit code of the command. Ignored if NULL.
@retval EFI_SUCCESS The command was completed.
@retval EFI_ABORTED The command's operation was aborted.
@@ -1734,35 +2417,21 @@ DoHelpUpdateArgcArgv(
EFI_STATUS
EFIAPI
RunCommand(
- IN CONST CHAR16 *CmdLine
+ IN CONST CHAR16 *CmdLine,
+ OUT SHELL_STATUS *ExitStatus
)
{
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;
CHAR16 *CleanOriginal;
+ CHAR16 *FirstParameter;
+ CHAR16 *TempWalker;
+ SHELL_OPERATION_TYPES Type;
ASSERT(CmdLine != NULL);
if (StrLen(CmdLine) == 0) {
return (EFI_SUCCESS);
}
- CommandName = NULL;
- CommandWithPath = NULL;
- DevPath = NULL;
Status = EFI_SUCCESS;
CleanOriginal = NULL;
@@ -1777,180 +2446,65 @@ RunCommand(
// Handle case that passed in command line is just 1 or more " " characters.
//
if (StrLen (CleanOriginal) == 0) {
- if (CleanOriginal != NULL) {
- FreePool(CleanOriginal);
- CleanOriginal = NULL;
- }
+ SHELL_FREE_NON_NULL(CleanOriginal);
return (EFI_SUCCESS);
}
- Status = ShellSubstituteAliases(&CleanOriginal);
+ Status = ProcessCommandLineToFinal(&CleanOriginal);
if (EFI_ERROR(Status)) {
+ SHELL_FREE_NON_NULL(CleanOriginal);
return (Status);
}
- Status = ShellSubstituteVariables(&CleanOriginal);
- if (EFI_ERROR(Status)) {
- return (Status);
- }
-
- TrimSpaces(&CleanOriginal);
-
//
// We dont do normal processing with a split command line (output from one command input to another)
//
if (ContainsSplit(CleanOriginal)) {
- Status = ProcessNewSplitCommandLine(CleanOriginal);
- } else {
- //
- // If this is a mapped drive change handle that...
- //
- if (CleanOriginal[(StrLen(CleanOriginal)-1)] == L':' && StrStr(CleanOriginal, L" ") == NULL) {
- Status = ChangeMappedDrive(CleanOriginal);
- SHELL_FREE_NON_NULL(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...
-
-
-
- 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);
-
- //
- // get the argc and argv updated for internal commands
- //
- Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, CleanOriginal, &Argv, &Argc);
- ASSERT_EFI_ERROR(Status);
-
- if (StrStr(CleanOriginal, L"?") != NULL) {
- Status = DoHelpUpdateArgcArgv(
- &ShellInfoObject.NewShellParametersProtocol->Argc,
- &ShellInfoObject.NewShellParametersProtocol->Argv);
- }
-
- //
- // command or file?
- //
- 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 (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);
- }
- //
- // Pass thru the exitcode from the app.
- //
- 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);
- }
- }
- 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.
- //
- 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 = ProcessNewSplitCommandLine(CleanOriginal, ExitStatus);
+ SHELL_FREE_NON_NULL(CleanOriginal);
+ return (Status);
+ }
- //
- // 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);
- }
- }
- }
+ //
+ // 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,
+ ExitStatus
+ );
+ break;
+ default:
//
- // Print some error info.
+ // Whatever was typed, it was invalid.
//
- if (EFI_ERROR(Status)) {
- ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));
- }
-
- CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);
-
- RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);
-
- 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;
- }
- }
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
+ SetLastError(SHELL_NOT_FOUND);
+ break;
}
-
- SHELL_FREE_NON_NULL(CommandName);
- SHELL_FREE_NON_NULL(CommandWithPath);
+
SHELL_FREE_NON_NULL(CleanOriginal);
+ SHELL_FREE_NON_NULL(FirstParameter);
return (Status);
}
@@ -1993,13 +2547,16 @@ IsValidCommandName(
@param[in] Handle The handle to the already opened file.
@param[in] Name The name of the script file.
+ @param[out] ExitStatus The exit code of the script. Ignored if NULL.
+
@retval EFI_SUCCESS the script completed sucessfully
**/
EFI_STATUS
EFIAPI
RunScriptFileHandle (
- IN SHELL_FILE_HANDLE Handle,
- IN CONST CHAR16 *Name
+ IN SHELL_FILE_HANDLE Handle,
+ IN CONST CHAR16 *Name,
+ OUT SHELL_STATUS *ExitStatus
)
{
EFI_STATUS Status;
@@ -2015,8 +2572,11 @@ RunScriptFileHandle (
CONST CHAR16 *CurDir;
UINTN LineCount;
CHAR16 LeString[50];
+ SHELL_STATUS CalleeExitStatus;
ASSERT(!ShellCommandGetScriptExit());
+
+ CalleeExitStatus = SHELL_SUCCESS;
PreScriptEchoState = ShellCommandGetEchoState();
@@ -2067,11 +2627,13 @@ RunScriptFileHandle (
while(!ShellFileHandleEof(Handle)) {
CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);
LineCount++;
- if (CommandLine == NULL || StrLen(CommandLine) == 0) {
+ if (CommandLine == NULL || StrLen(CommandLine) == 0 || CommandLine[0] == '#') {
+ SHELL_FREE_NON_NULL(CommandLine);
continue;
}
NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));
if (NewScriptFile->CurrentCommand == NULL) {
+ SHELL_FREE_NON_NULL(CommandLine);
DeleteScriptFileStruct(NewScriptFile);
return (EFI_OUT_OF_RESOURCES);
}
@@ -2198,7 +2760,7 @@ RunScriptFileHandle (
//
PreCommandEchoState = ShellCommandGetEchoState();
ShellCommandSetEchoState(FALSE);
- Status = RunCommand(CommandLine3+1);
+ Status = RunCommand(CommandLine3+1, NULL);
//
// If command was "@echo -off" or "@echo -on" then don't restore echo state
@@ -2220,7 +2782,7 @@ RunScriptFileHandle (
}
ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);
}
- Status = RunCommand(CommandLine3);
+ Status = RunCommand(CommandLine3, NULL);
}
}
@@ -2228,7 +2790,8 @@ RunScriptFileHandle (
//
// ShellCommandGetExitCode() always returns a UINT64
//
- UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellCommandGetExitCode());
+ CalleeExitStatus = (SHELL_STATUS) ShellCommandGetExitCode();
+ UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", CalleeExitStatus);
DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););
InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);
@@ -2240,9 +2803,11 @@ RunScriptFileHandle (
break;
}
if (EFI_ERROR(Status)) {
+ CalleeExitStatus = (SHELL_STATUS) Status;
break;
}
if (ShellCommandGetExit()) {
+ CalleeExitStatus = (SHELL_STATUS) ShellCommandGetExitCode();
break;
}
}
@@ -2274,6 +2839,11 @@ RunScriptFileHandle (
if (ShellCommandGetCurrentScriptFile()==NULL) {
ShellCommandSetEchoState(PreScriptEchoState);
}
+
+ if (ExitStatus != NULL) {
+ *ExitStatus = CalleeExitStatus;
+ }
+
return (EFI_SUCCESS);
}
@@ -2281,30 +2851,65 @@ RunScriptFileHandle (
Function to process a NSH script file.
@param[in] ScriptPath Pointer to the script file name (including file system path).
+ @param[in] Handle the handle of the script file already opened.
+ @param[in] CmdLine the command line to run.
+ @param[in] ParamProtocol the shell parameters protocol pointer
+
+ @param[out] ExitStatus The exit code of the script. Ignored if NULL.
@retval EFI_SUCCESS the script completed sucessfully
**/
EFI_STATUS
EFIAPI
RunScriptFile (
- IN CONST CHAR16 *ScriptPath
+ IN CONST CHAR16 *ScriptPath,
+ IN SHELL_FILE_HANDLE Handle OPTIONAL,
+ IN CONST CHAR16 *CmdLine,
+ IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
+ OUT SHELL_STATUS *ExitStatus
)
{
EFI_STATUS Status;
SHELL_FILE_HANDLE FileHandle;
+ UINTN Argc;
+ CHAR16 **Argv;
if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {
return (EFI_INVALID_PARAMETER);
}
- Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);
- if (EFI_ERROR(Status)) {
- return (Status);
- }
+ //
+ // get the argc and argv updated for scripts
+ //
+ Status = UpdateArgcArgv(ParamProtocol, CmdLine, &Argv, &Argc);
+ if (!EFI_ERROR(Status)) {
- Status = RunScriptFileHandle(FileHandle, ScriptPath);
+ if (Handle == NULL) {
+ //
+ // open the file
+ //
+ Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);
+ if (!EFI_ERROR(Status)) {
+ //
+ // run it
+ //
+ Status = RunScriptFileHandle(FileHandle, ScriptPath, ExitStatus);
- ShellCloseFile(&FileHandle);
+ //
+ // now close the file
+ //
+ ShellCloseFile(&FileHandle);
+ }
+ } else {
+ Status = RunScriptFileHandle(Handle, ScriptPath, ExitStatus);
+ }
+ }
+
+ //
+ // 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);
return (Status);
}