X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ShellPkg%2FApplication%2FShell%2FShell.c;h=df101a32af0157e16eeac13a085e67e62397796b;hb=0477054bc0f5c891a098280cffde0aed549262b3;hp=7af8153b4d560bcc4a211b599ff4ffbb73179560;hpb=ad2bc85412dcc566dae32cd4b18da356ace1ea3d;p=mirror_edk2.git
diff --git a/ShellPkg/Application/Shell/Shell.c b/ShellPkg/Application/Shell/Shell.c
index 7af8153b4d..df101a32af 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;
}
}
@@ -407,7 +416,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 +482,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 +519,7 @@ UefiMain (
//
Status = DoShellPrompt();
} while (!ShellCommandGetExit());
+ ExitStatus = (SHELL_STATUS) ShellCommandGetExitCode();
}
if (OldConIn != NULL && ConInHandle != NULL) {
CloseSimpleTextInOnFile (gST->ConIn);
@@ -515,6 +529,7 @@ UefiMain (
}
}
+FreeResources:
//
// uninstall protocols / free memory / etc...
//
@@ -576,10 +591,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 +765,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 +798,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 ( ; 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 +973,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 +1017,7 @@ DoStartupScript(
StrCat(FileStringPath, L" ");
StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);
}
- Status = RunCommand(FileStringPath);
+ Status = RunCommand(FileStringPath, ExitStatus);
FreePool(FileStringPath);
return (Status);
@@ -991,7 +1094,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 +1111,13 @@ DoStartupScript(
Status = EFI_SUCCESS;
ASSERT(FileHandle == NULL);
} else {
- Status = RunScriptFile(FileStringPath);
+ Status = RunScriptFile(
+ FileStringPath,
+ NULL,
+ L"",
+ ShellInfoObject.NewShellParametersProtocol,
+ ExitStatus
+ );
FreePool(FileStringPath);
}
}
@@ -1067,8 +1182,8 @@ DoShellPrompt (
//
if (!EFI_ERROR (Status)) {
CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
- Status = RunCommand(CmdLine);
- }
+ Status = RunCommand(CmdLine, NULL);
+ }
//
// Done with this command
@@ -1327,6 +1442,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 +1453,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 +1508,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 +1523,7 @@ RunSplitCommand(
ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);
if (!EFI_ERROR(Status)) {
- Status = RunCommand(NextCommandLine);
+ Status = RunCommand(NextCommandLine, ExitStatus);
}
//
@@ -1431,348 +1550,916 @@ RunSplitCommand(
}
/**
- Function will process and run a command line.
+ Take the original command line, substitute any variables, free
+ the original string, return the modified copy.
- This will determine if the command line represents an internal shell
- command or dispatch an external application.
-
- @param[in] CmdLine The command line to parse.
+ @param[in] CmdLine pointer to the command line to update.
- @retval EFI_SUCCESS The command was completed.
- @retval EFI_ABORTED The command's operation was aborted.
+ @retval EFI_SUCCESS the function was successful.
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
**/
EFI_STATUS
EFIAPI
-RunCommand(
- IN CONST CHAR16 *CmdLine
+ShellSubstituteVariables(
+ IN CHAR16 **CmdLine
)
{
- EFI_STATUS Status;
- EFI_STATUS StatusCode;
- CHAR16 *CommandName;
- SHELL_STATUS ShellStatus;
- UINTN Argc;
- CHAR16 **Argv;
- BOOLEAN LastError;
- CHAR16 LeString[19];
- CHAR16 *PostAliasCmdLine;
- UINTN PostAliasSize;
- CHAR16 *PostVariableCmdLine;
- CHAR16 *CommandWithPath;
- CONST EFI_DEVICE_PATH_PROTOCOL *DevPath;
- CONST CHAR16 *TempLocation;
- CONST CHAR16 *TempLocation2;
- SHELL_FILE_HANDLE OriginalStdIn;
- SHELL_FILE_HANDLE OriginalStdOut;
- SHELL_FILE_HANDLE OriginalStdErr;
- SYSTEM_TABLE_INFO OriginalSystemTableInfo;
- UINTN Count;
- UINTN Count2;
- CHAR16 *CleanOriginal;
- SPLIT_LIST *Split;
-
- ASSERT(CmdLine != NULL);
- if (StrLen(CmdLine) == 0) {
- return (EFI_SUCCESS);
+ CHAR16 *NewCmdLine;
+ NewCmdLine = ShellConvertVariables(*CmdLine);
+ SHELL_FREE_NON_NULL(*CmdLine);
+ if (NewCmdLine == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
}
+ *CmdLine = NewCmdLine;
+ return (EFI_SUCCESS);
+}
- CommandName = NULL;
- PostVariableCmdLine = NULL;
- PostAliasCmdLine = NULL;
- CommandWithPath = NULL;
- DevPath = NULL;
- Status = EFI_SUCCESS;
- CleanOriginal = NULL;
- Split = NULL;
+/**
+ Take the original command line, substitute any alias in the first group of space delimited characters, free
+ the original string, return the modified copy.
- CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);
- if (CleanOriginal == NULL) {
- return (EFI_OUT_OF_RESOURCES);
- }
+ @param[in] CmdLine pointer to the command line to update.
- TrimSpaces(&CleanOriginal);
+ @retval EFI_SUCCESS the function was successful.
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
+**/
+EFI_STATUS
+EFIAPI
+ShellSubstituteAliases(
+ IN CHAR16 **CmdLine
+ )
+{
+ CHAR16 *NewCmdLine;
+ CHAR16 *CommandName;
+ EFI_STATUS Status;
+ UINTN PostAliasSize;
+ ASSERT(CmdLine != NULL);
+ ASSERT(*CmdLine!= NULL);
- //
- // Handle case that passed in command line is just 1 or more " " characters.
- //
- if (StrLen (CleanOriginal) == 0) {
- if (CleanOriginal != NULL) {
- FreePool(CleanOriginal);
- CleanOriginal = NULL;
- }
- return (EFI_SUCCESS);
- }
CommandName = NULL;
- if (StrStr(CleanOriginal, L" ") == NULL){
- StrnCatGrow(&CommandName, NULL, CleanOriginal, 0);
+ if (StrStr((*CmdLine), L" ") == NULL){
+ StrnCatGrow(&CommandName, NULL, (*CmdLine), 0);
} else {
- StrnCatGrow(&CommandName, NULL, CleanOriginal, StrStr(CleanOriginal, L" ") - CleanOriginal);
+ StrnCatGrow(&CommandName, NULL, (*CmdLine), StrStr((*CmdLine), L" ") - (*CmdLine));
}
- ASSERT(PostAliasCmdLine == NULL);
+ //
+ // This cannot happen 'inline' since the CmdLine can need extra space.
+ //
+ NewCmdLine = NULL;
if (!ShellCommandIsCommandOnList(CommandName)) {
//
// Convert via alias
//
Status = ShellConvertAlias(&CommandName);
+ if (EFI_ERROR(Status)){
+ return (Status);
+ }
PostAliasSize = 0;
- PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, CommandName, 0);
- PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, StrStr(CleanOriginal, L" "), 0);
- ASSERT_EFI_ERROR(Status);
+ NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, CommandName, 0);
+ if (NewCmdLine == NULL) {
+ SHELL_FREE_NON_NULL(CommandName);
+ SHELL_FREE_NON_NULL(*CmdLine);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, StrStr((*CmdLine), L" "), 0);
+ if (NewCmdLine == NULL) {
+ SHELL_FREE_NON_NULL(CommandName);
+ SHELL_FREE_NON_NULL(*CmdLine);
+ return (EFI_OUT_OF_RESOURCES);
+ }
} else {
- PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, NULL, CleanOriginal, 0);
+ NewCmdLine = StrnCatGrow(&NewCmdLine, NULL, (*CmdLine), 0);
}
- if (CleanOriginal != NULL) {
- FreePool(CleanOriginal);
- CleanOriginal = NULL;
- }
+ SHELL_FREE_NON_NULL(*CmdLine);
+ SHELL_FREE_NON_NULL(CommandName);
+
+ //
+ // re-assign the passed in double pointer to point to our newly allocated buffer
+ //
+ *CmdLine = NewCmdLine;
- if (CommandName != NULL) {
- FreePool(CommandName);
- CommandName = NULL;
- }
+ return (EFI_SUCCESS);
+}
+
+/**
+ Takes the Argv[0] part of the command line and determine the meaning of it.
- PostVariableCmdLine = ShellConvertVariables(PostAliasCmdLine);
+ @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).
+**/
+SHELL_OPERATION_TYPES
+EFIAPI
+GetOperationType(
+ IN CONST CHAR16 *CmdName
+ )
+{
+ CHAR16* FileWithPath;
+ CONST CHAR16* TempLocation;
+ CONST CHAR16* TempLocation2;
+ FileWithPath = NULL;
//
- // we can now free the modified by alias command line
+ // test for an internal command.
//
- if (PostAliasCmdLine != NULL) {
- FreePool(PostAliasCmdLine);
- PostAliasCmdLine = NULL;
+ if (ShellCommandIsCommandOnList(CmdName)) {
+ return (Internal_Command);
}
- if (PostVariableCmdLine == NULL) {
- return (EFI_OUT_OF_RESOURCES);
+ //
+ // Test for file system change request. anything ending with : and cant have spaces.
+ //
+ if (CmdName[(StrLen(CmdName)-1)] == L':') {
+ if (StrStr(CmdName, L" ") != NULL) {
+ return (Unknown_Invalid);
+ }
+ return (File_Sys_Change);
}
- TrimSpaces(&PostVariableCmdLine);
-
//
- // We dont do normal processing with a split command line (output from one command input to another)
+ // Test for a file
//
- if (ContainsSplit(PostVariableCmdLine)) {
+ if ((FileWithPath = ShellFindFilePathEx(CmdName, mExecutableExtensions)) != NULL) {
//
- // are we in an existing split???
+ // See if that file has a script file extension
//
- if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {
- Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);
- }
-
- if (Split == NULL) {
- Status = RunSplitCommand(PostVariableCmdLine, NULL, NULL);
- } else {
- Status = RunSplitCommand(PostVariableCmdLine, Split->SplitStdIn, Split->SplitStdOut);
- }
- if (EFI_ERROR(Status)) {
- ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, PostVariableCmdLine);
+ if (StrLen(FileWithPath) > 4) {
+ TempLocation = FileWithPath+StrLen(FileWithPath)-4;
+ TempLocation2 = mScriptExtension;
+ if (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0) {
+ SHELL_FREE_NON_NULL(FileWithPath);
+ return (Script_File_Name);
+ }
}
- } else {
//
- // If this is a mapped drive change handle that...
+ // Was a file, but not a script. we treat this as an application.
//
- if (PostVariableCmdLine[(StrLen(PostVariableCmdLine)-1)] == L':' && StrStr(PostVariableCmdLine, L" ") == NULL) {
- Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, PostVariableCmdLine);
- if (EFI_ERROR(Status)) {
- ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, PostVariableCmdLine);
- }
- FreePool(PostVariableCmdLine);
- 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, PostVariableCmdLine, &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(&PostVariableCmdLine);
-
- //
- // get the argc and argv updated for internal commands
- //
- Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &Argv, &Argc);
- ASSERT_EFI_ERROR(Status);
+ SHELL_FREE_NON_NULL(FileWithPath);
+ return (Efi_Application);
+ }
+
+ SHELL_FREE_NON_NULL(FileWithPath);
+ //
+ // No clue what this is... return invalid flag...
+ //
+ return (Unknown_Invalid);
+}
- for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) {
- if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[Count], L"-?") == ShellInfoObject.NewShellParametersProtocol->Argv[Count]
- || (ShellInfoObject.NewShellParametersProtocol->Argv[0][0] == L'?' && ShellInfoObject.NewShellParametersProtocol->Argv[0][1] == CHAR_NULL)
- ) {
- //
- // We need to redo the arguments since a parameter was -?
- // move them all down 1 to the end, then up one then replace the first with help
- //
- FreePool(ShellInfoObject.NewShellParametersProtocol->Argv[Count]);
- ShellInfoObject.NewShellParametersProtocol->Argv[Count] = NULL;
- for (Count2 = Count ; (Count2 + 1) < ShellInfoObject.NewShellParametersProtocol->Argc ; Count2++) {
- ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2+1];
- }
- ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = NULL;
- for (Count2 = ShellInfoObject.NewShellParametersProtocol->Argc -1 ; Count2 > 0 ; Count2--) {
- ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2-1];
- }
- ShellInfoObject.NewShellParametersProtocol->Argv[0] = NULL;
- ShellInfoObject.NewShellParametersProtocol->Argv[0] = StrnCatGrow(&ShellInfoObject.NewShellParametersProtocol->Argv[0], NULL, L"help", 0);
- break;
- }
- }
+/**
+ Determine if the first item in a command line is valid.
- //
- // 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);
+ @param[in] CmdLine The command line to parse.
- 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]);
+ @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;
- 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,
- PostVariableCmdLine,
- NULL,
- &StatusCode
- );
+ Temp = NULL;
- //
- // 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);
- }
- }
- }
+ Temp = StrnCatGrow(&Temp, NULL, CmdLine, 0);
+ if (Temp == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
- //
- // Print some error info.
- //
- if (EFI_ERROR(Status)) {
- ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));
- }
+ FirstParameter = StrStr(Temp, L"|");
+ if (FirstParameter != NULL) {
+ *FirstParameter = CHAR_NULL;
+ }
- CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);
+ FirstParameter = NULL;
- RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);
+ //
+ // Process the command line
+ //
+ Status = ProcessCommandLineToFinal(&Temp);
- RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
+ if (!EFI_ERROR(Status)) {
+ FirstParameter = AllocateZeroPool(StrSize(CmdLine));
+ if (FirstParameter == NULL) {
+ SHELL_FREE_NON_NULL(Temp);
+ return (EFI_OUT_OF_RESOURCES);
}
- 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;
- }
+ 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(CommandName);
- SHELL_FREE_NON_NULL(CommandWithPath);
- SHELL_FREE_NON_NULL(PostVariableCmdLine);
-
- return (Status);
+ SHELL_FREE_NON_NULL(Temp);
+ SHELL_FREE_NON_NULL(FirstParameter);
+ return Status;
}
-STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};
/**
- Function determins if the CommandName COULD be a valid command. It does not determine whether
- this is a valid command. It only checks for invalid characters.
+ Determine if a command line contains with a split contains only valid commands.
- @param[in] CommandName The name to check
+ @param[in] CmdLine The command line to parse.
- @retval TRUE CommandName could be a command name
- @retval FALSE CommandName could not be a valid command name
+ @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.
**/
-BOOLEAN
+EFI_STATUS
EFIAPI
-IsValidCommandName(
- IN CONST CHAR16 *CommandName
+VerifySplit(
+ IN CONST CHAR16 *CmdLine
)
{
- UINTN Count;
- if (CommandName == NULL) {
- ASSERT(FALSE);
- return (FALSE);
+ CONST CHAR16 *TempSpot;
+ EFI_STATUS Status;
+
+ //
+ // Verify up to the pipe or end character
+ //
+ Status = IsValidSplit(CmdLine);
+ if (EFI_ERROR(Status)) {
+ return (Status);
}
- for ( Count = 0
- ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])
- ; Count++
- ){
- if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {
- return (FALSE);
- }
+
+ //
+ // If this was the only item, then get out
+ //
+ if (!ContainsSplit(CmdLine)) {
+ return (EFI_SUCCESS);
}
- return (TRUE);
+
+ //
+ // 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[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.
+**/
+EFI_STATUS
+EFIAPI
+ProcessNewSplitCommandLine(
+ 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;
+
+ //
+ // are we in an existing split???
+ //
+ if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {
+ Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);
+ }
+
+ if (Split == NULL) {
+ Status = RunSplitCommand(CmdLine, NULL, NULL, ExitStatus);
+ } else {
+ 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);
+ }
+ return (Status);
+}
+
+/**
+ Handle a request to change the current file system.
+
+ @param[in] CmdLine The passed in command line.
+
+ @retval EFI_SUCCESS The operation was successful.
+**/
+EFI_STATUS
+EFIAPI
+ChangeMappedDrive(
+ IN CONST CHAR16 *CmdLine
+ )
+{
+ EFI_STATUS Status;
+ Status = EFI_SUCCESS;
+
+ //
+ // make sure we are the right operation
+ //
+ ASSERT(CmdLine[(StrLen(CmdLine)-1)] == L':' && StrStr(CmdLine, L" ") == NULL);
+
+ //
+ // Call the protocol API to do the work
+ //
+ Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, CmdLine);
+
+ //
+ // Report any errors
+ //
+ if (EFI_ERROR(Status)) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine);
+ }
+
+ return (Status);
+}
+
+/**
+ Reprocess the command line to direct all -? to the help command.
+
+ if found, will add "help" as argv[0], and move the rest later.
+
+ @param[in,out] CmdLine pointer to the command line to update
+**/
+EFI_STATUS
+EFIAPI
+DoHelpUpdate(
+ IN OUT CHAR16 **CmdLine
+ )
+{
+ CHAR16 *CurrentParameter;
+ CHAR16 *Walker;
+ CHAR16 *LastWalker;
+ CHAR16 *NewCommandLine;
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ CurrentParameter = AllocateZeroPool(StrSize(*CmdLine));
+ if (CurrentParameter == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ Walker = *CmdLine;
+ while(Walker != NULL && *Walker != CHAR_NULL) {
+ LastWalker = Walker;
+ GetNextParameter(&Walker, &CurrentParameter);
+ if (StrStr(CurrentParameter, L"-?") == CurrentParameter) {
+ LastWalker[0] = L' ';
+ LastWalker[1] = L' ';
+ NewCommandLine = AllocateZeroPool(StrSize(L"help ") + StrSize(*CmdLine));
+ if (NewCommandLine == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+ StrCpy(NewCommandLine, L"help ");
+ StrCat(NewCommandLine, *CmdLine);
+ SHELL_FREE_NON_NULL(*CmdLine);
+ *CmdLine = NewCommandLine;
+ break;
+ }
+ }
+
+ 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);
+
+ //
+ // update for help parsing
+ //
+ if (StrStr(*CmdLine, L"?") != NULL) {
+ //
+ // This may do nothing if the ? does not indicate help.
+ // Save all the details for in the API below.
+ //
+ 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)) {
+ //
+ // Update last error status.
+ // some commands do not update last error.
+ //
+ if (LastError) {
+ SetLastError(CommandReturnedStatus);
+ }
+ if (ExitStatus != NULL) {
+ *ExitStatus = CommandReturnedStatus;
+ }
+
+ //
+ // Pass thru the exitcode from the app.
+ //
+ if (ShellCommandGetExit()) {
+ //
+ // An Exit was requested ("exit" command), pass its value up.
+ //
+ Status = CommandReturnedStatus;
+ } else if (CommandReturnedStatus != SHELL_SUCCESS && IsScriptOnlyCommand(FirstParameter)) {
+ //
+ // Always abort when a script only command fails for any reason
+ //
+ Status = EFI_ABORTED;
+ } else if (ShellCommandGetCurrentScriptFile() != NULL && CommandReturnedStatus == SHELL_ABORTED) {
+ //
+ // Abort when in a script and a command aborted
+ //
+ Status = EFI_ABORTED;
+ }
+ }
+ }
+
+ //
+ // 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);
+}
+
+/**
+ Function will process and run a command line.
+
+ This will determine if the command line represents an internal shell
+ command or dispatch an external application.
+
+ @param[in] CmdLine The command line to parse.
+ @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
+RunCommand(
+ IN CONST CHAR16 *CmdLine,
+ OUT SHELL_STATUS *ExitStatus
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *CleanOriginal;
+ CHAR16 *FirstParameter;
+ CHAR16 *TempWalker;
+ SHELL_OPERATION_TYPES Type;
+
+ ASSERT(CmdLine != NULL);
+ if (StrLen(CmdLine) == 0) {
+ return (EFI_SUCCESS);
+ }
+
+ Status = EFI_SUCCESS;
+ CleanOriginal = NULL;
+
+ CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);
+ if (CleanOriginal == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ TrimSpaces(&CleanOriginal);
+
+ //
+ // Handle case that passed in command line is just 1 or more " " characters.
+ //
+ if (StrLen (CleanOriginal) == 0) {
+ SHELL_FREE_NON_NULL(CleanOriginal);
+ return (EFI_SUCCESS);
+ }
+
+ Status = ProcessCommandLineToFinal(&CleanOriginal);
+ if (EFI_ERROR(Status)) {
+ SHELL_FREE_NON_NULL(CleanOriginal);
+ return (Status);
+ }
+
+ //
+ // We dont do normal processing with a split command line (output from one command input to another)
+ //
+ if (ContainsSplit(CleanOriginal)) {
+ Status = ProcessNewSplitCommandLine(CleanOriginal, ExitStatus);
+ SHELL_FREE_NON_NULL(CleanOriginal);
+ return (Status);
+ }
+
+ //
+ // We need the first parameter information so we can determine the operation type
+ //
+ FirstParameter = AllocateZeroPool(StrSize(CleanOriginal));
+ if (FirstParameter == NULL) {
+ SHELL_FREE_NON_NULL(CleanOriginal);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ TempWalker = CleanOriginal;
+ GetNextParameter(&TempWalker, &FirstParameter);
+
+ //
+ // Depending on the first parameter we change the behavior
+ //
+ switch (Type = GetOperationType(FirstParameter)) {
+ case File_Sys_Change:
+ Status = ChangeMappedDrive(CleanOriginal);
+ break;
+ case Internal_Command:
+ case Script_File_Name:
+ case Efi_Application:
+ Status = SetupAndRunCommandOrFile(
+ Type,
+ CleanOriginal,
+ FirstParameter,
+ ShellInfoObject.NewShellParametersProtocol,
+ ExitStatus
+ );
+ break;
+ default:
+ //
+ // Whatever was typed, it was invalid.
+ //
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
+ SetLastError(SHELL_NOT_FOUND);
+ break;
+ }
+
+ SHELL_FREE_NON_NULL(CleanOriginal);
+ SHELL_FREE_NON_NULL(FirstParameter);
+
+ return (Status);
+}
+
+STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};
+/**
+ Function determins if the CommandName COULD be a valid command. It does not determine whether
+ this is a valid command. It only checks for invalid characters.
+
+ @param[in] CommandName The name to check
+
+ @retval TRUE CommandName could be a command name
+ @retval FALSE CommandName could not be a valid command name
+**/
+BOOLEAN
+EFIAPI
+IsValidCommandName(
+ IN CONST CHAR16 *CommandName
+ )
+{
+ UINTN Count;
+ if (CommandName == NULL) {
+ ASSERT(FALSE);
+ return (FALSE);
+ }
+ for ( Count = 0
+ ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])
+ ; Count++
+ ){
+ if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {
+ return (FALSE);
+ }
+ }
+ return (TRUE);
}
/**
@@ -1781,13 +2468,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;
@@ -1803,8 +2493,11 @@ RunScriptFileHandle (
CONST CHAR16 *CurDir;
UINTN LineCount;
CHAR16 LeString[50];
+ SHELL_STATUS CalleeExitStatus;
ASSERT(!ShellCommandGetScriptExit());
+
+ CalleeExitStatus = SHELL_SUCCESS;
PreScriptEchoState = ShellCommandGetEchoState();
@@ -1855,11 +2548,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);
}
@@ -1986,7 +2681,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
@@ -2008,7 +2703,7 @@ RunScriptFileHandle (
}
ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);
}
- Status = RunCommand(CommandLine3);
+ Status = RunCommand(CommandLine3, NULL);
}
}
@@ -2016,7 +2711,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);
@@ -2028,9 +2724,11 @@ RunScriptFileHandle (
break;
}
if (EFI_ERROR(Status)) {
+ CalleeExitStatus = (SHELL_STATUS) Status;
break;
}
if (ShellCommandGetExit()) {
+ CalleeExitStatus = (SHELL_STATUS) ShellCommandGetExitCode();
break;
}
}
@@ -2062,6 +2760,11 @@ RunScriptFileHandle (
if (ShellCommandGetCurrentScriptFile()==NULL) {
ShellCommandSetEchoState(PreScriptEchoState);
}
+
+ if (ExitStatus != NULL) {
+ *ExitStatus = CalleeExitStatus;
+ }
+
return (EFI_SUCCESS);
}
@@ -2069,30 +2772,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);
}