/** @file | |
This is THE shell (application) | |
Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR> | |
(C) Copyright 2013-2014, 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 | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include "Shell.h" | |
// | |
// Initialize the global structure | |
// | |
SHELL_INFO ShellInfoObject = { | |
NULL, | |
NULL, | |
FALSE, | |
FALSE, | |
{ | |
{{ | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0 | |
}}, | |
0, | |
NULL, | |
NULL | |
}, | |
{{NULL, NULL}, NULL}, | |
{ | |
{{NULL, NULL}, NULL}, | |
0, | |
0, | |
TRUE | |
}, | |
NULL, | |
0, | |
NULL, | |
NULL, | |
NULL, | |
NULL, | |
NULL, | |
{{NULL, NULL}, NULL, NULL}, | |
{{NULL, NULL}, NULL, NULL}, | |
NULL, | |
NULL, | |
NULL, | |
NULL, | |
NULL, | |
NULL, | |
NULL, | |
NULL, | |
FALSE | |
}; | |
STATIC CONST CHAR16 mScriptExtension[] = L".NSH"; | |
STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI"; | |
STATIC CONST CHAR16 mStartupScript[] = L"startup.nsh"; | |
/** | |
Cleans off leading and trailing spaces and tabs. | |
@param[in] String pointer to the string to trim them off. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
TrimSpaces( | |
IN CHAR16 **String | |
) | |
{ | |
ASSERT(String != NULL); | |
ASSERT(*String!= NULL); | |
// | |
// Remove any spaces and tabs at the beginning of the (*String). | |
// | |
while (((*String)[0] == L' ') || ((*String)[0] == L'\t')) { | |
CopyMem((*String), (*String)+1, StrSize((*String)) - sizeof((*String)[0])); | |
} | |
// | |
// Remove any spaces and tabs at the end of the (*String). | |
// | |
while ((StrLen (*String) > 0) && (((*String)[StrLen((*String))-1] == L' ') || ((*String)[StrLen((*String))-1] == L'\t'))) { | |
(*String)[StrLen((*String))-1] = CHAR_NULL; | |
} | |
return (EFI_SUCCESS); | |
} | |
/** | |
Find a command line contains a split operation | |
@param[in] CmdLine The command line to parse. | |
@retval A pointer to the | character in CmdLine or NULL if not present. | |
**/ | |
CONST CHAR16* | |
EFIAPI | |
FindSplit( | |
IN CONST CHAR16 *CmdLine | |
) | |
{ | |
CONST CHAR16 *TempSpot; | |
TempSpot = NULL; | |
if (StrStr(CmdLine, L"|") != NULL) { | |
for (TempSpot = CmdLine ; TempSpot != NULL && *TempSpot != CHAR_NULL ; TempSpot++) { | |
if (*TempSpot == L'^' && *(TempSpot+1) == L'|') { | |
TempSpot++; | |
} else if (*TempSpot == L'|') { | |
break; | |
} | |
} | |
} | |
return (TempSpot); | |
} | |
/** | |
Determine if a command line contains a split operation | |
@param[in] CmdLine The command line to parse. | |
@retval TRUE CmdLine has a valid split. | |
@retval FALSE CmdLine does not have a valid split. | |
**/ | |
BOOLEAN | |
EFIAPI | |
ContainsSplit( | |
IN CONST CHAR16 *CmdLine | |
) | |
{ | |
CONST CHAR16 *TempSpot; | |
TempSpot = FindSplit(CmdLine); | |
return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL)); | |
} | |
/** | |
Function to start monitoring for CTRL-S using SimpleTextInputEx. This | |
feature's enabled state was not known when the shell initially launched. | |
@retval EFI_SUCCESS The feature is enabled. | |
@retval EFI_OUT_OF_RESOURCES There is not enough mnemory available. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
InternalEfiShellStartCtrlSMonitor( | |
VOID | |
) | |
{ | |
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx; | |
EFI_KEY_DATA KeyData; | |
EFI_STATUS Status; | |
Status = gBS->OpenProtocol( | |
gST->ConsoleInHandle, | |
&gEfiSimpleTextInputExProtocolGuid, | |
(VOID**)&SimpleEx, | |
gImageHandle, | |
NULL, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
if (EFI_ERROR(Status)) { | |
ShellPrintHiiEx( | |
-1, | |
-1, | |
NULL, | |
STRING_TOKEN (STR_SHELL_NO_IN_EX), | |
ShellInfoObject.HiiHandle); | |
return (EFI_SUCCESS); | |
} | |
KeyData.KeyState.KeyToggleState = 0; | |
KeyData.Key.ScanCode = 0; | |
KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED; | |
KeyData.Key.UnicodeChar = L's'; | |
Status = SimpleEx->RegisterKeyNotify( | |
SimpleEx, | |
&KeyData, | |
NotificationFunction, | |
&ShellInfoObject.CtrlSNotifyHandle1); | |
KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED; | |
if (!EFI_ERROR(Status)) { | |
Status = SimpleEx->RegisterKeyNotify( | |
SimpleEx, | |
&KeyData, | |
NotificationFunction, | |
&ShellInfoObject.CtrlSNotifyHandle2); | |
} | |
KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED; | |
KeyData.Key.UnicodeChar = 19; | |
if (!EFI_ERROR(Status)) { | |
Status = SimpleEx->RegisterKeyNotify( | |
SimpleEx, | |
&KeyData, | |
NotificationFunction, | |
&ShellInfoObject.CtrlSNotifyHandle3); | |
} | |
KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED; | |
if (!EFI_ERROR(Status)) { | |
Status = SimpleEx->RegisterKeyNotify( | |
SimpleEx, | |
&KeyData, | |
NotificationFunction, | |
&ShellInfoObject.CtrlSNotifyHandle4); | |
} | |
return (Status); | |
} | |
/** | |
The entry point for the application. | |
@param[in] ImageHandle The firmware allocated handle for the EFI image. | |
@param[in] SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The entry point is executed successfully. | |
@retval other Some error occurs when executing this entry point. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
UefiMain ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
CHAR16 *TempString; | |
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); | |
} | |
// | |
// Clear the screen | |
// | |
Status = gST->ConOut->ClearScreen(gST->ConOut); | |
if (EFI_ERROR(Status)) { | |
return (Status); | |
} | |
// | |
// Populate the global structure from PCDs | |
// | |
ShellInfoObject.ImageDevPath = NULL; | |
ShellInfoObject.FileDevPath = NULL; | |
ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault); | |
ShellInfoObject.ViewingSettings.InsertMode = PcdGetBool(PcdShellInsertModeDefault); | |
ShellInfoObject.LogScreenCount = PcdGet8 (PcdShellScreenLogCount ); | |
// | |
// verify we dont allow for spec violation | |
// | |
ASSERT(ShellInfoObject.LogScreenCount >= 3); | |
// | |
// Initialize the LIST ENTRY objects... | |
// | |
InitializeListHead(&ShellInfoObject.BufferToFreeList.Link); | |
InitializeListHead(&ShellInfoObject.ViewingSettings.CommandHistory.Link); | |
InitializeListHead(&ShellInfoObject.SplitList.Link); | |
// | |
// Check PCDs for optional features that are not implemented yet. | |
// | |
if ( PcdGetBool(PcdShellSupportOldProtocols) | |
|| !FeaturePcdGet(PcdShellRequireHiiPlatform) | |
|| FeaturePcdGet(PcdShellSupportFrameworkHii) | |
) { | |
return (EFI_UNSUPPORTED); | |
} | |
// | |
// turn off the watchdog timer | |
// | |
gBS->SetWatchdogTimer (0, 0, 0, NULL); | |
// | |
// 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 | |
// | |
gST->ConOut->EnableCursor (gST->ConOut, TRUE); | |
// | |
// If supporting EFI 1.1 we need to install HII protocol | |
// only do this if PcdShellRequireHiiPlatform == FALSE | |
// | |
// remove EFI_UNSUPPORTED check above when complete. | |
///@todo add support for Framework HII | |
// | |
// install our (solitary) HII package | |
// | |
ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL); | |
if (ShellInfoObject.HiiHandle == NULL) { | |
if (PcdGetBool(PcdShellSupportFrameworkHii)) { | |
///@todo Add our package into Framework HII | |
} | |
if (ShellInfoObject.HiiHandle == NULL) { | |
Status = EFI_NOT_STARTED; | |
goto FreeResources; | |
} | |
} | |
// | |
// create and install the EfiShellParametersProtocol | |
// | |
Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance); | |
ASSERT_EFI_ERROR(Status); | |
ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL); | |
// | |
// create and install the EfiShellProtocol | |
// | |
Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol); | |
ASSERT_EFI_ERROR(Status); | |
ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL); | |
// | |
// Now initialize the shell library (it requires Shell Parameters protocol) | |
// | |
Status = ShellInitialize(); | |
ASSERT_EFI_ERROR(Status); | |
Status = CommandInit(); | |
ASSERT_EFI_ERROR(Status); | |
// | |
// Check the command line | |
// | |
Status = ProcessCommandLine (); | |
if (EFI_ERROR (Status)) { | |
goto FreeResources; | |
} | |
// | |
// If shell support level is >= 1 create the mappings and paths | |
// | |
if (PcdGet8(PcdShellSupportLevel) >= 1) { | |
Status = ShellCommandCreateInitialMappingsAndPaths(); | |
} | |
// | |
// save the device path for the loaded image and the device path for the filepath (under loaded image) | |
// These are where to look for the startup.nsh file | |
// | |
Status = GetDevicePathsForImageAndFile(&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath); | |
ASSERT_EFI_ERROR(Status); | |
// | |
// Display the version | |
// | |
if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) { | |
ShellPrintHiiEx ( | |
0, | |
gST->ConOut->Mode->CursorRow, | |
NULL, | |
STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL), | |
ShellInfoObject.HiiHandle, | |
SupportLevel[PcdGet8(PcdShellSupportLevel)], | |
gEfiShellProtocol->MajorVersion, | |
gEfiShellProtocol->MinorVersion | |
); | |
ShellPrintHiiEx ( | |
-1, | |
-1, | |
NULL, | |
STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER), | |
ShellInfoObject.HiiHandle, | |
(CHAR16 *) PcdGetPtr (PcdShellSupplier) | |
); | |
ShellPrintHiiEx ( | |
-1, | |
-1, | |
NULL, | |
STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI), | |
ShellInfoObject.HiiHandle, | |
(gST->Hdr.Revision&0xffff0000)>>16, | |
(gST->Hdr.Revision&0x0000ffff), | |
gST->FirmwareVendor, | |
gST->FirmwareRevision | |
); | |
} | |
// | |
// Display the mapping | |
// | |
if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) { | |
Status = RunCommand(L"map", NULL); | |
ASSERT_EFI_ERROR(Status); | |
} | |
// | |
// init all the built in alias' | |
// | |
Status = SetBuiltInAlias(); | |
ASSERT_EFI_ERROR(Status); | |
// | |
// Initialize environment variables | |
// | |
if (ShellCommandGetProfileList() != NULL) { | |
Status = InternalEfiShellSetEnv(L"profiles", ShellCommandGetProfileList(), TRUE); | |
ASSERT_EFI_ERROR(Status); | |
} | |
Size = 100; | |
TempString = AllocateZeroPool(Size); | |
UnicodeSPrint(TempString, Size, L"%d", PcdGet8(PcdShellSupportLevel)); | |
Status = InternalEfiShellSetEnv(L"uefishellsupport", TempString, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
UnicodeSPrint(TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion); | |
Status = InternalEfiShellSetEnv(L"uefishellversion", TempString, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
UnicodeSPrint(TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF); | |
Status = InternalEfiShellSetEnv(L"uefiversion", TempString, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
FreePool(TempString); | |
if (!EFI_ERROR(Status)) { | |
if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) { | |
// | |
// Set up the event for CTRL-C monitoring... | |
// | |
Status = InernalEfiShellStartMonitor(); | |
} | |
if (!EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) { | |
// | |
// Set up the event for CTRL-S monitoring... | |
// | |
Status = InternalEfiShellStartCtrlSMonitor(); | |
} | |
if (!EFI_ERROR(Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) { | |
// | |
// close off the gST->ConIn | |
// | |
OldConIn = gST->ConIn; | |
ConInHandle = gST->ConsoleInHandle; | |
gST->ConIn = CreateSimpleTextInOnFile((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle); | |
} else { | |
OldConIn = NULL; | |
ConInHandle = NULL; | |
} | |
if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) { | |
// | |
// process the startup script or launch the called app. | |
// | |
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) { | |
// | |
// begin the UI waiting loop | |
// | |
do { | |
// | |
// clean out all the memory allocated for CONST <something> * return values | |
// between each shell prompt presentation | |
// | |
if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){ | |
FreeBufferList(&ShellInfoObject.BufferToFreeList); | |
} | |
// | |
// Reset page break back to default. | |
// | |
ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault); | |
ASSERT (ShellInfoObject.ConsoleInfo != NULL); | |
ShellInfoObject.ConsoleInfo->Enabled = TRUE; | |
ShellInfoObject.ConsoleInfo->RowCounter = 0; | |
// | |
// Reset the CTRL-C event (yes we ignore the return values) | |
// | |
Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak); | |
// | |
// Display Prompt | |
// | |
Status = DoShellPrompt(); | |
} while (!ShellCommandGetExit()); | |
ExitStatus = (SHELL_STATUS) ShellCommandGetExitCode(); | |
} | |
if (OldConIn != NULL && ConInHandle != NULL) { | |
CloseSimpleTextInOnFile (gST->ConIn); | |
gST->ConIn = OldConIn; | |
gST->ConsoleInHandle = ConInHandle; | |
} | |
} | |
} | |
FreeResources: | |
// | |
// uninstall protocols / free memory / etc... | |
// | |
if (ShellInfoObject.UserBreakTimer != NULL) { | |
gBS->CloseEvent(ShellInfoObject.UserBreakTimer); | |
DEBUG_CODE(ShellInfoObject.UserBreakTimer = NULL;); | |
} | |
if (ShellInfoObject.ImageDevPath != NULL) { | |
FreePool(ShellInfoObject.ImageDevPath); | |
DEBUG_CODE(ShellInfoObject.ImageDevPath = NULL;); | |
} | |
if (ShellInfoObject.FileDevPath != NULL) { | |
FreePool(ShellInfoObject.FileDevPath); | |
DEBUG_CODE(ShellInfoObject.FileDevPath = NULL;); | |
} | |
if (ShellInfoObject.NewShellParametersProtocol != NULL) { | |
CleanUpShellParametersProtocol(ShellInfoObject.NewShellParametersProtocol); | |
DEBUG_CODE(ShellInfoObject.NewShellParametersProtocol = NULL;); | |
} | |
if (ShellInfoObject.NewEfiShellProtocol != NULL){ | |
if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){ | |
InternalEfiShellSetEnv(L"cwd", NULL, TRUE); | |
} | |
CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol); | |
DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;); | |
} | |
if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){ | |
FreeBufferList(&ShellInfoObject.BufferToFreeList); | |
} | |
if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){ | |
ASSERT(FALSE); ///@todo finish this de-allocation. | |
} | |
if (ShellInfoObject.ShellInitSettings.FileName != NULL) { | |
FreePool(ShellInfoObject.ShellInitSettings.FileName); | |
DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileName = NULL;); | |
} | |
if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) { | |
FreePool(ShellInfoObject.ShellInitSettings.FileOptions); | |
DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileOptions = NULL;); | |
} | |
if (ShellInfoObject.HiiHandle != NULL) { | |
HiiRemovePackages(ShellInfoObject.HiiHandle); | |
DEBUG_CODE(ShellInfoObject.HiiHandle = NULL;); | |
} | |
if (!IsListEmpty(&ShellInfoObject.ViewingSettings.CommandHistory.Link)){ | |
FreeBufferList(&ShellInfoObject.ViewingSettings.CommandHistory); | |
} | |
ASSERT(ShellInfoObject.ConsoleInfo != NULL); | |
if (ShellInfoObject.ConsoleInfo != NULL) { | |
ConsoleLoggerUninstall(ShellInfoObject.ConsoleInfo); | |
FreePool(ShellInfoObject.ConsoleInfo); | |
DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;); | |
} | |
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; | |
} | |
} | |
/** | |
Sets all the alias' that were registered with the ShellCommandLib library. | |
@retval EFI_SUCCESS all init commands were run sucessfully. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SetBuiltInAlias( | |
) | |
{ | |
EFI_STATUS Status; | |
CONST ALIAS_LIST *List; | |
ALIAS_LIST *Node; | |
// | |
// Get all the commands we want to run | |
// | |
List = ShellCommandGetInitAliasList(); | |
// | |
// for each command in the List | |
// | |
for ( Node = (ALIAS_LIST*)GetFirstNode(&List->Link) | |
; !IsNull (&List->Link, &Node->Link) | |
; Node = (ALIAS_LIST *)GetNextNode(&List->Link, &Node->Link) | |
){ | |
// | |
// install the alias' | |
// | |
Status = InternalSetAlias(Node->CommandString, Node->Alias, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
} | |
return (EFI_SUCCESS); | |
} | |
/** | |
Internal function to determine if 2 command names are really the same. | |
@param[in] Command1 The pointer to the first command name. | |
@param[in] Command2 The pointer to the second command name. | |
@retval TRUE The 2 command names are the same. | |
@retval FALSE The 2 command names are not the same. | |
**/ | |
BOOLEAN | |
EFIAPI | |
IsCommand( | |
IN CONST CHAR16 *Command1, | |
IN CONST CHAR16 *Command2 | |
) | |
{ | |
if (StringNoCaseCompare(&Command1, &Command2) == 0) { | |
return (TRUE); | |
} | |
return (FALSE); | |
} | |
/** | |
Internal function to determine if a command is a script only command. | |
@param[in] CommandName The pointer to the command name. | |
@retval TRUE The command is a script only command. | |
@retval FALSE The command is not a script only command. | |
**/ | |
BOOLEAN | |
EFIAPI | |
IsScriptOnlyCommand( | |
IN CONST CHAR16 *CommandName | |
) | |
{ | |
if (IsCommand(CommandName, L"for") | |
||IsCommand(CommandName, L"endfor") | |
||IsCommand(CommandName, L"if") | |
||IsCommand(CommandName, L"else") | |
||IsCommand(CommandName, L"endif") | |
||IsCommand(CommandName, L"goto")) { | |
return (TRUE); | |
} | |
return (FALSE); | |
} | |
/** | |
This function will populate the 2 device path protocol parameters based on the | |
global gImageHandle. The DevPath will point to the device path for the handle that has | |
loaded image protocol installed on it. The FilePath will point to the device path | |
for the file that was loaded. | |
@param[in, out] DevPath On a sucessful return the device path to the loaded image. | |
@param[in, out] FilePath On a sucessful return the device path to the file. | |
@retval EFI_SUCCESS The 2 device paths were sucessfully returned. | |
@retval other A error from gBS->HandleProtocol. | |
@sa HandleProtocol | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GetDevicePathsForImageAndFile ( | |
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath, | |
IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; | |
EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath; | |
ASSERT(DevPath != NULL); | |
ASSERT(FilePath != NULL); | |
Status = gBS->OpenProtocol ( | |
gImageHandle, | |
&gEfiLoadedImageProtocolGuid, | |
(VOID**)&LoadedImage, | |
gImageHandle, | |
NULL, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (!EFI_ERROR (Status)) { | |
Status = gBS->OpenProtocol ( | |
LoadedImage->DeviceHandle, | |
&gEfiDevicePathProtocolGuid, | |
(VOID**)&ImageDevicePath, | |
gImageHandle, | |
NULL, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (!EFI_ERROR (Status)) { | |
*DevPath = DuplicateDevicePath (ImageDevicePath); | |
*FilePath = DuplicateDevicePath (LoadedImage->FilePath); | |
gBS->CloseProtocol( | |
LoadedImage->DeviceHandle, | |
&gEfiDevicePathProtocolGuid, | |
gImageHandle, | |
NULL); | |
} | |
gBS->CloseProtocol( | |
gImageHandle, | |
&gEfiLoadedImageProtocolGuid, | |
gImageHandle, | |
NULL); | |
} | |
return (Status); | |
} | |
/** | |
Process all Uefi Shell 2.0 command line options. | |
see Uefi Shell 2.0 section 3.2 for full details. | |
the command line must resemble the following: | |
shell.efi [ShellOpt-options] [options] [file-name [file-name-options]] | |
ShellOpt-options Options which control the initialization behavior of the shell. | |
These options are read from the EFI global variable "ShellOpt" | |
and are processed before options or file-name. | |
options Options which control the initialization behavior of the shell. | |
file-name The name of a UEFI shell application or script to be executed | |
after initialization is complete. By default, if file-name is | |
specified, then -nostartup is implied. Scripts are not supported | |
by level 0. | |
file-name-options The command-line options that are passed to file-name when it | |
is invoked. | |
This will initialize the ShellInfoObject.ShellInitSettings global variable. | |
@retval EFI_SUCCESS The variable is initialized. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ProcessCommandLine( | |
VOID | |
) | |
{ | |
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 0 to parse only optional arguments at Argv[0] | |
// and parse other parameters from Argv[1]. This is for use case that | |
// UEFI Shell boot option is created, and OptionalData is provided | |
// that starts with shell command-line options. | |
// | |
for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) { | |
CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar]; | |
if (UnicodeCollation->StriColl ( | |
UnicodeCollation, | |
L"-startup", | |
CurrentArg | |
) == 0) { | |
ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = TRUE; | |
} | |
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 { | |
// | |
// First argument should be Shell.efi image name | |
// | |
if (LoopVar == 0) { | |
continue; | |
} | |
ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(CurrentArg)); | |
if (ShellInfoObject.ShellInitSettings.FileName == NULL) { | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
// | |
// We found `file-name`. | |
// | |
ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1; | |
StrCpy (ShellInfoObject.ShellInitSettings.FileName, CurrentArg); | |
LoopVar++; | |
// Add `file-name-options` | |
for (Size = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) { | |
ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL)); | |
StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions, | |
&Size, | |
L" ", | |
0); | |
if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) { | |
SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName); | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions, | |
&Size, | |
gEfiShellParametersProtocol->Argv[LoopVar], | |
0); | |
if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) { | |
SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName); | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
} | |
} | |
} | |
// "-nointerrupt" overrides "-delay" | |
if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) { | |
ShellInfoObject.ShellInitSettings.Delay = 0; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Handles all interaction with the default startup script. | |
this will check that the correct command line parameters were passed, handle the delay, and then start running the script. | |
@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( | |
IN EFI_DEVICE_PATH_PROTOCOL *ImagePath, | |
IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
OUT SHELL_STATUS *ExitStatus | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Delay; | |
EFI_INPUT_KEY Key; | |
SHELL_FILE_HANDLE FileHandle; | |
EFI_DEVICE_PATH_PROTOCOL *NewPath; | |
EFI_DEVICE_PATH_PROTOCOL *NamePath; | |
CHAR16 *FileStringPath; | |
CHAR16 *TempSpot; | |
UINTN NewSize; | |
CONST CHAR16 *MapName; | |
Key.UnicodeChar = CHAR_NULL; | |
Key.ScanCode = 0; | |
FileHandle = NULL; | |
if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) { | |
// | |
// launch something else instead | |
// | |
NewSize = StrSize(ShellInfoObject.ShellInitSettings.FileName); | |
if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) { | |
NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16); | |
} | |
FileStringPath = AllocateZeroPool(NewSize); | |
if (FileStringPath == NULL) { | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
StrCpy(FileStringPath, ShellInfoObject.ShellInitSettings.FileName); | |
if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) { | |
StrCat(FileStringPath, L" "); | |
StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions); | |
} | |
Status = RunCommand(FileStringPath, ExitStatus); | |
FreePool(FileStringPath); | |
return (Status); | |
} | |
// | |
// for shell level 0 we do no scripts | |
// Without the Startup bit overriding we allow for nostartup to prevent scripts | |
// | |
if ( (PcdGet8(PcdShellSupportLevel) < 1) | |
|| (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup) | |
){ | |
return (EFI_SUCCESS); | |
} | |
gST->ConOut->EnableCursor(gST->ConOut, FALSE); | |
// | |
// print out our warning and see if they press a key | |
// | |
for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay | |
; Delay != 0 && EFI_ERROR(Status) | |
; Delay-- | |
){ | |
ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay); | |
gBS->Stall (1000000); | |
if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) { | |
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); | |
} | |
} | |
ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle); | |
gST->ConOut->EnableCursor(gST->ConOut, TRUE); | |
// | |
// ESC was pressed | |
// | |
if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) { | |
return (EFI_SUCCESS); | |
} | |
// | |
// Try the first location (must be file system) | |
// | |
MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath(&ImagePath); | |
if (MapName != NULL) { | |
FileStringPath = NULL; | |
NewSize = 0; | |
FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0); | |
if (FileStringPath == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
} else { | |
TempSpot = StrStr(FileStringPath, L";"); | |
if (TempSpot != NULL) { | |
*TempSpot = CHAR_NULL; | |
} | |
FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0); | |
PathRemoveLastItem(FileStringPath); | |
FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0); | |
Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ); | |
FreePool(FileStringPath); | |
} | |
} | |
if (EFI_ERROR(Status)) { | |
NamePath = FileDevicePath (NULL, mStartupScript); | |
NewPath = AppendDevicePathNode (ImagePath, NamePath); | |
FreePool(NamePath); | |
// | |
// Try the location | |
// | |
Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0); | |
FreePool(NewPath); | |
} | |
// | |
// If we got a file, run it | |
// | |
if (!EFI_ERROR(Status) && FileHandle != NULL) { | |
Status = RunScriptFile ( | |
mStartupScript, | |
FileHandle, | |
L"", | |
ShellInfoObject.NewShellParametersProtocol, | |
ExitStatus | |
); | |
ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle); | |
} else { | |
FileStringPath = ShellFindFilePath(mStartupScript); | |
if (FileStringPath == NULL) { | |
// | |
// we return success since we dont need to have a startup script | |
// | |
Status = EFI_SUCCESS; | |
ASSERT(FileHandle == NULL); | |
} else { | |
Status = RunScriptFile( | |
FileStringPath, | |
NULL, | |
L"", | |
ShellInfoObject.NewShellParametersProtocol, | |
ExitStatus | |
); | |
FreePool(FileStringPath); | |
} | |
} | |
return (Status); | |
} | |
/** | |
Function to perform the shell prompt looping. It will do a single prompt, | |
dispatch the result, and then return. It is expected that the caller will | |
call this function in a loop many times. | |
@retval EFI_SUCCESS | |
@retval RETURN_ABORTED | |
**/ | |
EFI_STATUS | |
EFIAPI | |
DoShellPrompt ( | |
VOID | |
) | |
{ | |
UINTN Column; | |
UINTN Row; | |
CHAR16 *CmdLine; | |
CONST CHAR16 *CurDir; | |
UINTN BufferSize; | |
EFI_STATUS Status; | |
CurDir = NULL; | |
// | |
// Get screen setting to decide size of the command line buffer | |
// | |
gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row); | |
BufferSize = Column * Row * sizeof (CHAR16); | |
CmdLine = AllocateZeroPool (BufferSize); | |
if (CmdLine == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd"); | |
// | |
// Prompt for input | |
// | |
gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow); | |
if (CurDir != NULL && StrLen(CurDir) > 1) { | |
ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir); | |
} else { | |
ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle); | |
} | |
// | |
// Read a line from the console | |
// | |
Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine); | |
// | |
// Null terminate the string and parse it | |
// | |
if (!EFI_ERROR (Status)) { | |
CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL; | |
Status = RunCommand(CmdLine, NULL); | |
} | |
// | |
// Done with this command | |
// | |
FreePool (CmdLine); | |
return Status; | |
} | |
/** | |
Add a buffer to the Buffer To Free List for safely returning buffers to other | |
places without risking letting them modify internal shell information. | |
@param Buffer Something to pass to FreePool when the shell is exiting. | |
**/ | |
VOID* | |
EFIAPI | |
AddBufferToFreeList( | |
VOID *Buffer | |
) | |
{ | |
BUFFER_LIST *BufferListEntry; | |
if (Buffer == NULL) { | |
return (NULL); | |
} | |
BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST)); | |
ASSERT(BufferListEntry != NULL); | |
BufferListEntry->Buffer = Buffer; | |
InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link); | |
return (Buffer); | |
} | |
/** | |
Add a buffer to the Line History List | |
@param Buffer The line buffer to add. | |
**/ | |
VOID | |
EFIAPI | |
AddLineToCommandHistory( | |
IN CONST CHAR16 *Buffer | |
) | |
{ | |
BUFFER_LIST *Node; | |
Node = AllocateZeroPool(sizeof(BUFFER_LIST)); | |
ASSERT(Node != NULL); | |
Node->Buffer = AllocateZeroPool(StrSize(Buffer)); | |
ASSERT(Node->Buffer != NULL); | |
StrCpy(Node->Buffer, Buffer); | |
InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link); | |
} | |
/** | |
Checks if a string is an alias for another command. If yes, then it replaces the alias name | |
with the correct command name. | |
@param[in, out] CommandString Upon entry the potential alias. Upon return the | |
command name if it was an alias. If it was not | |
an alias it will be unchanged. This function may | |
change the buffer to fit the command name. | |
@retval EFI_SUCCESS The name was changed. | |
@retval EFI_SUCCESS The name was not an alias. | |
@retval EFI_OUT_OF_RESOURCES A memory allocation failed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ShellConvertAlias( | |
IN OUT CHAR16 **CommandString | |
) | |
{ | |
CONST CHAR16 *NewString; | |
NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL); | |
if (NewString == NULL) { | |
return (EFI_SUCCESS); | |
} | |
FreePool(*CommandString); | |
*CommandString = AllocateZeroPool(StrSize(NewString)); | |
if (*CommandString == NULL) { | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
StrCpy(*CommandString, NewString); | |
return (EFI_SUCCESS); | |
} | |
/** | |
Parse for the next instance of one string within another string. Can optionally make sure that | |
the string was not escaped (^ character) per the shell specification. | |
@param[in] SourceString The string to search within | |
@param[in] FindString The string to look for | |
@param[in] CheckForEscapeCharacter TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances | |
**/ | |
CHAR16* | |
EFIAPI | |
FindNextInstance( | |
IN CONST CHAR16 *SourceString, | |
IN CONST CHAR16 *FindString, | |
IN CONST BOOLEAN CheckForEscapeCharacter | |
) | |
{ | |
CHAR16 *Temp; | |
if (SourceString == NULL) { | |
return (NULL); | |
} | |
Temp = StrStr(SourceString, FindString); | |
// | |
// If nothing found, or we dont care about escape characters | |
// | |
if (Temp == NULL || !CheckForEscapeCharacter) { | |
return (Temp); | |
} | |
// | |
// If we found an escaped character, try again on the remainder of the string | |
// | |
if ((Temp > (SourceString)) && *(Temp-1) == L'^') { | |
return FindNextInstance(Temp+1, FindString, CheckForEscapeCharacter); | |
} | |
// | |
// we found the right character | |
// | |
return (Temp); | |
} | |
/** | |
This function will eliminate unreplaced (and therefore non-found) environment variables. | |
@param[in,out] CmdLine The command line to update. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
StripUnreplacedEnvironmentVariables( | |
IN OUT CHAR16 *CmdLine | |
) | |
{ | |
CHAR16 *FirstPercent; | |
CHAR16 *FirstQuote; | |
CHAR16 *SecondPercent; | |
CHAR16 *SecondQuote; | |
CHAR16 *CurrentLocator; | |
for (CurrentLocator = CmdLine ; CurrentLocator != NULL ; ) { | |
FirstQuote = FindNextInstance(CurrentLocator, L"\"", TRUE); | |
FirstPercent = FindNextInstance(CurrentLocator, L"%", TRUE); | |
SecondPercent = FirstPercent!=NULL?FindNextInstance(FirstPercent+1, L"%", TRUE):NULL; | |
if (FirstPercent == NULL || SecondPercent == NULL) { | |
// | |
// If we ever dont have 2 % we are done. | |
// | |
break; | |
} | |
if (FirstQuote < FirstPercent) { | |
SecondQuote = FirstQuote!= NULL?FindNextInstance(FirstQuote+1, L"\"", TRUE):NULL; | |
// | |
// Quote is first found | |
// | |
if (SecondQuote < FirstPercent) { | |
// | |
// restart after the pair of " | |
// | |
CurrentLocator = SecondQuote + 1; | |
} else /* FirstPercent < SecondQuote */{ | |
// | |
// Restart on the first percent | |
// | |
CurrentLocator = FirstPercent; | |
} | |
continue; | |
} | |
ASSERT(FirstPercent < FirstQuote); | |
if (SecondPercent < FirstQuote) { | |
FirstPercent[0] = L'\"'; | |
SecondPercent[0] = L'\"'; | |
// | |
// We need to remove from FirstPercent to SecondPercent | |
// | |
CopyMem(FirstPercent + 1, SecondPercent, StrSize(SecondPercent)); | |
CurrentLocator = FirstPercent + 2; | |
continue; | |
} | |
ASSERT(FirstQuote < SecondPercent); | |
CurrentLocator = FirstQuote; | |
} | |
return (EFI_SUCCESS); | |
} | |
/** | |
Function allocates a new command line and replaces all instances of environment | |
variable names that are correctly preset to their values. | |
If the return value is not NULL the memory must be caller freed. | |
@param[in] OriginalCommandLine The original command line | |
@retval NULL An error ocurred. | |
@return The new command line with no environment variables present. | |
**/ | |
CHAR16* | |
EFIAPI | |
ShellConvertVariables ( | |
IN CONST CHAR16 *OriginalCommandLine | |
) | |
{ | |
CONST CHAR16 *MasterEnvList; | |
UINTN NewSize; | |
CHAR16 *NewCommandLine1; | |
CHAR16 *NewCommandLine2; | |
CHAR16 *Temp; | |
UINTN ItemSize; | |
CHAR16 *ItemTemp; | |
SCRIPT_FILE *CurrentScriptFile; | |
ALIAS_LIST *AliasListNode; | |
ASSERT(OriginalCommandLine != NULL); | |
ItemSize = 0; | |
NewSize = StrSize(OriginalCommandLine); | |
CurrentScriptFile = ShellCommandGetCurrentScriptFile(); | |
Temp = NULL; | |
///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ? | |
// | |
// calculate the size required for the post-conversion string... | |
// | |
if (CurrentScriptFile != NULL) { | |
for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList) | |
; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link) | |
; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link) | |
){ | |
for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias) | |
; Temp != NULL | |
; Temp = StrStr(Temp+1, AliasListNode->Alias) | |
){ | |
// | |
// we need a preceeding and if there is space no ^ preceeding (if no space ignore) | |
// | |
if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) { | |
NewSize += StrSize(AliasListNode->CommandString); | |
} | |
} | |
} | |
} | |
for (MasterEnvList = EfiShellGetEnv(NULL) | |
; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL | |
; MasterEnvList += StrLen(MasterEnvList) + 1 | |
){ | |
if (StrSize(MasterEnvList) > ItemSize) { | |
ItemSize = StrSize(MasterEnvList); | |
} | |
for (Temp = StrStr(OriginalCommandLine, MasterEnvList) | |
; Temp != NULL | |
; Temp = StrStr(Temp+1, MasterEnvList) | |
){ | |
// | |
// we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore) | |
// | |
if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' && | |
((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) { | |
NewSize+=StrSize(EfiShellGetEnv(MasterEnvList)); | |
} | |
} | |
} | |
// | |
// now do the replacements... | |
// | |
NewCommandLine1 = AllocateZeroPool(NewSize); | |
NewCommandLine2 = AllocateZeroPool(NewSize); | |
ItemTemp = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16))); | |
if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) { | |
SHELL_FREE_NON_NULL(NewCommandLine1); | |
SHELL_FREE_NON_NULL(NewCommandLine2); | |
SHELL_FREE_NON_NULL(ItemTemp); | |
return (NULL); | |
} | |
StrCpy(NewCommandLine1, OriginalCommandLine); | |
for (MasterEnvList = EfiShellGetEnv(NULL) | |
; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL | |
; MasterEnvList += StrLen(MasterEnvList) + 1 | |
){ | |
StrCpy(ItemTemp, L"%"); | |
StrCat(ItemTemp, MasterEnvList); | |
StrCat(ItemTemp, L"%"); | |
ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE); | |
StrCpy(NewCommandLine1, NewCommandLine2); | |
} | |
if (CurrentScriptFile != NULL) { | |
for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList) | |
; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link) | |
; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link) | |
){ | |
ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE); | |
StrCpy(NewCommandLine1, NewCommandLine2); | |
} | |
// | |
// Remove non-existant environment variables in scripts only | |
// | |
StripUnreplacedEnvironmentVariables(NewCommandLine1); | |
} | |
// | |
// Now cleanup any straggler intentionally ignored "%" characters | |
// | |
ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE); | |
StrCpy(NewCommandLine1, NewCommandLine2); | |
FreePool(NewCommandLine2); | |
FreePool(ItemTemp); | |
return (NewCommandLine1); | |
} | |
/** | |
Internal function to run a command line with pipe usage. | |
@param[in] CmdLine The pointer to the command line. | |
@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. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
RunSplitCommand( | |
IN CONST CHAR16 *CmdLine, | |
IN SHELL_FILE_HANDLE *StdIn, | |
IN SHELL_FILE_HANDLE *StdOut, | |
OUT SHELL_STATUS *ExitStatus | |
) | |
{ | |
EFI_STATUS Status; | |
CHAR16 *NextCommandLine; | |
CHAR16 *OurCommandLine; | |
UINTN Size1; | |
UINTN Size2; | |
SPLIT_LIST *Split; | |
SHELL_FILE_HANDLE *TempFileHandle; | |
BOOLEAN Unicode; | |
ASSERT(StdOut == NULL); | |
ASSERT(StrStr(CmdLine, L"|") != NULL); | |
Status = EFI_SUCCESS; | |
NextCommandLine = NULL; | |
OurCommandLine = NULL; | |
Size1 = 0; | |
Size2 = 0; | |
NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0); | |
OurCommandLine = StrnCatGrow(&OurCommandLine , &Size2, CmdLine , StrStr(CmdLine, L"|") - CmdLine); | |
if (NextCommandLine == NULL || OurCommandLine == NULL) { | |
SHELL_FREE_NON_NULL(OurCommandLine); | |
SHELL_FREE_NON_NULL(NextCommandLine); | |
return (EFI_OUT_OF_RESOURCES); | |
} else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) { | |
SHELL_FREE_NON_NULL(OurCommandLine); | |
SHELL_FREE_NON_NULL(NextCommandLine); | |
return (EFI_INVALID_PARAMETER); | |
} else if (NextCommandLine[0] != CHAR_NULL && | |
NextCommandLine[0] == L'a' && | |
NextCommandLine[1] == L' ' | |
){ | |
CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0])); | |
Unicode = FALSE; | |
} else { | |
Unicode = TRUE; | |
} | |
// | |
// make a SPLIT_LIST item and add to list | |
// | |
Split = AllocateZeroPool(sizeof(SPLIT_LIST)); | |
ASSERT(Split != NULL); | |
Split->SplitStdIn = StdIn; | |
Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL); | |
ASSERT(Split->SplitStdOut != NULL); | |
InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link); | |
Status = RunCommand(OurCommandLine, NULL); | |
// | |
// move the output from the first to the in to the second. | |
// | |
TempFileHandle = Split->SplitStdOut; | |
if (Split->SplitStdIn == StdIn) { | |
Split->SplitStdOut = NULL; | |
} else { | |
Split->SplitStdOut = Split->SplitStdIn; | |
} | |
Split->SplitStdIn = TempFileHandle; | |
ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0); | |
if (!EFI_ERROR(Status)) { | |
Status = RunCommand(NextCommandLine, ExitStatus); | |
} | |
// | |
// remove the top level from the ScriptList | |
// | |
ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split); | |
RemoveEntryList(&Split->Link); | |
// | |
// Note that the original StdIn is now the StdOut... | |
// | |
if (Split->SplitStdOut != NULL && Split->SplitStdOut != StdIn) { | |
ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut)); | |
} | |
if (Split->SplitStdIn != NULL) { | |
ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn)); | |
} | |
FreePool(Split); | |
FreePool(NextCommandLine); | |
FreePool(OurCommandLine); | |
return (Status); | |
} | |
/** | |
Take the original command line, substitute any variables, free | |
the original string, return the modified copy. | |
@param[in] CmdLine pointer to the command line to update. | |
@retval EFI_SUCCESS the function was successful. | |
@retval EFI_OUT_OF_RESOURCES a memory allocation failed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ShellSubstituteVariables( | |
IN CHAR16 **CmdLine | |
) | |
{ | |
CHAR16 *NewCmdLine; | |
NewCmdLine = ShellConvertVariables(*CmdLine); | |
SHELL_FREE_NON_NULL(*CmdLine); | |
if (NewCmdLine == NULL) { | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
*CmdLine = NewCmdLine; | |
return (EFI_SUCCESS); | |
} | |
/** | |
Take the original command line, substitute any alias in the first group of space delimited characters, free | |
the original string, return the modified copy. | |
@param[in] CmdLine pointer to the command line to update. | |
@retval EFI_SUCCESS the function was successful. | |
@retval EFI_OUT_OF_RESOURCES a memory allocation failed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ShellSubstituteAliases( | |
IN CHAR16 **CmdLine | |
) | |
{ | |
CHAR16 *NewCmdLine; | |
CHAR16 *CommandName; | |
EFI_STATUS Status; | |
UINTN PostAliasSize; | |
ASSERT(CmdLine != NULL); | |
ASSERT(*CmdLine!= NULL); | |
CommandName = NULL; | |
if (StrStr((*CmdLine), L" ") == NULL){ | |
StrnCatGrow(&CommandName, NULL, (*CmdLine), 0); | |
} else { | |
StrnCatGrow(&CommandName, NULL, (*CmdLine), StrStr((*CmdLine), L" ") - (*CmdLine)); | |
} | |
// | |
// 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; | |
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 { | |
NewCmdLine = StrnCatGrow(&NewCmdLine, NULL, (*CmdLine), 0); | |
} | |
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; | |
return (EFI_SUCCESS); | |
} | |
/** | |
Takes the Argv[0] part of the command line and determine the meaning of it. | |
@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; | |
// | |
// test for an internal command. | |
// | |
if (ShellCommandIsCommandOnList(CmdName)) { | |
return (Internal_Command); | |
} | |
// | |
// Test for file system change request. anything ending with first : and cant have spaces. | |
// | |
if (CmdName[(StrLen(CmdName)-1)] == L':') { | |
if ( StrStr(CmdName, L" ") != NULL | |
|| StrLen(StrStr(CmdName, L":")) > 1 | |
) { | |
return (Unknown_Invalid); | |
} | |
return (File_Sys_Change); | |
} | |
// | |
// Test for a file | |
// | |
if ((FileWithPath = ShellFindFilePathEx(CmdName, mExecutableExtensions)) != NULL) { | |
// | |
// See if that file has a script file extension | |
// | |
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); | |
} | |
} | |
// | |
// Was a file, but not a script. we treat this as an application. | |
// | |
SHELL_FREE_NON_NULL(FileWithPath); | |
return (Efi_Application); | |
} | |
SHELL_FREE_NON_NULL(FileWithPath); | |
// | |
// No clue what this is... return invalid flag... | |
// | |
return (Unknown_Invalid); | |
} | |
/** | |
Determine if the first item in a command line is valid. | |
@param[in] CmdLine The command line to parse. | |
@retval EFI_SUCCESS The item is valid. | |
@retval EFI_OUT_OF_RESOURCES A memory allocation failed. | |
@retval EFI_NOT_FOUND The operation type is unknown or invalid. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
IsValidSplit( | |
IN CONST CHAR16 *CmdLine | |
) | |
{ | |
CHAR16 *Temp; | |
CHAR16 *FirstParameter; | |
CHAR16 *TempWalker; | |
EFI_STATUS Status; | |
Temp = NULL; | |
Temp = StrnCatGrow(&Temp, NULL, CmdLine, 0); | |
if (Temp == NULL) { | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
FirstParameter = StrStr(Temp, L"|"); | |
if (FirstParameter != NULL) { | |
*FirstParameter = CHAR_NULL; | |
} | |
FirstParameter = NULL; | |
// | |
// Process the command line | |
// | |
Status = ProcessCommandLineToFinal(&Temp); | |
if (!EFI_ERROR(Status)) { | |
FirstParameter = AllocateZeroPool(StrSize(CmdLine)); | |
if (FirstParameter == NULL) { | |
SHELL_FREE_NON_NULL(Temp); | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
TempWalker = (CHAR16*)Temp; | |
GetNextParameter(&TempWalker, &FirstParameter); | |
if (GetOperationType(FirstParameter) == Unknown_Invalid) { | |
ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter); | |
SetLastError(SHELL_NOT_FOUND); | |
Status = EFI_NOT_FOUND; | |
} | |
} | |
SHELL_FREE_NON_NULL(Temp); | |
SHELL_FREE_NON_NULL(FirstParameter); | |
return Status; | |
} | |
/** | |
Determine if a command line contains with a split contains only valid commands. | |
@param[in] CmdLine The command line to parse. | |
@retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split. | |
@retval EFI_ABORTED CmdLine has at least one invalid command or application. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
VerifySplit( | |
IN CONST CHAR16 *CmdLine | |
) | |
{ | |
CONST CHAR16 *TempSpot; | |
EFI_STATUS Status; | |
// | |
// Verify up to the pipe or end character | |
// | |
Status = IsValidSplit(CmdLine); | |
if (EFI_ERROR(Status)) { | |
return (Status); | |
} | |
// | |
// If this was the only item, then get out | |
// | |
if (!ContainsSplit(CmdLine)) { | |
return (EFI_SUCCESS); | |
} | |
// | |
// recurse to verify the next item | |
// | |
TempSpot = FindSplit(CmdLine)+1; | |
return (VerifySplit(TempSpot)); | |
} | |
/** | |
Process a split based operation. | |
@param[in] CmdLine Pointer to the command line to process | |
@param[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); | |
} | |
ASSERT (*CmdLine != NULL); | |
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; | |
EFI_STATUS StartStatus; | |
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, | |
&StartStatus, | |
NULL, | |
NULL | |
); | |
SHELL_FREE_NON_NULL(DevPath); | |
if(EFI_ERROR (Status)) { | |
CalleeExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT)); | |
} else { | |
CalleeExitStatus = (SHELL_STATUS) StartStatus; | |
} | |
// | |
// 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 (FirstParameter); | |
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); | |
} | |
/** | |
Function to process a NSH script file via SHELL_FILE_HANDLE. | |
@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, | |
OUT SHELL_STATUS *ExitStatus | |
) | |
{ | |
EFI_STATUS Status; | |
SCRIPT_FILE *NewScriptFile; | |
UINTN LoopVar; | |
CHAR16 *CommandLine; | |
CHAR16 *CommandLine2; | |
CHAR16 *CommandLine3; | |
SCRIPT_COMMAND_LIST *LastCommand; | |
BOOLEAN Ascii; | |
BOOLEAN PreScriptEchoState; | |
BOOLEAN PreCommandEchoState; | |
CONST CHAR16 *CurDir; | |
UINTN LineCount; | |
CHAR16 LeString[50]; | |
SHELL_STATUS CalleeExitStatus; | |
ASSERT(!ShellCommandGetScriptExit()); | |
CalleeExitStatus = SHELL_SUCCESS; | |
PreScriptEchoState = ShellCommandGetEchoState(); | |
NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE)); | |
if (NewScriptFile == NULL) { | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
// | |
// Set up the name | |
// | |
ASSERT(NewScriptFile->ScriptName == NULL); | |
NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0); | |
if (NewScriptFile->ScriptName == NULL) { | |
DeleteScriptFileStruct(NewScriptFile); | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
// | |
// Save the parameters (used to replace %0 to %9 later on) | |
// | |
NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc; | |
if (NewScriptFile->Argc != 0) { | |
NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*)); | |
if (NewScriptFile->Argv == NULL) { | |
DeleteScriptFileStruct(NewScriptFile); | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) { | |
ASSERT(NewScriptFile->Argv[LoopVar] == NULL); | |
NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0); | |
if (NewScriptFile->Argv[LoopVar] == NULL) { | |
DeleteScriptFileStruct(NewScriptFile); | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
} | |
} else { | |
NewScriptFile->Argv = NULL; | |
} | |
InitializeListHead(&NewScriptFile->CommandList); | |
InitializeListHead(&NewScriptFile->SubstList); | |
// | |
// Now build the list of all script commands. | |
// | |
LineCount = 0; | |
while(!ShellFileHandleEof(Handle)) { | |
CommandLine = ShellFileHandleReturnLine(Handle, &Ascii); | |
LineCount++; | |
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); | |
} | |
NewScriptFile->CurrentCommand->Cl = CommandLine; | |
NewScriptFile->CurrentCommand->Data = NULL; | |
NewScriptFile->CurrentCommand->Line = LineCount; | |
InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link); | |
} | |
// | |
// Add this as the topmost script file | |
// | |
ShellCommandSetNewScript (NewScriptFile); | |
// | |
// Now enumerate through the commands and run each one. | |
// | |
CommandLine = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize)); | |
if (CommandLine == NULL) { | |
DeleteScriptFileStruct(NewScriptFile); | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
CommandLine2 = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize)); | |
if (CommandLine2 == NULL) { | |
FreePool(CommandLine); | |
DeleteScriptFileStruct(NewScriptFile); | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList) | |
; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link) | |
; // conditional increment in the body of the loop | |
){ | |
ASSERT(CommandLine2 != NULL); | |
StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl); | |
// | |
// NULL out comments | |
// | |
for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) { | |
if (*CommandLine3 == L'^') { | |
if (*(CommandLine3+1) == L'#' || *(CommandLine3+1) == L':') { | |
CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0])); | |
} | |
} else if (*CommandLine3 == L'#') { | |
*CommandLine3 = CHAR_NULL; | |
} | |
} | |
if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) { | |
// | |
// Due to variability in starting the find and replace action we need to have both buffers the same. | |
// | |
StrCpy(CommandLine, CommandLine2); | |
// | |
// Remove the %0 to %9 from the command line (if we have some arguments) | |
// | |
if (NewScriptFile->Argv != NULL) { | |
switch (NewScriptFile->Argc) { | |
default: | |
Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", NewScriptFile->Argv[9], FALSE, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
case 9: | |
Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", NewScriptFile->Argv[8], FALSE, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
case 8: | |
Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", NewScriptFile->Argv[7], FALSE, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
case 7: | |
Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", NewScriptFile->Argv[6], FALSE, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
case 6: | |
Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", NewScriptFile->Argv[5], FALSE, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
case 5: | |
Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", NewScriptFile->Argv[4], FALSE, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
case 4: | |
Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", NewScriptFile->Argv[3], FALSE, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
case 3: | |
Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", NewScriptFile->Argv[2], FALSE, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
case 2: | |
Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", NewScriptFile->Argv[1], FALSE, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
case 1: | |
Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, TRUE); | |
ASSERT_EFI_ERROR(Status); | |
break; | |
case 0: | |
break; | |
} | |
} | |
Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", L"\"\"", FALSE, FALSE); | |
Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", L"\"\"", FALSE, FALSE); | |
Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", L"\"\"", FALSE, FALSE); | |
Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", L"\"\"", FALSE, FALSE); | |
Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", L"\"\"", FALSE, FALSE); | |
Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", L"\"\"", FALSE, FALSE); | |
Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", L"\"\"", FALSE, FALSE); | |
Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", L"\"\"", FALSE, FALSE); | |
Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", L"\"\"", FALSE, FALSE); | |
StrCpy(CommandLine2, CommandLine); | |
LastCommand = NewScriptFile->CurrentCommand; | |
for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++); | |
if (CommandLine3 != NULL && CommandLine3[0] == L':' ) { | |
// | |
// This line is a goto target / label | |
// | |
} else { | |
if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) { | |
if (CommandLine3[0] == L'@') { | |
// | |
// We need to save the current echo state | |
// and disable echo for just this command. | |
// | |
PreCommandEchoState = ShellCommandGetEchoState(); | |
ShellCommandSetEchoState(FALSE); | |
Status = RunCommand(CommandLine3+1, NULL); | |
// | |
// If command was "@echo -off" or "@echo -on" then don't restore echo state | |
// | |
if (StrCmp (L"@echo -off", CommandLine3) != 0 && | |
StrCmp (L"@echo -on", CommandLine3) != 0) { | |
// | |
// Now restore the pre-'@' echo state. | |
// | |
ShellCommandSetEchoState(PreCommandEchoState); | |
} | |
} else { | |
if (ShellCommandGetEchoState()) { | |
CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd"); | |
if (CurDir != NULL && StrLen(CurDir) > 1) { | |
ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir); | |
} else { | |
ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle); | |
} | |
ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2); | |
} | |
Status = RunCommand(CommandLine3, NULL); | |
} | |
} | |
if (ShellCommandGetScriptExit()) { | |
// | |
// ShellCommandGetExitCode() always returns a UINT64 | |
// | |
CalleeExitStatus = (SHELL_STATUS) ShellCommandGetExitCode(); | |
UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", CalleeExitStatus); | |
DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE);); | |
InternalEfiShellSetEnv(L"lasterror", LeString, TRUE); | |
ShellCommandRegisterExit(FALSE, 0); | |
Status = EFI_SUCCESS; | |
break; | |
} | |
if (ShellGetExecutionBreakFlag()) { | |
break; | |
} | |
if (EFI_ERROR(Status)) { | |
CalleeExitStatus = (SHELL_STATUS) Status; | |
break; | |
} | |
if (ShellCommandGetExit()) { | |
CalleeExitStatus = (SHELL_STATUS) ShellCommandGetExitCode(); | |
break; | |
} | |
} | |
// | |
// If that commend did not update the CurrentCommand then we need to advance it... | |
// | |
if (LastCommand == NewScriptFile->CurrentCommand) { | |
NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link); | |
if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) { | |
NewScriptFile->CurrentCommand->Reset = TRUE; | |
} | |
} | |
} else { | |
NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link); | |
if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) { | |
NewScriptFile->CurrentCommand->Reset = TRUE; | |
} | |
} | |
} | |
FreePool(CommandLine); | |
FreePool(CommandLine2); | |
ShellCommandSetNewScript (NULL); | |
// | |
// Only if this was the last script reset the state. | |
// | |
if (ShellCommandGetCurrentScriptFile()==NULL) { | |
ShellCommandSetEchoState(PreScriptEchoState); | |
} | |
if (ExitStatus != NULL) { | |
*ExitStatus = CalleeExitStatus; | |
} | |
return (EFI_SUCCESS); | |
} | |
/** | |
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 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); | |
} | |
// | |
// get the argc and argv updated for scripts | |
// | |
Status = UpdateArgcArgv(ParamProtocol, CmdLine, &Argv, &Argc); | |
if (!EFI_ERROR(Status)) { | |
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); | |
// | |
// 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); | |
} |