/** @file\r
This is THE shell (application)\r
\r
- Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>\r
+ Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>\r
Copyright (c) 2013, Hewlett-Packard Development Company, L.P.\r
This program and the accompanying materials\r
are licensed and made available under the terms and conditions of the BSD License\r
STATIC CONST CHAR16 mStartupScript[] = L"startup.nsh";\r
\r
/**\r
- Cleans off leading and trailing spaces and tabs\r
+ Cleans off leading and trailing spaces and tabs.\r
\r
- @param[in] String pointer to the string to trim them off\r
+ @param[in] String pointer to the string to trim them off.\r
**/\r
EFI_STATUS\r
EFIAPI\r
}\r
\r
//\r
- // Remove any spaces at the end of the (*String).\r
+ // Remove any spaces and tabs at the end of the (*String).\r
//\r
- while ((*String)[StrLen((*String))-1] == L' ') {\r
+ while ((StrLen (*String) > 0) && (((*String)[StrLen((*String))-1] == L' ') || ((*String)[StrLen((*String))-1] == L'\t'))) {\r
(*String)[StrLen((*String))-1] = CHAR_NULL;\r
}\r
\r
\r
@retval A pointer to the | character in CmdLine or NULL if not present.\r
**/\r
-CONST\r
-CHAR16*\r
+CONST CHAR16*\r
EFIAPI\r
FindSplit(\r
IN CONST CHAR16 *CmdLine\r
UINTN Size;\r
EFI_HANDLE ConInHandle;\r
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *OldConIn;\r
+ UINTN ExitDataSize;\r
+ CHAR16 *ExitData;\r
+ SHELL_STATUS ExitStatus;\r
\r
if (PcdGet8(PcdShellSupportLevel) > 3) {\r
return (EFI_UNSUPPORTED);\r
// install our console logger. This will keep a log of the output for back-browsing\r
//\r
Status = ConsoleLoggerInstall(ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);\r
+ if(EFI_ERROR (Status)) {\r
+ ExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));\r
+ } else {\r
+ ExitStatus = SHELL_SUCCESS;\r
+ }\r
+ \r
if (!EFI_ERROR(Status)) {\r
//\r
// Enable the cursor to be visible\r
///@todo Add our package into Framework HII\r
}\r
if (ShellInfoObject.HiiHandle == NULL) {\r
- return (EFI_NOT_STARTED);\r
+ Status = EFI_NOT_STARTED;\r
+ goto FreeResources;\r
}\r
}\r
\r
//\r
// Check the command line\r
//\r
- Status = ProcessCommandLine();\r
+ Status = ProcessCommandLine ();\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeResources;\r
+ }\r
\r
//\r
// If shell support level is >= 1 create the mappings and paths\r
// Display the mapping\r
//\r
if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {\r
- Status = RunCommand(L"map");\r
+ Status = RunCommand(L"map", NULL);\r
ASSERT_EFI_ERROR(Status);\r
}\r
\r
//\r
// process the startup script or launch the called app.\r
//\r
- Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);\r
+ Status = DoStartupScript(\r
+ ShellInfoObject.ImageDevPath,\r
+ ShellInfoObject.FileDevPath,\r
+ &ExitStatus\r
+ );\r
}\r
\r
if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
//\r
Status = DoShellPrompt();\r
} while (!ShellCommandGetExit());\r
+ ExitStatus = (SHELL_STATUS) ShellCommandGetExitCode();\r
}\r
if (OldConIn != NULL && ConInHandle != NULL) {\r
CloseSimpleTextInOnFile (gST->ConIn);\r
}\r
}\r
\r
+FreeResources:\r
//\r
// uninstall protocols / free memory / etc...\r
//\r
DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);\r
}\r
\r
- if (ShellCommandGetExit()) {\r
- return ((EFI_STATUS)ShellCommandGetExitCode());\r
+ if (!EFI_ERROR (Status)) {\r
+ // If the command exited with an error, we pass this error out in the ExitData\r
+ // so that it can be retrieved by the EfiShellExecute function (which may\r
+ // start the shell with gBS->StartImage)\r
+ if (ExitStatus != SHELL_SUCCESS) {\r
+ // Allocate a buffer for exit data to pass to gBS->Exit().\r
+ // This buffer will contain the empty string immediately followed by\r
+ // the shell's exit status. (The empty string is required by the UEFI spec)\r
+ ExitDataSize = (sizeof (CHAR16) + sizeof (SHELL_STATUS));\r
+ ExitData = AllocatePool (ExitDataSize);\r
+ if (ExitData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ ExitData[0] = '\0';\r
+ // Use CopyMem to avoid alignment faults\r
+ CopyMem ((ExitData + 1), &ExitStatus, sizeof (ExitStatus));\r
+\r
+ gBS->Exit (ImageHandle, EFI_ABORTED, ExitDataSize, ExitData);\r
+\r
+ ASSERT (FALSE);\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ return EFI_SUCCESS;\r
+ }\r
+ } else {\r
+ return Status;\r
}\r
- return (Status);\r
}\r
\r
/**\r
return (Status);\r
}\r
\r
-STATIC CONST SHELL_PARAM_ITEM mShellParamList[] = {\r
- {L"-nostartup", TypeFlag},\r
- {L"-startup", TypeFlag},\r
- {L"-noconsoleout", TypeFlag},\r
- {L"-noconsolein", TypeFlag},\r
- {L"-nointerrupt", TypeFlag},\r
- {L"-nomap", TypeFlag},\r
- {L"-noversion", TypeFlag},\r
- {L"-startup", TypeFlag},\r
- {L"-delay", TypeValue},\r
- {L"-_exit", TypeFlag},\r
- {NULL, TypeMax}\r
- };\r
-\r
/**\r
Process all Uefi Shell 2.0 command line options.\r
\r
VOID\r
)\r
{\r
- EFI_STATUS Status;\r
- LIST_ENTRY *Package;\r
- UINTN Size;\r
- CONST CHAR16 *TempConst;\r
- UINTN Count;\r
- UINTN LoopVar;\r
- CHAR16 *ProblemParam;\r
- UINT64 Intermediate;\r
-\r
- Package = NULL;\r
- ProblemParam = NULL;\r
- \r
- Status = ShellCommandLineParse (mShellParamList, &Package, NULL, FALSE);\r
-\r
- Count = 1;\r
- Size = 0;\r
- TempConst = ShellCommandLineGetRawValue(Package, Count++);\r
- if (TempConst != NULL && StrLen(TempConst)) {\r
- ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(TempConst));\r
- if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
- return (EFI_OUT_OF_RESOURCES);\r
+ UINTN Size;\r
+ UINTN LoopVar;\r
+ CHAR16 *CurrentArg;\r
+ CHAR16 *DelayValueStr;\r
+ UINT64 DelayValue;\r
+ EFI_STATUS Status;\r
+ EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation;\r
+\r
+ // `file-name-options` will contain arguments to `file-name` that we don't\r
+ // know about. This would cause ShellCommandLineParse to error, so we parse\r
+ // arguments manually, ignoring those after the first thing that doesn't look\r
+ // like a shell option (which is assumed to be `file-name`).\r
+\r
+ Status = gBS->LocateProtocol (\r
+ &gEfiUnicodeCollationProtocolGuid,\r
+ NULL,\r
+ (VOID **) &UnicodeCollation\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ // Set default options\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = FALSE;\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = FALSE;\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = FALSE;\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = FALSE;\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = FALSE;\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = FALSE;\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = FALSE;\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = FALSE;\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = FALSE;\r
+ ShellInfoObject.ShellInitSettings.Delay = 5;\r
+\r
+ // Start LoopVar at 1 to ignore Argv[0] which is the name of this binary\r
+ // (probably "Shell.efi")\r
+ for (LoopVar = 1 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
+ CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar];\r
+ if (UnicodeCollation->StriColl (\r
+ UnicodeCollation,\r
+ L"-startup",\r
+ CurrentArg\r
+ ) == 0) {\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = TRUE;\r
}\r
- StrCpy(ShellInfoObject.ShellInitSettings.FileName, TempConst);\r
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;\r
- for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
- if (StrCmp(gEfiShellParametersProtocol->Argv[LoopVar], ShellInfoObject.ShellInitSettings.FileName)==0) {\r
- LoopVar++;\r
- //\r
- // We found the file... add the rest of the params...\r
- //\r
- for ( ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
- ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));\r
- StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
- &Size,\r
- L" ",\r
- 0);\r
- if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
- SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
- return (EFI_OUT_OF_RESOURCES);\r
- }\r
- StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
- &Size,\r
- gEfiShellParametersProtocol->Argv[LoopVar],\r
- 0);\r
- if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
- SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
- return (EFI_OUT_OF_RESOURCES);\r
- }\r
+ else if (UnicodeCollation->StriColl (\r
+ UnicodeCollation,\r
+ L"-nostartup",\r
+ CurrentArg\r
+ ) == 0) {\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = TRUE;\r
+ }\r
+ else if (UnicodeCollation->StriColl (\r
+ UnicodeCollation,\r
+ L"-noconsoleout",\r
+ CurrentArg\r
+ ) == 0) {\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = TRUE;\r
+ }\r
+ else if (UnicodeCollation->StriColl (\r
+ UnicodeCollation,\r
+ L"-noconsolein",\r
+ CurrentArg\r
+ ) == 0) {\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = TRUE;\r
+ }\r
+ else if (UnicodeCollation->StriColl (\r
+ UnicodeCollation,\r
+ L"-nointerrupt",\r
+ CurrentArg\r
+ ) == 0) {\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = TRUE;\r
+ }\r
+ else if (UnicodeCollation->StriColl (\r
+ UnicodeCollation,\r
+ L"-nomap",\r
+ CurrentArg\r
+ ) == 0) {\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = TRUE;\r
+ }\r
+ else if (UnicodeCollation->StriColl (\r
+ UnicodeCollation,\r
+ L"-noversion",\r
+ CurrentArg\r
+ ) == 0) {\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = TRUE;\r
+ }\r
+ else if (UnicodeCollation->StriColl (\r
+ UnicodeCollation,\r
+ L"-delay",\r
+ CurrentArg\r
+ ) == 0) {\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = TRUE;\r
+ // Check for optional delay value following "-delay"\r
+ DelayValueStr = gEfiShellParametersProtocol->Argv[LoopVar + 1];\r
+ if (DelayValueStr != NULL){\r
+ if (*DelayValueStr == L':') {\r
+ DelayValueStr++;\r
+ }\r
+ if (!EFI_ERROR(ShellConvertStringToUint64 (\r
+ DelayValueStr,\r
+ &DelayValue,\r
+ FALSE,\r
+ FALSE\r
+ ))) {\r
+ ShellInfoObject.ShellInitSettings.Delay = (UINTN)DelayValue;\r
+ LoopVar++;\r
+ }\r
+ }\r
+ } else if (UnicodeCollation->StriColl (\r
+ UnicodeCollation,\r
+ L"-_exit",\r
+ CurrentArg\r
+ ) == 0) {\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = TRUE;\r
+ } else if (StrnCmp (L"-", CurrentArg, 1) == 0) {\r
+ // Unrecognised option\r
+ ShellPrintHiiEx(-1, -1, NULL,\r
+ STRING_TOKEN (STR_GEN_PROBLEM),\r
+ ShellInfoObject.HiiHandle,\r
+ CurrentArg\r
+ );\r
+ return EFI_INVALID_PARAMETER;\r
+ } else {\r
+ ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(CurrentArg));\r
+ if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
+ return (EFI_OUT_OF_RESOURCES);\r
+ }\r
+ //\r
+ // We found `file-name`.\r
+ //\r
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;\r
+\r
+ StrCpy (ShellInfoObject.ShellInitSettings.FileName, CurrentArg);\r
+ LoopVar++;\r
+\r
+ // Add `file-name-options`\r
+ for (Size = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
+ ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));\r
+ StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
+ &Size,\r
+ L" ",\r
+ 0);\r
+ if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
+ SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
+ return (EFI_OUT_OF_RESOURCES);\r
+ }\r
+ StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
+ &Size,\r
+ gEfiShellParametersProtocol->Argv[LoopVar],\r
+ 0);\r
+ if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
+ SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
+ return (EFI_OUT_OF_RESOURCES);\r
}\r
}\r
- }\r
- } else {\r
- ShellCommandLineFreeVarList(Package);\r
- Package = NULL;\r
- Status = ShellCommandLineParse (mShellParamList, &Package, &ProblemParam, FALSE);\r
- if (EFI_ERROR(Status)) {\r
- ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), ShellInfoObject.HiiHandle, ProblemParam);\r
- FreePool(ProblemParam);\r
- ShellCommandLineFreeVarList(Package);\r
- return (EFI_INVALID_PARAMETER);\r
}\r
}\r
\r
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = ShellCommandLineGetFlag(Package, L"-startup");\r
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = ShellCommandLineGetFlag(Package, L"-nostartup");\r
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = ShellCommandLineGetFlag(Package, L"-noconsoleout");\r
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = ShellCommandLineGetFlag(Package, L"-noconsolein");\r
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = ShellCommandLineGetFlag(Package, L"-nointerrupt");\r
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = ShellCommandLineGetFlag(Package, L"-nomap");\r
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = ShellCommandLineGetFlag(Package, L"-noversion");\r
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = ShellCommandLineGetFlag(Package, L"-delay");\r
- ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = ShellCommandLineGetFlag(Package, L"-_exit");\r
-\r
- ShellInfoObject.ShellInitSettings.Delay = 5;\r
-\r
+ // "-nointerrupt" overrides "-delay"\r
if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {\r
ShellInfoObject.ShellInitSettings.Delay = 0;\r
- } else if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay) {\r
- TempConst = ShellCommandLineGetValue(Package, L"-delay");\r
- if (TempConst != NULL && *TempConst == L':') {\r
- TempConst++;\r
- }\r
- if (TempConst != NULL && !EFI_ERROR(ShellConvertStringToUint64(TempConst, &Intermediate, FALSE, FALSE))) {\r
- ShellInfoObject.ShellInitSettings.Delay = (UINTN)Intermediate;\r
- }\r
}\r
- ShellCommandLineFreeVarList(Package);\r
\r
- return (Status);\r
+ return EFI_SUCCESS;\r
}\r
\r
/**\r
@param ImagePath the path to the image for shell. first place to look for the startup script\r
@param FilePath the path to the file for shell. second place to look for the startup script.\r
\r
+ @param[out] ExitStatus The exit code of the script. Ignored if NULL.\r
+\r
@retval EFI_SUCCESS the variable is initialized.\r
**/\r
EFI_STATUS\r
EFIAPI\r
DoStartupScript(\r
- EFI_DEVICE_PATH_PROTOCOL *ImagePath,\r
- EFI_DEVICE_PATH_PROTOCOL *FilePath\r
+ IN EFI_DEVICE_PATH_PROTOCOL *ImagePath,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
+ OUT SHELL_STATUS *ExitStatus\r
)\r
{\r
EFI_STATUS Status;\r
StrCat(FileStringPath, L" ");\r
StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);\r
}\r
- Status = RunCommand(FileStringPath);\r
+ Status = RunCommand(FileStringPath, ExitStatus);\r
FreePool(FileStringPath);\r
return (Status);\r
\r
// If we got a file, run it\r
//\r
if (!EFI_ERROR(Status) && FileHandle != NULL) {\r
- Status = RunScriptFileHandle (FileHandle, mStartupScript);\r
+ Status = RunScriptFile (\r
+ mStartupScript,\r
+ FileHandle,\r
+ L"",\r
+ ShellInfoObject.NewShellParametersProtocol,\r
+ ExitStatus\r
+ );\r
ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);\r
} else {\r
FileStringPath = ShellFindFilePath(mStartupScript);\r
Status = EFI_SUCCESS;\r
ASSERT(FileHandle == NULL);\r
} else {\r
- Status = RunScriptFile(FileStringPath);\r
+ Status = RunScriptFile(\r
+ FileStringPath,\r
+ NULL,\r
+ L"",\r
+ ShellInfoObject.NewShellParametersProtocol,\r
+ ExitStatus\r
+ );\r
FreePool(FileStringPath);\r
}\r
}\r
//\r
if (!EFI_ERROR (Status)) {\r
CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;\r
- Status = RunCommand(CmdLine);\r
- }\r
+ Status = RunCommand(CmdLine, NULL);\r
+ }\r
\r
//\r
// Done with this command\r
return (EFI_SUCCESS);\r
}\r
\r
+/**\r
+ Parse for the next instance of one string within another string. Can optionally make sure that \r
+ the string was not escaped (^ character) per the shell specification.\r
+\r
+ @param[in] SourceString The string to search within\r
+ @param[in] FindString The string to look for\r
+ @param[in] CheckForEscapeCharacter TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances\r
+**/\r
+CHAR16*\r
+EFIAPI\r
+FindNextInstance(\r
+ IN CONST CHAR16 *SourceString,\r
+ IN CONST CHAR16 *FindString,\r
+ IN CONST BOOLEAN CheckForEscapeCharacter\r
+ )\r
+{\r
+ CHAR16 *Temp;\r
+ if (SourceString == NULL) {\r
+ return (NULL);\r
+ }\r
+ Temp = StrStr(SourceString, FindString);\r
+\r
+ //\r
+ // If nothing found, or we dont care about escape characters\r
+ //\r
+ if (Temp == NULL || !CheckForEscapeCharacter) {\r
+ return (Temp);\r
+ }\r
+\r
+ //\r
+ // If we found an escaped character, try again on the remainder of the string\r
+ //\r
+ if ((Temp > (SourceString)) && *(Temp-1) == L'^') {\r
+ return FindNextInstance(Temp+1, FindString, CheckForEscapeCharacter);\r
+ }\r
+\r
+ //\r
+ // we found the right character\r
+ //\r
+ return (Temp);\r
+}\r
+\r
+/**\r
+ This function will eliminate unreplaced (and therefore non-found) environment variables.\r
+\r
+ @param[in,out] CmdLine The command line to update.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+StripUnreplacedEnvironmentVariables(\r
+ IN OUT CHAR16 *CmdLine\r
+ )\r
+{\r
+ CHAR16 *FirstPercent;\r
+ CHAR16 *FirstQuote;\r
+ CHAR16 *SecondPercent;\r
+ CHAR16 *SecondQuote;\r
+ CHAR16 *CurrentLocator;\r
+\r
+ for (CurrentLocator = CmdLine ; CurrentLocator != NULL ; ) {\r
+ FirstQuote = FindNextInstance(CurrentLocator, L"\"", TRUE);\r
+ FirstPercent = FindNextInstance(CurrentLocator, L"%", TRUE);\r
+ SecondPercent = FirstPercent!=NULL?FindNextInstance(FirstPercent+1, L"%", TRUE):NULL;\r
+ if (FirstPercent == NULL || SecondPercent == NULL) {\r
+ //\r
+ // If we ever dont have 2 % we are done.\r
+ //\r
+ break;\r
+ }\r
+\r
+ if (FirstQuote < FirstPercent) {\r
+ SecondQuote = FirstQuote!= NULL?FindNextInstance(FirstQuote+1, L"\"", TRUE):NULL;\r
+ //\r
+ // Quote is first found\r
+ //\r
+\r
+ if (SecondQuote < FirstPercent) {\r
+ //\r
+ // restart after the pair of "\r
+ //\r
+ CurrentLocator = SecondQuote + 1;\r
+ } else /* FirstPercent < SecondQuote */{\r
+ //\r
+ // Restart on the first percent\r
+ //\r
+ CurrentLocator = FirstPercent;\r
+ }\r
+ continue;\r
+ }\r
+ ASSERT(FirstPercent < FirstQuote);\r
+ if (SecondPercent < FirstQuote) {\r
+ //\r
+ // We need to remove from FirstPercent to SecondPercent\r
+ //\r
+ CopyMem(FirstPercent, SecondPercent + 1, StrSize(SecondPercent + 1));\r
+\r
+ //\r
+ // dont need to update the locator. both % characters are gone.\r
+ //\r
+ continue;\r
+ }\r
+ ASSERT(FirstQuote < SecondPercent);\r
+ CurrentLocator = FirstQuote;\r
+ }\r
+ return (EFI_SUCCESS);\r
+}\r
+\r
/**\r
Function allocates a new command line and replaces all instances of environment\r
variable names that are correctly preset to their values.\r
CHAR16 *NewCommandLine1;\r
CHAR16 *NewCommandLine2;\r
CHAR16 *Temp;\r
- CHAR16 *Temp2;\r
UINTN ItemSize;\r
CHAR16 *ItemTemp;\r
SCRIPT_FILE *CurrentScriptFile;\r
//\r
// Remove non-existant environment variables in scripts only\r
//\r
- for (Temp = NewCommandLine1 ; Temp != NULL ; ) {\r
- Temp = StrStr(Temp, L"%");\r
- if (Temp == NULL) {\r
- break;\r
- }\r
- while (*(Temp - 1) == L'^') {\r
- Temp = StrStr(Temp + 1, L"%");\r
- if (Temp == NULL) {\r
- break;\r
- }\r
- }\r
- if (Temp == NULL) {\r
- break;\r
- }\r
- \r
- Temp2 = StrStr(Temp + 1, L"%");\r
- if (Temp2 == NULL) {\r
- break;\r
- }\r
- while (*(Temp2 - 1) == L'^') {\r
- Temp2 = StrStr(Temp2 + 1, L"%");\r
- if (Temp2 == NULL) {\r
- break;\r
- }\r
- }\r
- if (Temp2 == NULL) {\r
- break;\r
- }\r
- \r
- Temp2++;\r
- CopyMem(Temp, Temp2, StrSize(Temp2));\r
- }\r
-\r
+ StripUnreplacedEnvironmentVariables(NewCommandLine1);\r
}\r
\r
//\r
@param[in] StdIn The pointer to the Standard input.\r
@param[in] StdOut The pointer to the Standard output.\r
\r
+ @param[out] ExitStatus The exit code of the last command in the pipeline.\r
+ Ignored if NULL.\r
+\r
@retval EFI_SUCCESS The split command is executed successfully.\r
@retval other Some error occurs when executing the split command.\r
**/\r
RunSplitCommand(\r
IN CONST CHAR16 *CmdLine,\r
IN SHELL_FILE_HANDLE *StdIn,\r
- IN SHELL_FILE_HANDLE *StdOut\r
+ IN SHELL_FILE_HANDLE *StdOut,\r
+ OUT SHELL_STATUS *ExitStatus\r
)\r
{\r
EFI_STATUS Status;\r
ASSERT(Split->SplitStdOut != NULL);\r
InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);\r
\r
- Status = RunCommand(OurCommandLine);\r
+ Status = RunCommand(OurCommandLine, NULL);\r
\r
//\r
// move the output from the first to the in to the second.\r
ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);\r
\r
if (!EFI_ERROR(Status)) {\r
- Status = RunCommand(NextCommandLine);\r
+ Status = RunCommand(NextCommandLine, ExitStatus);\r
}\r
\r
//\r
\r
/**\r
Take the original command line, substitute any variables, free \r
- the original string, return the modified copy\r
+ the original string, return the modified copy.\r
\r
- @param[in,out] CmdLine pointer to the command line to update\r
- @param[out]CmdName upon successful return the name of the command to be run\r
+ @param[in] CmdLine pointer to the command line to update.\r
\r
- @retval EFI_SUCCESS the function was successful\r
- @retval EFI_OUT_OF_RESOURCES a memory allocation failed\r
+ @retval EFI_SUCCESS the function was successful.\r
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.\r
**/\r
EFI_STATUS\r
EFIAPI\r
\r
/**\r
Take the original command line, substitute any alias in the first group of space delimited characters, free \r
- the original string, return the modified copy\r
+ the original string, return the modified copy.\r
\r
- @param[in] CmdLine pointer to the command line to update\r
- @param[out]CmdName upon successful return the name of the command to be run\r
+ @param[in] CmdLine pointer to the command line to update.\r
\r
- @retval EFI_SUCCESS the function was successful\r
- @retval EFI_OUT_OF_RESOURCES a memory allocation failed\r
+ @retval EFI_SUCCESS the function was successful.\r
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.\r
**/\r
EFI_STATUS\r
EFIAPI\r
/**\r
Takes the Argv[0] part of the command line and determine the meaning of it.\r
\r
- @param[in] CmdLine pointer to the command line to update\r
+ @param[in] CmdName pointer to the command line to update.\r
\r
- @retval INTERNAL_COMMAND The name is an internal command\r
- @retval FILE_SYS_CHANGE the name is a file system change\r
- @retval SCRIPT_FILE_NAME the name is a NSH script file\r
- @retval UNKNOWN_INVALID the name is unknown\r
- @retval EFI_APPLICATION the name is an application (.EFI)\r
+ @retval Internal_Command The name is an internal command.\r
+ @retval File_Sys_Change the name is a file system change.\r
+ @retval Script_File_Name the name is a NSH script file.\r
+ @retval Unknown_Invalid the name is unknown.\r
+ @retval Efi_Application the name is an application (.EFI).\r
**/\r
SHELL_OPERATION_TYPES\r
EFIAPI\r
// test for an internal command.\r
//\r
if (ShellCommandIsCommandOnList(CmdName)) {\r
- return (INTERNAL_COMMAND);\r
+ return (Internal_Command);\r
}\r
\r
//\r
- // Test for file system change request. anything ending with : and cant have spaces.\r
+ // Test for file system change request. anything ending with first : and cant have spaces.\r
//\r
if (CmdName[(StrLen(CmdName)-1)] == L':') {\r
- if (StrStr(CmdName, L" ") != NULL) {\r
- return (UNKNOWN_INVALID);\r
+ if ( StrStr(CmdName, L" ") != NULL \r
+ || StrLen(StrStr(CmdName, L":")) > 1\r
+ ) {\r
+ return (Unknown_Invalid);\r
}\r
- return (FILE_SYS_CHANGE);\r
+ return (File_Sys_Change);\r
}\r
\r
//\r
TempLocation2 = mScriptExtension;\r
if (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0) {\r
SHELL_FREE_NON_NULL(FileWithPath);\r
- return (SCRIPT_FILE_NAME);\r
+ return (Script_File_Name);\r
}\r
}\r
\r
// Was a file, but not a script. we treat this as an application.\r
//\r
SHELL_FREE_NON_NULL(FileWithPath);\r
- return (EFI_APPLICATION);\r
+ return (Efi_Application);\r
}\r
\r
SHELL_FREE_NON_NULL(FileWithPath);\r
//\r
// No clue what this is... return invalid flag...\r
//\r
- return (UNKNOWN_INVALID);\r
+ return (Unknown_Invalid);\r
+}\r
+\r
+/**\r
+ Determine if the first item in a command line is valid.\r
+\r
+ @param[in] CmdLine The command line to parse.\r
+\r
+ @retval EFI_SUCCESS The item is valid.\r
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.\r
+ @retval EFI_NOT_FOUND The operation type is unknown or invalid.\r
+**/\r
+EFI_STATUS \r
+EFIAPI\r
+IsValidSplit(\r
+ IN CONST CHAR16 *CmdLine\r
+ )\r
+{\r
+ CHAR16 *Temp;\r
+ CHAR16 *FirstParameter;\r
+ CHAR16 *TempWalker;\r
+ EFI_STATUS Status;\r
+\r
+ Temp = NULL;\r
+\r
+ Temp = StrnCatGrow(&Temp, NULL, CmdLine, 0);\r
+ if (Temp == NULL) {\r
+ return (EFI_OUT_OF_RESOURCES);\r
+ }\r
+\r
+ FirstParameter = StrStr(Temp, L"|");\r
+ if (FirstParameter != NULL) {\r
+ *FirstParameter = CHAR_NULL;\r
+ }\r
+\r
+ FirstParameter = NULL;\r
+\r
+ //\r
+ // Process the command line\r
+ //\r
+ Status = ProcessCommandLineToFinal(&Temp);\r
+\r
+ if (!EFI_ERROR(Status)) {\r
+ FirstParameter = AllocateZeroPool(StrSize(CmdLine));\r
+ if (FirstParameter == NULL) {\r
+ SHELL_FREE_NON_NULL(Temp);\r
+ return (EFI_OUT_OF_RESOURCES);\r
+ }\r
+ TempWalker = (CHAR16*)Temp;\r
+ GetNextParameter(&TempWalker, &FirstParameter);\r
+\r
+ if (GetOperationType(FirstParameter) == Unknown_Invalid) {\r
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
+ SetLastError(SHELL_NOT_FOUND);\r
+ Status = EFI_NOT_FOUND;\r
+ }\r
+ }\r
+\r
+ SHELL_FREE_NON_NULL(Temp);\r
+ SHELL_FREE_NON_NULL(FirstParameter);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Determine if a command line contains with a split contains only valid commands.\r
+\r
+ @param[in] CmdLine The command line to parse.\r
+\r
+ @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.\r
+ @retval EFI_ABORTED CmdLine has at least one invalid command or application.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+VerifySplit(\r
+ IN CONST CHAR16 *CmdLine\r
+ )\r
+{\r
+ 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
+ @param[in] CmdLine Pointer to the command line to process\r
+ @param[out] ExitStatus The exit status of the command. Ignored if NULL.\r
+ Invalid if this function returns an error.\r
\r
@retval EFI_SUCCESS The operation was successful\r
@return an error occured.\r
EFI_STATUS\r
EFIAPI\r
ProcessNewSplitCommandLine(\r
- IN CONST CHAR16 *CmdLine\r
+ IN CONST CHAR16 *CmdLine,\r
+ OUT SHELL_STATUS *ExitStatus\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
}\r
\r
if (Split == NULL) {\r
- Status = RunSplitCommand(CmdLine, NULL, NULL);\r
+ Status = RunSplitCommand(CmdLine, NULL, NULL, ExitStatus);\r
} else {\r
- Status = RunSplitCommand(CmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
+ Status = RunSplitCommand(\r
+ CmdLine,\r
+ Split->SplitStdIn,\r
+ Split->SplitStdOut,\r
+ ExitStatus\r
+ );\r
}\r
if (EFI_ERROR(Status)) {\r
ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);\r
}\r
\r
/**\r
- Handle a request to change the current file system\r
+ Handle a request to change the current file system.\r
\r
- @param[in] CmdLine The passed in command line\r
+ @param[in] CmdLine The passed in command line.\r
\r
- @retval EFI_SUCCESS The operation was successful\r
+ @retval EFI_SUCCESS The operation was successful.\r
**/\r
EFI_STATUS\r
EFIAPI\r
\r
if found, will add "help" as argv[0], and move the rest later.\r
\r
- @param[in,out] Argc The pointer to argc to update \r
- @param[in,out] Argv The pointer to argv to update (this is a pointer to an array of string pointers)\r
+ @param[in,out] CmdLine pointer to the command line to update\r
**/\r
EFI_STATUS\r
EFIAPI\r
-DoHelpUpdateArgcArgv(\r
- IN OUT UINTN *Argc,\r
- IN OUT CHAR16 ***Argv\r
+DoHelpUpdate(\r
+ IN OUT CHAR16 **CmdLine\r
)\r
{\r
- UINTN Count;\r
- UINTN Count2;\r
+ CHAR16 *CurrentParameter;\r
+ CHAR16 *Walker;\r
+ CHAR16 *LastWalker;\r
+ CHAR16 *NewCommandLine;\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ CurrentParameter = AllocateZeroPool(StrSize(*CmdLine));\r
+ if (CurrentParameter == NULL) {\r
+ return (EFI_OUT_OF_RESOURCES);\r
+ }\r
+\r
+ Walker = *CmdLine;\r
+ while(Walker != NULL && *Walker != CHAR_NULL) {\r
+ LastWalker = Walker;\r
+ GetNextParameter(&Walker, &CurrentParameter);\r
+ if (StrStr(CurrentParameter, L"-?") == CurrentParameter) {\r
+ LastWalker[0] = L' ';\r
+ LastWalker[1] = L' ';\r
+ NewCommandLine = AllocateZeroPool(StrSize(L"help ") + StrSize(*CmdLine));\r
+ if (NewCommandLine == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ break;\r
+ }\r
+ StrCpy(NewCommandLine, L"help ");\r
+ StrCat(NewCommandLine, *CmdLine);\r
+ SHELL_FREE_NON_NULL(*CmdLine);\r
+ *CmdLine = NewCommandLine;\r
+ break;\r
+ }\r
+ }\r
+\r
+ SHELL_FREE_NON_NULL(CurrentParameter);\r
+\r
+ return (Status);\r
+}\r
+\r
+/**\r
+ Function to update the shell variable "lasterror".\r
+\r
+ @param[in] ErrorCode the error code to put into lasterror.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SetLastError(\r
+ IN CONST 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
+\r
+ TrimSpaces(CmdLine);\r
+\r
//\r
- // Check each parameter\r
+ // update for help parsing\r
//\r
- for (Count = 0 ; Count < (*Argc) ; Count++) {\r
+ if (StrStr(*CmdLine, L"?") != NULL) {\r
//\r
- // if it's "-?" or if the first parameter is "?"\r
+ // This may do nothing if the ? does not indicate help.\r
+ // Save all the details for in the API below.\r
//\r
- if (StrStr((*Argv)[Count], L"-?") == (*Argv)[Count] \r
- || ((*Argv)[0][0] == L'?' && (*Argv)[0][1] == CHAR_NULL)\r
- ) {\r
+ Status = DoHelpUpdate(CmdLine);\r
+ }\r
+\r
+ TrimSpaces(CmdLine);\r
+\r
+ return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+ Run an internal shell command.\r
+\r
+ This API will upadate the shell's environment since these commands are libraries.\r
+ \r
+ @param[in] CmdLine the command line to run.\r
+ @param[in] FirstParameter the first parameter on the command line\r
+ @param[in] ParamProtocol the shell parameters protocol pointer\r
+\r
+ @param[out] ExitStatus The exit code of the command. Ignored if NULL.\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
+ OUT SHELL_STATUS *ExitStatus OPTIONAL\r
+)\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Argc;\r
+ CHAR16 **Argv;\r
+ SHELL_STATUS CommandReturnedStatus;\r
+ BOOLEAN LastError;\r
+\r
+ //\r
+ // get the argc and argv updated for internal commands\r
+ //\r
+ Status = UpdateArgcArgv(ParamProtocol, CmdLine, &Argv, &Argc);\r
+ if (!EFI_ERROR(Status)) {\r
+ //\r
+ // Run the internal command.\r
+ //\r
+ Status = ShellCommandRunCommandHandler(FirstParameter, &CommandReturnedStatus, &LastError);\r
+\r
+ if (!EFI_ERROR(Status)) {\r
//\r
- // We need to redo the arguments since a parameter was -?\r
- // move them all down 1 to the end, then up one then replace the first with help\r
+ // Update last error status.\r
+ // some commands do not update last error.\r
//\r
- FreePool((*Argv)[Count]);\r
- (*Argv)[Count] = NULL;\r
- for (Count2 = Count ; (Count2 + 1) < (*Argc) ; Count2++) {\r
- (*Argv)[Count2] = (*Argv)[Count2+1];\r
+ if (LastError) {\r
+ SetLastError(CommandReturnedStatus);\r
}\r
- (*Argv)[Count2] = NULL;\r
- for (Count2 = (*Argc) -1 ; Count2 > 0 ; Count2--) {\r
- (*Argv)[Count2] = (*Argv)[Count2-1];\r
+ if (ExitStatus != NULL) {\r
+ *ExitStatus = CommandReturnedStatus;\r
}\r
- (*Argv)[0] = NULL;\r
- (*Argv)[0] = StrnCatGrow(&(*Argv)[0], NULL, L"help", 0);\r
- if ((*Argv)[0] == NULL) {\r
- return (EFI_OUT_OF_RESOURCES);\r
+\r
+ //\r
+ // 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
- break;\r
}\r
}\r
- return (EFI_SUCCESS);\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
+ 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
+ @param[out] ExitStatus The exit code of the command or file.\r
+ Ignored if NULL.\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
+ OUT SHELL_STATUS *ExitStatus\r
+)\r
+{\r
+ EFI_STATUS Status;\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(\r
+ CmdLine,\r
+ FirstParameter,\r
+ ParamProtocol,\r
+ &CalleeExitStatus\r
+ );\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 (\r
+ CommandWithPath,\r
+ NULL,\r
+ CmdLine,\r
+ ParamProtocol,\r
+ &CalleeExitStatus\r
+ );\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
+ NULL,\r
+ NULL\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_SUCCESS;\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
+ if (ExitStatus != NULL) {\r
+ *ExitStatus = CalleeExitStatus;\r
+ }\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
+ @param[out] ExitStatus The exit code of the command or file.\r
+ Ignored if NULL.\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
+ OUT SHELL_STATUS *ExitStatus\r
+)\r
+{\r
+ EFI_STATUS Status;\r
+ SHELL_FILE_HANDLE OriginalStdIn;\r
+ SHELL_FILE_HANDLE OriginalStdOut;\r
+ SHELL_FILE_HANDLE OriginalStdErr;\r
+ SYSTEM_TABLE_INFO OriginalSystemTableInfo;\r
+\r
+ //\r
+ // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII\r
+ //\r
+ Status = UpdateStdInStdOutStdErr(ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
+\r
+ //\r
+ // The StdIn, StdOut, and StdErr are set up.\r
+ // Now run the command, script, or application\r
+ //\r
+ if (!EFI_ERROR(Status)) {\r
+ Status = RunCommandOrFile(\r
+ Type,\r
+ CmdLine,\r
+ FirstParameter,\r
+ ParamProtocol,\r
+ ExitStatus\r
+ );\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
command or dispatch an external application.\r
\r
@param[in] CmdLine The command line to parse.\r
+ @param[out] ExitStatus The exit code of the command. Ignored if NULL.\r
\r
@retval EFI_SUCCESS The command was completed.\r
@retval EFI_ABORTED The command's operation was aborted.\r
EFI_STATUS\r
EFIAPI\r
RunCommand(\r
- IN CONST CHAR16 *CmdLine\r
+ IN CONST CHAR16 *CmdLine,\r
+ OUT SHELL_STATUS *ExitStatus\r
)\r
{\r
EFI_STATUS Status;\r
- EFI_STATUS StatusCode;\r
- CHAR16 *CommandName;\r
- SHELL_STATUS ShellStatus;\r
- UINTN Argc;\r
- CHAR16 **Argv;\r
- BOOLEAN LastError;\r
- CHAR16 LeString[19];\r
- CHAR16 *CommandWithPath;\r
- CONST EFI_DEVICE_PATH_PROTOCOL *DevPath;\r
- CONST CHAR16 *TempLocation;\r
- CONST CHAR16 *TempLocation2;\r
- SHELL_FILE_HANDLE OriginalStdIn;\r
- SHELL_FILE_HANDLE OriginalStdOut;\r
- SHELL_FILE_HANDLE OriginalStdErr;\r
- SYSTEM_TABLE_INFO OriginalSystemTableInfo;\r
CHAR16 *CleanOriginal;\r
+ CHAR16 *FirstParameter;\r
+ CHAR16 *TempWalker;\r
+ SHELL_OPERATION_TYPES Type;\r
\r
ASSERT(CmdLine != NULL);\r
if (StrLen(CmdLine) == 0) {\r
return (EFI_SUCCESS);\r
}\r
\r
- CommandName = NULL;\r
- CommandWithPath = NULL;\r
- DevPath = NULL;\r
Status = EFI_SUCCESS;\r
CleanOriginal = NULL;\r
\r
// Handle case that passed in command line is just 1 or more " " characters.\r
//\r
if (StrLen (CleanOriginal) == 0) {\r
- if (CleanOriginal != NULL) {\r
- FreePool(CleanOriginal);\r
- CleanOriginal = NULL;\r
- }\r
+ SHELL_FREE_NON_NULL(CleanOriginal);\r
return (EFI_SUCCESS);\r
}\r
\r
- Status = ShellSubstituteAliases(&CleanOriginal);\r
+ Status = ProcessCommandLineToFinal(&CleanOriginal);\r
if (EFI_ERROR(Status)) {\r
+ SHELL_FREE_NON_NULL(CleanOriginal);\r
return (Status);\r
}\r
\r
- Status = ShellSubstituteVariables(&CleanOriginal);\r
- if (EFI_ERROR(Status)) {\r
- return (Status);\r
- }\r
-\r
- TrimSpaces(&CleanOriginal);\r
-\r
//\r
// We dont do normal processing with a split command line (output from one command input to another)\r
//\r
if (ContainsSplit(CleanOriginal)) {\r
- Status = ProcessNewSplitCommandLine(CleanOriginal);\r
- } else {\r
- //\r
- // If this is a mapped drive change handle that...\r
- //\r
- if (CleanOriginal[(StrLen(CleanOriginal)-1)] == L':' && StrStr(CleanOriginal, L" ") == NULL) {\r
- Status = ChangeMappedDrive(CleanOriginal);\r
- SHELL_FREE_NON_NULL(CleanOriginal);\r
- return (Status);\r
- }\r
-\r
-\r
- ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...\r
- /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...\r
-\r
-\r
-\r
- Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, CleanOriginal, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
- if (EFI_ERROR(Status)) {\r
- if (Status == EFI_NOT_FOUND) {\r
- ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle);\r
- } else {\r
- ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);\r
- }\r
- } else {\r
- TrimSpaces(&CleanOriginal);\r
- \r
- //\r
- // get the argc and argv updated for internal commands\r
- //\r
- Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, CleanOriginal, &Argv, &Argc);\r
- ASSERT_EFI_ERROR(Status);\r
-\r
- if (StrStr(CleanOriginal, L"?") != NULL) {\r
- Status = DoHelpUpdateArgcArgv(\r
- &ShellInfoObject.NewShellParametersProtocol->Argc,\r
- &ShellInfoObject.NewShellParametersProtocol->Argv);\r
- }\r
-\r
- //\r
- // command or file?\r
- //\r
- if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {\r
- //\r
- // Run the command (which was converted if it was an alias)\r
- //\r
- if (!EFI_ERROR(Status)) {\r
- Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError);\r
- ASSERT_EFI_ERROR(Status);\r
-\r
- if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
- UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellStatus);\r
- } else {\r
- UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ShellStatus);\r
- }\r
- DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
- if (LastError) {\r
- InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
- }\r
- //\r
- // Pass thru the exitcode from the app.\r
- //\r
- if (ShellCommandGetExit()) {\r
- Status = ShellStatus;\r
- } else if (ShellStatus != 0 && IsScriptOnlyCommand(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {\r
- Status = EFI_ABORTED;\r
- }\r
- }\r
- } else {\r
- //\r
- // run an external file (or script)\r
- //\r
- if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[0], L":") != NULL) {\r
- ASSERT (CommandWithPath == NULL);\r
- if (ShellIsFile(ShellInfoObject.NewShellParametersProtocol->Argv[0]) == EFI_SUCCESS) {\r
- CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
- }\r
- }\r
- if (CommandWithPath == NULL) {\r
- CommandWithPath = ShellFindFilePathEx(ShellInfoObject.NewShellParametersProtocol->Argv[0], mExecutableExtensions);\r
- }\r
- if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) {\r
- ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]);\r
-\r
- if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
- UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", EFI_NOT_FOUND);\r
- } else {\r
- UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", EFI_NOT_FOUND);\r
- }\r
- DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
- InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
- } else {\r
- //\r
- // Check if it's a NSH (script) file.\r
- //\r
- TempLocation = CommandWithPath+StrLen(CommandWithPath)-4;\r
- TempLocation2 = mScriptExtension;\r
- if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) {\r
- Status = RunScriptFile (CommandWithPath);\r
- } else {\r
- DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
- ASSERT(DevPath != NULL);\r
- Status = InternalShellExecuteDevicePath(\r
- &gImageHandle,\r
- DevPath,\r
- CleanOriginal,\r
- NULL,\r
- &StatusCode\r
- );\r
+ Status = ProcessNewSplitCommandLine(CleanOriginal, ExitStatus);\r
+ SHELL_FREE_NON_NULL(CleanOriginal);\r
+ return (Status);\r
+ } \r
\r
- //\r
- // Update last error status.\r
- //\r
- if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
- UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", StatusCode);\r
- } else {\r
- UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", StatusCode);\r
- }\r
- DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
- InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
- }\r
- }\r
- }\r
+ //\r
+ // We need the first parameter information so we can determine the operation type\r
+ //\r
+ FirstParameter = AllocateZeroPool(StrSize(CleanOriginal));\r
+ if (FirstParameter == NULL) {\r
+ SHELL_FREE_NON_NULL(CleanOriginal);\r
+ return (EFI_OUT_OF_RESOURCES);\r
+ }\r
+ TempWalker = CleanOriginal;\r
+ GetNextParameter(&TempWalker, &FirstParameter);\r
\r
+ //\r
+ // Depending on the first parameter we change the behavior\r
+ //\r
+ switch (Type = GetOperationType(FirstParameter)) {\r
+ case File_Sys_Change:\r
+ Status = ChangeMappedDrive(CleanOriginal);\r
+ break;\r
+ case Internal_Command:\r
+ case Script_File_Name:\r
+ case Efi_Application:\r
+ Status = SetupAndRunCommandOrFile(\r
+ Type,\r
+ CleanOriginal,\r
+ FirstParameter,\r
+ ShellInfoObject.NewShellParametersProtocol,\r
+ ExitStatus\r
+ );\r
+ break;\r
+ default:\r
//\r
- // Print some error info.\r
+ // Whatever was typed, it was invalid.\r
//\r
- if (EFI_ERROR(Status)) {\r
- ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));\r
- }\r
-\r
- CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
-\r
- RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);\r
-\r
- RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
- }\r
- if (CommandName != NULL) {\r
- if (ShellCommandGetCurrentScriptFile() != NULL && !IsScriptOnlyCommand(CommandName)) {\r
- //\r
- // if this is NOT a scipt only command return success so the script won't quit.\r
- // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)\r
- //\r
- Status = EFI_SUCCESS;\r
- }\r
- }\r
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
+ SetLastError(SHELL_NOT_FOUND);\r
+ break;\r
}\r
-\r
- SHELL_FREE_NON_NULL(CommandName);\r
- SHELL_FREE_NON_NULL(CommandWithPath);\r
+ \r
SHELL_FREE_NON_NULL(CleanOriginal);\r
+ SHELL_FREE_NON_NULL(FirstParameter);\r
\r
return (Status);\r
}\r
@param[in] Handle The handle to the already opened file.\r
@param[in] Name The name of the script file.\r
\r
+ @param[out] ExitStatus The exit code of the script. Ignored if NULL.\r
+\r
@retval EFI_SUCCESS the script completed sucessfully\r
**/\r
EFI_STATUS\r
EFIAPI\r
RunScriptFileHandle (\r
- IN SHELL_FILE_HANDLE Handle,\r
- IN CONST CHAR16 *Name\r
+ IN SHELL_FILE_HANDLE Handle,\r
+ IN CONST CHAR16 *Name,\r
+ OUT SHELL_STATUS *ExitStatus\r
)\r
{\r
EFI_STATUS Status;\r
CONST CHAR16 *CurDir;\r
UINTN LineCount;\r
CHAR16 LeString[50];\r
+ SHELL_STATUS CalleeExitStatus;\r
\r
ASSERT(!ShellCommandGetScriptExit());\r
+ \r
+ CalleeExitStatus = SHELL_SUCCESS;\r
\r
PreScriptEchoState = ShellCommandGetEchoState();\r
\r
while(!ShellFileHandleEof(Handle)) {\r
CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);\r
LineCount++;\r
- if (CommandLine == NULL || StrLen(CommandLine) == 0) {\r
+ if (CommandLine == NULL || StrLen(CommandLine) == 0 || CommandLine[0] == '#') {\r
+ SHELL_FREE_NON_NULL(CommandLine);\r
continue;\r
}\r
NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));\r
if (NewScriptFile->CurrentCommand == NULL) {\r
+ SHELL_FREE_NON_NULL(CommandLine);\r
DeleteScriptFileStruct(NewScriptFile);\r
return (EFI_OUT_OF_RESOURCES);\r
}\r
//\r
PreCommandEchoState = ShellCommandGetEchoState();\r
ShellCommandSetEchoState(FALSE);\r
- Status = RunCommand(CommandLine3+1);\r
+ Status = RunCommand(CommandLine3+1, NULL);\r
\r
//\r
// If command was "@echo -off" or "@echo -on" then don't restore echo state\r
}\r
ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);\r
}\r
- Status = RunCommand(CommandLine3);\r
+ Status = RunCommand(CommandLine3, NULL);\r
}\r
}\r
\r
//\r
// ShellCommandGetExitCode() always returns a UINT64\r
//\r
- UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellCommandGetExitCode());\r
+ CalleeExitStatus = (SHELL_STATUS) ShellCommandGetExitCode();\r
+ UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", CalleeExitStatus);\r
DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
\r
break;\r
}\r
if (EFI_ERROR(Status)) {\r
+ CalleeExitStatus = (SHELL_STATUS) Status;\r
break;\r
}\r
if (ShellCommandGetExit()) {\r
+ CalleeExitStatus = (SHELL_STATUS) ShellCommandGetExitCode();\r
break;\r
}\r
}\r
if (ShellCommandGetCurrentScriptFile()==NULL) {\r
ShellCommandSetEchoState(PreScriptEchoState);\r
}\r
+\r
+ if (ExitStatus != NULL) {\r
+ *ExitStatus = CalleeExitStatus;\r
+ }\r
+\r
return (EFI_SUCCESS);\r
}\r
\r
Function to process a NSH script file.\r
\r
@param[in] ScriptPath Pointer to the script file name (including file system path).\r
+ @param[in] Handle the handle of the script file already opened.\r
+ @param[in] CmdLine the command line to run.\r
+ @param[in] ParamProtocol the shell parameters protocol pointer\r
+\r
+ @param[out] ExitStatus The exit code of the script. Ignored if NULL.\r
\r
@retval EFI_SUCCESS the script completed sucessfully\r
**/\r
EFI_STATUS\r
EFIAPI\r
RunScriptFile (\r
- IN CONST CHAR16 *ScriptPath\r
+ IN CONST CHAR16 *ScriptPath,\r
+ IN SHELL_FILE_HANDLE Handle OPTIONAL,\r
+ IN CONST CHAR16 *CmdLine,\r
+ IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,\r
+ OUT SHELL_STATUS *ExitStatus\r
)\r
{\r
EFI_STATUS Status;\r
SHELL_FILE_HANDLE FileHandle;\r
+ UINTN Argc;\r
+ CHAR16 **Argv;\r
\r
if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {\r
return (EFI_INVALID_PARAMETER);\r
}\r
\r
- Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
- if (EFI_ERROR(Status)) {\r
- return (Status);\r
- }\r
+ //\r
+ // get the argc and argv updated for scripts\r
+ //\r
+ Status = UpdateArgcArgv(ParamProtocol, CmdLine, &Argv, &Argc);\r
+ if (!EFI_ERROR(Status)) {\r
\r
- Status = RunScriptFileHandle(FileHandle, ScriptPath);\r
+ if (Handle == NULL) {\r
+ //\r
+ // open the file\r
+ //\r
+ Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
+ if (!EFI_ERROR(Status)) {\r
+ //\r
+ // run it\r
+ //\r
+ Status = RunScriptFileHandle(FileHandle, ScriptPath, ExitStatus);\r
\r
- ShellCloseFile(&FileHandle);\r
+ //\r
+ // now close the file\r
+ //\r
+ ShellCloseFile(&FileHandle);\r
+ }\r
+ } else {\r
+ Status = RunScriptFileHandle(Handle, ScriptPath, ExitStatus);\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
return (Status);\r
}\r