mirror of https://github.com/acidanthera/audk.git
3350 lines
102 KiB
C
3350 lines
102 KiB
C
/** @file
|
|
This is THE shell (application)
|
|
|
|
Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
|
|
(C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.<BR>
|
|
Copyright 2015-2018 Dell Technologies.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "Shell.h"
|
|
|
|
//
|
|
// Initialize the global structure
|
|
//
|
|
SHELL_INFO ShellInfoObject = {
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
FALSE,
|
|
{
|
|
{
|
|
{
|
|
0,
|
|
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";
|
|
CONST CHAR16 mNoNestingEnvVarName[] = L"nonesting";
|
|
CONST CHAR16 mNoNestingTrue[] = L"True";
|
|
CONST CHAR16 mNoNestingFalse[] = L"False";
|
|
|
|
/**
|
|
Cleans off leading and trailing spaces and tabs.
|
|
|
|
@param[in] String pointer to the string to trim them off.
|
|
**/
|
|
EFI_STATUS
|
|
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);
|
|
}
|
|
|
|
/**
|
|
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 *
|
|
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 don't 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);
|
|
}
|
|
|
|
/**
|
|
Check whether the string between a pair of % is a valid environment variable name.
|
|
|
|
@param[in] BeginPercent pointer to the first percent.
|
|
@param[in] EndPercent pointer to the last percent.
|
|
|
|
@retval TRUE is a valid environment variable name.
|
|
@retval FALSE is NOT a valid environment variable name.
|
|
**/
|
|
BOOLEAN
|
|
IsValidEnvironmentVariableName (
|
|
IN CONST CHAR16 *BeginPercent,
|
|
IN CONST CHAR16 *EndPercent
|
|
)
|
|
{
|
|
CONST CHAR16 *Walker;
|
|
|
|
Walker = NULL;
|
|
|
|
ASSERT (BeginPercent != NULL);
|
|
ASSERT (EndPercent != NULL);
|
|
ASSERT (BeginPercent < EndPercent);
|
|
|
|
if ((BeginPercent + 1) == EndPercent) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (Walker = BeginPercent + 1; Walker < EndPercent; Walker++) {
|
|
if (
|
|
((*Walker >= L'0') && (*Walker <= L'9')) ||
|
|
((*Walker >= L'A') && (*Walker <= L'Z')) ||
|
|
((*Walker >= L'a') && (*Walker <= L'z')) ||
|
|
(*Walker == L'_')
|
|
)
|
|
{
|
|
if ((Walker == BeginPercent + 1) && ((*Walker >= L'0') && (*Walker <= L'9'))) {
|
|
return FALSE;
|
|
} else {
|
|
continue;
|
|
}
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
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
|
|
ContainsSplit (
|
|
IN CONST CHAR16 *CmdLine
|
|
)
|
|
{
|
|
CONST CHAR16 *TempSpot;
|
|
CONST CHAR16 *FirstQuote;
|
|
CONST CHAR16 *SecondQuote;
|
|
|
|
FirstQuote = FindNextInstance (CmdLine, L"\"", TRUE);
|
|
SecondQuote = NULL;
|
|
TempSpot = FindFirstCharacter (CmdLine, L"|", L'^');
|
|
|
|
if ((FirstQuote == NULL) ||
|
|
(TempSpot == NULL) ||
|
|
(TempSpot == CHAR_NULL) ||
|
|
(FirstQuote > TempSpot)
|
|
)
|
|
{
|
|
return (BOOLEAN)((TempSpot != NULL) && (*TempSpot != CHAR_NULL));
|
|
}
|
|
|
|
while ((TempSpot != NULL) && (*TempSpot != CHAR_NULL)) {
|
|
if ((FirstQuote == NULL) || (FirstQuote > TempSpot)) {
|
|
break;
|
|
}
|
|
|
|
SecondQuote = FindNextInstance (FirstQuote + 1, L"\"", TRUE);
|
|
if (SecondQuote == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (SecondQuote < TempSpot) {
|
|
FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);
|
|
continue;
|
|
} else {
|
|
FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);
|
|
TempSpot = FindFirstCharacter (TempSpot + 1, L"|", L'^');
|
|
continue;
|
|
}
|
|
}
|
|
|
|
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 memory available.
|
|
**/
|
|
EFI_STATUS
|
|
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;
|
|
SPLIT_LIST *Split;
|
|
|
|
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)) {
|
|
//
|
|
// 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);
|
|
|
|
Status = ShellInitEnvVarList ();
|
|
|
|
//
|
|
// 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 ();
|
|
}
|
|
|
|
//
|
|
// Set the environment variable for nesting support
|
|
//
|
|
Size = 0;
|
|
TempString = NULL;
|
|
if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) {
|
|
//
|
|
// No change. require nesting in Shell Protocol Execute()
|
|
//
|
|
StrnCatGrow (
|
|
&TempString,
|
|
&Size,
|
|
L"False",
|
|
0
|
|
);
|
|
} else {
|
|
StrnCatGrow (
|
|
&TempString,
|
|
&Size,
|
|
mNoNestingTrue,
|
|
0
|
|
);
|
|
}
|
|
|
|
Status = InternalEfiShellSetEnv (mNoNestingEnvVarName, TempString, TRUE);
|
|
SHELL_FREE_NON_NULL (TempString);
|
|
Size = 0;
|
|
|
|
//
|
|
// 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");
|
|
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);
|
|
}
|
|
|
|
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;
|
|
|
|
//
|
|
// Display Prompt
|
|
//
|
|
Status = DoShellPrompt ();
|
|
} while (!ShellCommandGetExit ());
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
CleanUpShellEnvironment (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 (free SplitStdIn/Out when needed).
|
|
|
|
for ( Split = (SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link)
|
|
; !IsNull (&ShellInfoObject.SplitList.Link, &Split->Link)
|
|
; Split = (SPLIT_LIST *)GetNextNode (&ShellInfoObject.SplitList.Link, &Split->Link)
|
|
)
|
|
{
|
|
RemoveEntryList (&Split->Link);
|
|
FreePool (Split);
|
|
}
|
|
|
|
DEBUG_CODE (
|
|
InitializeListHead (&ShellInfoObject.SplitList.Link);
|
|
);
|
|
}
|
|
|
|
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;
|
|
);
|
|
}
|
|
|
|
ShellFreeEnvVarList ();
|
|
|
|
if (ShellCommandGetExit ()) {
|
|
return ((EFI_STATUS)ShellCommandGetExitCode ());
|
|
}
|
|
|
|
return (Status);
|
|
}
|
|
|
|
/**
|
|
Sets all the alias' that were registered with the ShellCommandLib library.
|
|
|
|
@retval EFI_SUCCESS all init commands were run successfully.
|
|
**/
|
|
EFI_STATUS
|
|
SetBuiltInAlias (
|
|
VOID
|
|
)
|
|
{
|
|
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
|
|
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
|
|
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 successful return the device path to the loaded image.
|
|
@param[in, out] FilePath On a successful return the device path to the file.
|
|
|
|
@retval EFI_SUCCESS The 2 device paths were successfully returned.
|
|
@retval other A error from gBS->HandleProtocol.
|
|
|
|
@sa HandleProtocol
|
|
**/
|
|
EFI_STATUS
|
|
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
|
|
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 (
|
|
&gEfiUnicodeCollation2ProtocolGuid,
|
|
NULL,
|
|
(VOID **)&UnicodeCollation
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
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.BitUnion.Bits.NoNest = FALSE;
|
|
ShellInfoObject.ShellInitSettings.Delay = PcdGet32 (PcdShellDefaultDelay);
|
|
|
|
//
|
|
// 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"-nonest",
|
|
CurrentArg
|
|
) == 0)
|
|
{
|
|
ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = TRUE;
|
|
} else if (UnicodeCollation->StriColl (
|
|
UnicodeCollation,
|
|
L"-delay",
|
|
CurrentArg
|
|
) == 0)
|
|
{
|
|
ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = TRUE;
|
|
// Check for optional delay value following "-delay"
|
|
if ((LoopVar + 1) >= gEfiShellParametersProtocol->Argc) {
|
|
DelayValueStr = NULL;
|
|
} else {
|
|
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) {
|
|
// Unrecognized 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 = NULL;
|
|
Size = 0;
|
|
//
|
|
// If first argument contains a space, then add double quotes before the argument
|
|
//
|
|
if (StrStr (CurrentArg, L" ") != NULL) {
|
|
StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0);
|
|
if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
|
|
return (EFI_OUT_OF_RESOURCES);
|
|
}
|
|
}
|
|
|
|
StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, CurrentArg, 0);
|
|
if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
|
|
return (EFI_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// If first argument contains a space, then add double quotes after the argument
|
|
//
|
|
if (StrStr (CurrentArg, L" ") != NULL) {
|
|
StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0);
|
|
if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
|
|
return (EFI_OUT_OF_RESOURCES);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We found `file-name`.
|
|
//
|
|
ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;
|
|
LoopVar++;
|
|
|
|
// Add `file-name-options`
|
|
for (Size = 0; LoopVar < gEfiShellParametersProtocol->Argc; LoopVar++) {
|
|
ASSERT ((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));
|
|
//
|
|
// Add a space between arguments
|
|
//
|
|
if (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);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If an argument contains a space, then add double quotes before the argument
|
|
//
|
|
if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != 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);
|
|
}
|
|
|
|
//
|
|
// If an argument contains a space, then add double quotes after the argument
|
|
//
|
|
if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// "-nointerrupt" overrides "-delay"
|
|
if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
|
|
ShellInfoObject.ShellInitSettings.Delay = 0;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Function try to find location of the Startup.nsh file.
|
|
|
|
The buffer is callee allocated and should be freed by the caller.
|
|
|
|
@param ImageDevicePath The path to the image for shell. first place to look for the startup script
|
|
@param FileDevicePath The path to the file for shell. second place to look for the startup script.
|
|
|
|
@retval NULL No Startup.nsh file was found.
|
|
@return !=NULL Pointer to NULL-terminated path.
|
|
**/
|
|
CHAR16 *
|
|
LocateStartupScript (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *FileDevicePath
|
|
)
|
|
{
|
|
CHAR16 *StartupScriptPath;
|
|
CHAR16 *TempSpot;
|
|
CONST CHAR16 *MapName;
|
|
UINTN Size;
|
|
|
|
StartupScriptPath = NULL;
|
|
Size = 0;
|
|
|
|
//
|
|
// Try to find 'Startup.nsh' in the directory where the shell itself was launched.
|
|
//
|
|
MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath (&ImageDevicePath);
|
|
if (MapName != NULL) {
|
|
StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, MapName, 0);
|
|
if (StartupScriptPath == NULL) {
|
|
//
|
|
// Do not locate the startup script in sys path when out of resource.
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
TempSpot = StrStr (StartupScriptPath, L";");
|
|
if (TempSpot != NULL) {
|
|
*TempSpot = CHAR_NULL;
|
|
}
|
|
|
|
InternalEfiShellSetEnv (L"homefilesystem", StartupScriptPath, TRUE);
|
|
|
|
StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, ((FILEPATH_DEVICE_PATH *)FileDevicePath)->PathName, 0);
|
|
PathRemoveLastItem (StartupScriptPath);
|
|
StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, mStartupScript, 0);
|
|
}
|
|
|
|
//
|
|
// Try to find 'Startup.nsh' in the execution path defined by the environment variable PATH.
|
|
//
|
|
if ((StartupScriptPath == NULL) || EFI_ERROR (ShellIsFile (StartupScriptPath))) {
|
|
SHELL_FREE_NON_NULL (StartupScriptPath);
|
|
StartupScriptPath = ShellFindFilePath (mStartupScript);
|
|
}
|
|
|
|
return StartupScriptPath;
|
|
}
|
|
|
|
/**
|
|
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.
|
|
|
|
@retval EFI_SUCCESS the variable is initialized.
|
|
**/
|
|
EFI_STATUS
|
|
DoStartupScript (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *ImagePath,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *FilePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_STATUS CalleeStatus;
|
|
UINTN Delay;
|
|
EFI_INPUT_KEY Key;
|
|
CHAR16 *FileStringPath;
|
|
CHAR16 *FullFileStringPath;
|
|
UINTN NewSize;
|
|
|
|
CalleeStatus = EFI_SUCCESS;
|
|
Key.UnicodeChar = CHAR_NULL;
|
|
Key.ScanCode = 0;
|
|
|
|
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);
|
|
}
|
|
|
|
StrCpyS (FileStringPath, NewSize/sizeof (CHAR16), ShellInfoObject.ShellInitSettings.FileName);
|
|
if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
|
|
StrnCatS (FileStringPath, NewSize/sizeof (CHAR16), L" ", NewSize/sizeof (CHAR16) - StrLen (FileStringPath) -1);
|
|
StrnCatS (FileStringPath, NewSize/sizeof (CHAR16), ShellInfoObject.ShellInitSettings.FileOptions, NewSize/sizeof (CHAR16) - StrLen (FileStringPath) -1);
|
|
}
|
|
|
|
Status = RunShellCommand (FileStringPath, &CalleeStatus);
|
|
if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit == TRUE) {
|
|
ShellCommandRegisterExit (gEfiShellProtocol->BatchIsActive (), (UINT64)CalleeStatus);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
FileStringPath = LocateStartupScript (ImagePath, FilePath);
|
|
if (FileStringPath != NULL) {
|
|
FullFileStringPath = FullyQualifyPath (FileStringPath);
|
|
if (FullFileStringPath == NULL) {
|
|
Status = RunScriptFile (FileStringPath, NULL, FileStringPath, ShellInfoObject.NewShellParametersProtocol);
|
|
} else {
|
|
Status = RunScriptFile (FullFileStringPath, NULL, FullFileStringPath, ShellInfoObject.NewShellParametersProtocol);
|
|
FreePool (FullFileStringPath);
|
|
}
|
|
|
|
FreePool (FileStringPath);
|
|
} else {
|
|
//
|
|
// we return success since startup script is not mandatory.
|
|
//
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
|
|
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
|
|
DoShellPrompt (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN Column;
|
|
UINTN Row;
|
|
CHAR16 *CmdLine;
|
|
CONST CHAR16 *CurDir;
|
|
UINTN BufferSize;
|
|
EFI_STATUS Status;
|
|
LIST_ENTRY OldBufferList;
|
|
|
|
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;
|
|
}
|
|
|
|
SaveBufferList (&OldBufferList);
|
|
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)) {
|
|
//
|
|
// Reset the CTRL-C event just before running the command (yes we ignore the return values)
|
|
//
|
|
Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);
|
|
|
|
CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
|
|
Status = RunCommand (CmdLine);
|
|
}
|
|
|
|
//
|
|
// Done with this command
|
|
//
|
|
RestoreBufferList (&OldBufferList);
|
|
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 *
|
|
AddBufferToFreeList (
|
|
VOID *Buffer
|
|
)
|
|
{
|
|
BUFFER_LIST *BufferListEntry;
|
|
|
|
if (Buffer == NULL) {
|
|
return (NULL);
|
|
}
|
|
|
|
BufferListEntry = AllocateZeroPool (sizeof (BUFFER_LIST));
|
|
if (BufferListEntry == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
BufferListEntry->Buffer = Buffer;
|
|
InsertTailList (&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);
|
|
return (Buffer);
|
|
}
|
|
|
|
/**
|
|
Create a new buffer list and stores the old one to OldBufferList
|
|
|
|
@param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
|
|
**/
|
|
VOID
|
|
SaveBufferList (
|
|
OUT LIST_ENTRY *OldBufferList
|
|
)
|
|
{
|
|
CopyMem (OldBufferList, &ShellInfoObject.BufferToFreeList.Link, sizeof (LIST_ENTRY));
|
|
InitializeListHead (&ShellInfoObject.BufferToFreeList.Link);
|
|
}
|
|
|
|
/**
|
|
Restore previous nodes into BufferToFreeList .
|
|
|
|
@param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
|
|
**/
|
|
VOID
|
|
RestoreBufferList (
|
|
IN OUT LIST_ENTRY *OldBufferList
|
|
)
|
|
{
|
|
FreeBufferList (&ShellInfoObject.BufferToFreeList);
|
|
CopyMem (&ShellInfoObject.BufferToFreeList.Link, OldBufferList, sizeof (LIST_ENTRY));
|
|
}
|
|
|
|
/**
|
|
Add a buffer to the Line History List
|
|
|
|
@param Buffer The line buffer to add.
|
|
**/
|
|
VOID
|
|
AddLineToCommandHistory (
|
|
IN CONST CHAR16 *Buffer
|
|
)
|
|
{
|
|
BUFFER_LIST *Node;
|
|
BUFFER_LIST *Walker;
|
|
UINT16 MaxHistoryCmdCount;
|
|
UINT16 Count;
|
|
|
|
Count = 0;
|
|
MaxHistoryCmdCount = PcdGet16 (PcdShellMaxHistoryCommandCount);
|
|
|
|
if (MaxHistoryCmdCount == 0) {
|
|
return;
|
|
}
|
|
|
|
Node = AllocateZeroPool (sizeof (BUFFER_LIST));
|
|
if (Node == NULL) {
|
|
return;
|
|
}
|
|
|
|
Node->Buffer = AllocateCopyPool (StrSize (Buffer), Buffer);
|
|
if (Node->Buffer == NULL) {
|
|
FreePool (Node);
|
|
return;
|
|
}
|
|
|
|
for ( Walker = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link)
|
|
; !IsNull (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)
|
|
; Walker = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)
|
|
)
|
|
{
|
|
Count++;
|
|
}
|
|
|
|
if (Count < MaxHistoryCmdCount) {
|
|
InsertTailList (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
|
|
} else {
|
|
Walker = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link);
|
|
RemoveEntryList (&Walker->Link);
|
|
if (Walker->Buffer != NULL) {
|
|
FreePool (Walker->Buffer);
|
|
}
|
|
|
|
FreePool (Walker);
|
|
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
|
|
ShellConvertAlias (
|
|
IN OUT CHAR16 **CommandString
|
|
)
|
|
{
|
|
CONST CHAR16 *NewString;
|
|
|
|
NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias (*CommandString, NULL);
|
|
if (NewString == NULL) {
|
|
return (EFI_SUCCESS);
|
|
}
|
|
|
|
FreePool (*CommandString);
|
|
*CommandString = AllocateCopyPool (StrSize (NewString), NewString);
|
|
if (*CommandString == NULL) {
|
|
return (EFI_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
return (EFI_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
This function will eliminate unreplaced (and therefore non-found) environment variables.
|
|
|
|
@param[in,out] CmdLine The command line to update.
|
|
**/
|
|
EFI_STATUS
|
|
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 don't have 2 % we are done.
|
|
//
|
|
break;
|
|
}
|
|
|
|
if ((FirstQuote != NULL) && (FirstQuote < FirstPercent)) {
|
|
SecondQuote = FindNextInstance (FirstQuote+1, L"\"", TRUE);
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
if ((FirstQuote == NULL) || (SecondPercent < FirstQuote)) {
|
|
if (IsValidEnvironmentVariableName (FirstPercent, SecondPercent)) {
|
|
//
|
|
// We need to remove from FirstPercent to SecondPercent
|
|
//
|
|
CopyMem (FirstPercent, SecondPercent + 1, StrSize (SecondPercent + 1));
|
|
//
|
|
// don't need to update the locator. both % characters are gone.
|
|
//
|
|
} else {
|
|
CurrentLocator = SecondPercent + 1;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
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 occurred.
|
|
@return The new command line with no environment variables present.
|
|
**/
|
|
CHAR16 *
|
|
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 preceding and if there is space no ^ preceding (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 preceding and following % and if there is space no ^ preceding (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);
|
|
}
|
|
|
|
CopyMem (NewCommandLine1, OriginalCommandLine, StrSize (OriginalCommandLine));
|
|
|
|
for (MasterEnvList = EfiShellGetEnv (NULL)
|
|
; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL
|
|
; MasterEnvList += StrLen (MasterEnvList) + 1
|
|
)
|
|
{
|
|
StrCpyS (
|
|
ItemTemp,
|
|
((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)),
|
|
L"%"
|
|
);
|
|
StrCatS (
|
|
ItemTemp,
|
|
((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)),
|
|
MasterEnvList
|
|
);
|
|
StrCatS (
|
|
ItemTemp,
|
|
((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)),
|
|
L"%"
|
|
);
|
|
ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv (MasterEnvList), TRUE, FALSE);
|
|
StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), 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);
|
|
StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), NewCommandLine2);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove non-existent environment variables
|
|
//
|
|
StripUnreplacedEnvironmentVariables (NewCommandLine1);
|
|
|
|
//
|
|
// Now cleanup any straggler intentionally ignored "%" characters
|
|
//
|
|
ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);
|
|
StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), 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.
|
|
|
|
@retval EFI_SUCCESS The split command is executed successfully.
|
|
@retval other Some error occurs when executing the split command.
|
|
**/
|
|
EFI_STATUS
|
|
RunSplitCommand (
|
|
IN CONST CHAR16 *CmdLine,
|
|
IN SHELL_FILE_HANDLE StdIn,
|
|
IN SHELL_FILE_HANDLE StdOut
|
|
)
|
|
{
|
|
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] == L'a') &&
|
|
((NextCommandLine[1] == L' ') || (NextCommandLine[1] == CHAR_NULL))
|
|
)
|
|
{
|
|
CopyMem (NextCommandLine, NextCommandLine+1, StrSize (NextCommandLine) - sizeof (NextCommandLine[0]));
|
|
while (NextCommandLine[0] == L' ') {
|
|
CopyMem (NextCommandLine, NextCommandLine+1, StrSize (NextCommandLine) - sizeof (NextCommandLine[0]));
|
|
}
|
|
|
|
if (NextCommandLine[0] == CHAR_NULL) {
|
|
SHELL_FREE_NON_NULL (OurCommandLine);
|
|
SHELL_FREE_NON_NULL (NextCommandLine);
|
|
return (EFI_INVALID_PARAMETER);
|
|
}
|
|
|
|
Unicode = FALSE;
|
|
} else {
|
|
Unicode = TRUE;
|
|
}
|
|
|
|
//
|
|
// make a SPLIT_LIST item and add to list
|
|
//
|
|
Split = AllocateZeroPool (sizeof (SPLIT_LIST));
|
|
if (Split == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Split->SplitStdIn = StdIn;
|
|
Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (Unicode), NULL);
|
|
ASSERT (Split->SplitStdOut != NULL);
|
|
InsertHeadList (&ShellInfoObject.SplitList.Link, &Split->Link);
|
|
|
|
Status = RunCommand (OurCommandLine);
|
|
|
|
//
|
|
// 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 (Split->SplitStdIn, 0);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = RunCommand (NextCommandLine);
|
|
}
|
|
|
|
//
|
|
// 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) {
|
|
ShellInfoObject.NewEfiShellProtocol->CloseFile (Split->SplitStdOut);
|
|
}
|
|
|
|
if (Split->SplitStdIn != NULL) {
|
|
ShellInfoObject.NewEfiShellProtocol->CloseFile (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
|
|
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
|
|
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
|
|
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
|
|
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;
|
|
if (!EFI_ERROR (GetNextParameter (&TempWalker, &FirstParameter, StrSize (CmdLine), TRUE))) {
|
|
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
|
|
VerifySplit (
|
|
IN CONST CHAR16 *CmdLine
|
|
)
|
|
{
|
|
CONST CHAR16 *TempSpot;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// If this was the only item, then get out
|
|
//
|
|
if (!ContainsSplit (CmdLine)) {
|
|
return (EFI_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// Verify up to the pipe or end character
|
|
//
|
|
Status = IsValidSplit (CmdLine);
|
|
if (EFI_ERROR (Status)) {
|
|
return (Status);
|
|
}
|
|
|
|
//
|
|
// recurse to verify the next item
|
|
//
|
|
TempSpot = FindFirstCharacter (CmdLine, L"|", L'^') + 1;
|
|
if ((*TempSpot == L'a') &&
|
|
((*(TempSpot + 1) == L' ') || (*(TempSpot + 1) == CHAR_NULL))
|
|
)
|
|
{
|
|
// If it's an ASCII pipe '|a'
|
|
TempSpot += 1;
|
|
}
|
|
|
|
return (VerifySplit (TempSpot));
|
|
}
|
|
|
|
/**
|
|
Process a split based operation.
|
|
|
|
@param[in] CmdLine pointer to the command line to process
|
|
|
|
@retval EFI_SUCCESS The operation was successful
|
|
@return an error occurred.
|
|
**/
|
|
EFI_STATUS
|
|
ProcessNewSplitCommandLine (
|
|
IN CONST CHAR16 *CmdLine
|
|
)
|
|
{
|
|
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);
|
|
} else {
|
|
Status = RunSplitCommand (CmdLine, Split->SplitStdIn, Split->SplitStdOut);
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);
|
|
}
|
|
|
|
return (Status);
|
|
}
|
|
|
|
/**
|
|
Handle a request to change the current file system.
|
|
|
|
@param[in] CmdLine The passed in command line.
|
|
|
|
@retval EFI_SUCCESS The operation was successful.
|
|
**/
|
|
EFI_STATUS
|
|
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
|
|
DoHelpUpdate (
|
|
IN OUT CHAR16 **CmdLine
|
|
)
|
|
{
|
|
CHAR16 *CurrentParameter;
|
|
CHAR16 *Walker;
|
|
CHAR16 *NewCommandLine;
|
|
EFI_STATUS Status;
|
|
UINTN NewCmdLineSize;
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
CurrentParameter = AllocateZeroPool (StrSize (*CmdLine));
|
|
if (CurrentParameter == NULL) {
|
|
return (EFI_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
Walker = *CmdLine;
|
|
while (Walker != NULL && *Walker != CHAR_NULL) {
|
|
if (!EFI_ERROR (GetNextParameter (&Walker, &CurrentParameter, StrSize (*CmdLine), TRUE))) {
|
|
if (StrStr (CurrentParameter, L"-?") == CurrentParameter) {
|
|
CurrentParameter[0] = L' ';
|
|
CurrentParameter[1] = L' ';
|
|
NewCmdLineSize = StrSize (L"help ") + StrSize (*CmdLine);
|
|
NewCommandLine = AllocateZeroPool (NewCmdLineSize);
|
|
if (NewCommandLine == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We know the space is sufficient since we just calculated it.
|
|
//
|
|
StrnCpyS (NewCommandLine, NewCmdLineSize/sizeof (CHAR16), L"help ", 5);
|
|
StrnCatS (NewCommandLine, NewCmdLineSize/sizeof (CHAR16), *CmdLine, StrLen (*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
|
|
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 its 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 occurred
|
|
**/
|
|
EFI_STATUS
|
|
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 update 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] CommandStatus the status from the command line.
|
|
|
|
@retval EFI_SUCCESS The command was completed.
|
|
@retval EFI_ABORTED The command's operation was aborted.
|
|
**/
|
|
EFI_STATUS
|
|
RunInternalCommand (
|
|
IN CONST CHAR16 *CmdLine,
|
|
IN CHAR16 *FirstParameter,
|
|
IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
|
|
OUT EFI_STATUS *CommandStatus
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Argc;
|
|
CHAR16 **Argv;
|
|
SHELL_STATUS CommandReturnedStatus;
|
|
BOOLEAN LastError;
|
|
CHAR16 *Walker;
|
|
CHAR16 *NewCmdLine;
|
|
|
|
NewCmdLine = AllocateCopyPool (StrSize (CmdLine), CmdLine);
|
|
if (NewCmdLine == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL; Walker++) {
|
|
if ((*Walker == L'^') && (*(Walker+1) == L'#')) {
|
|
CopyMem (Walker, Walker+1, StrSize (Walker) - sizeof (Walker[0]));
|
|
}
|
|
}
|
|
|
|
//
|
|
// get the argc and argv updated for internal commands
|
|
//
|
|
Status = UpdateArgcArgv (ParamProtocol, NewCmdLine, Internal_Command, &Argv, &Argc);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Run the internal command.
|
|
//
|
|
Status = ShellCommandRunCommandHandler (FirstParameter, &CommandReturnedStatus, &LastError);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
if (CommandStatus != NULL) {
|
|
if (CommandReturnedStatus != SHELL_SUCCESS) {
|
|
*CommandStatus = (EFI_STATUS)(CommandReturnedStatus | MAX_BIT);
|
|
} else {
|
|
*CommandStatus = EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update last error status.
|
|
// some commands do not update last error.
|
|
//
|
|
if (LastError) {
|
|
SetLastError (CommandReturnedStatus);
|
|
}
|
|
|
|
//
|
|
// Pass thru the exitcode from the app.
|
|
//
|
|
if (ShellCommandGetExit ()) {
|
|
//
|
|
// An Exit was requested ("exit" command), pass its value up.
|
|
//
|
|
Status = CommandReturnedStatus;
|
|
} else if ((CommandReturnedStatus != SHELL_SUCCESS) && IsScriptOnlyCommand (FirstParameter)) {
|
|
//
|
|
// Always abort when a script only command fails for any reason
|
|
//
|
|
Status = EFI_ABORTED;
|
|
} else if ((ShellCommandGetCurrentScriptFile () != NULL) && (CommandReturnedStatus == SHELL_ABORTED)) {
|
|
//
|
|
// Abort when in a script and a command aborted
|
|
//
|
|
Status = EFI_ABORTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// This is guaranteed 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 script 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;
|
|
}
|
|
|
|
FreePool (NewCmdLine);
|
|
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] CommandStatus the status from the command line.
|
|
|
|
@retval EFI_SUCCESS The command was completed.
|
|
@retval EFI_ABORTED The command's operation was aborted.
|
|
**/
|
|
EFI_STATUS
|
|
RunCommandOrFile (
|
|
IN SHELL_OPERATION_TYPES Type,
|
|
IN CONST CHAR16 *CmdLine,
|
|
IN CHAR16 *FirstParameter,
|
|
IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
|
|
OUT EFI_STATUS *CommandStatus
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_STATUS StartStatus;
|
|
CHAR16 *CommandWithPath;
|
|
CHAR16 *FullCommandWithPath;
|
|
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, CommandStatus);
|
|
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:
|
|
FullCommandWithPath = FullyQualifyPath (CommandWithPath);
|
|
if (FullCommandWithPath == NULL) {
|
|
Status = RunScriptFile (CommandWithPath, NULL, CmdLine, ParamProtocol);
|
|
} else {
|
|
Status = RunScriptFile (FullCommandWithPath, NULL, CmdLine, ParamProtocol);
|
|
FreePool (FullCommandWithPath);
|
|
}
|
|
|
|
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
|
|
);
|
|
|
|
SHELL_FREE_NON_NULL (DevPath);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
CalleeExitStatus = (SHELL_STATUS)(Status & (~MAX_BIT));
|
|
} else {
|
|
CalleeExitStatus = (SHELL_STATUS)StartStatus;
|
|
}
|
|
|
|
if (CommandStatus != NULL) {
|
|
*CommandStatus = CalleeExitStatus;
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
|
|
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] CommandStatus the status from the command line.
|
|
|
|
@retval EFI_SUCCESS The command was completed.
|
|
@retval EFI_ABORTED The command's operation was aborted.
|
|
**/
|
|
EFI_STATUS
|
|
SetupAndRunCommandOrFile (
|
|
IN SHELL_OPERATION_TYPES Type,
|
|
IN CHAR16 *CmdLine,
|
|
IN CHAR16 *FirstParameter,
|
|
IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
|
|
OUT EFI_STATUS *CommandStatus
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
SHELL_FILE_HANDLE OriginalStdIn;
|
|
SHELL_FILE_HANDLE OriginalStdOut;
|
|
SHELL_FILE_HANDLE OriginalStdErr;
|
|
SYSTEM_TABLE_INFO OriginalSystemTableInfo;
|
|
CONST SCRIPT_FILE *ConstScriptFile;
|
|
|
|
//
|
|
// 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)) {
|
|
TrimSpaces (&CmdLine);
|
|
Status = RunCommandOrFile (Type, CmdLine, FirstParameter, ParamProtocol, CommandStatus);
|
|
}
|
|
|
|
//
|
|
// Now print errors
|
|
//
|
|
if (EFI_ERROR (Status)) {
|
|
ConstScriptFile = ShellCommandGetCurrentScriptFile ();
|
|
if ((ConstScriptFile == NULL) || (ConstScriptFile->CurrentCommand == NULL)) {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID *)(Status));
|
|
} else {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR_SCRIPT), ShellInfoObject.HiiHandle, (VOID *)(Status), ConstScriptFile->CurrentCommand->Line);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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] CommandStatus The status from the command line.
|
|
|
|
@retval EFI_SUCCESS The command was completed.
|
|
@retval EFI_ABORTED The command's operation was aborted.
|
|
**/
|
|
EFI_STATUS
|
|
RunShellCommand (
|
|
IN CONST CHAR16 *CmdLine,
|
|
OUT EFI_STATUS *CommandStatus
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR16 *CleanOriginal;
|
|
CHAR16 *FirstParameter;
|
|
CHAR16 *TempWalker;
|
|
SHELL_OPERATION_TYPES Type;
|
|
CONST CHAR16 *CurDir;
|
|
|
|
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);
|
|
|
|
//
|
|
// NULL out comments (leveraged from RunScriptFileHandle() ).
|
|
// The # character on a line is used to denote that all characters on the same line
|
|
// and to the right of the # are to be ignored by the shell.
|
|
// Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.
|
|
//
|
|
for (TempWalker = CleanOriginal; TempWalker != NULL && *TempWalker != CHAR_NULL; TempWalker++) {
|
|
if (*TempWalker == L'^') {
|
|
if (*(TempWalker + 1) == L'#') {
|
|
TempWalker++;
|
|
}
|
|
} else if (*TempWalker == L'#') {
|
|
*TempWalker = CHAR_NULL;
|
|
}
|
|
}
|
|
|
|
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 don't do normal processing with a split command line (output from one command input to another)
|
|
//
|
|
if (ContainsSplit (CleanOriginal)) {
|
|
Status = ProcessNewSplitCommandLine (CleanOriginal);
|
|
SHELL_FREE_NON_NULL (CleanOriginal);
|
|
return (Status);
|
|
}
|
|
|
|
//
|
|
// We need the first parameter information so we can determine the operation type
|
|
//
|
|
FirstParameter = AllocateZeroPool (StrSize (CleanOriginal));
|
|
if (FirstParameter == NULL) {
|
|
SHELL_FREE_NON_NULL (CleanOriginal);
|
|
return (EFI_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
TempWalker = CleanOriginal;
|
|
if (!EFI_ERROR (GetNextParameter (&TempWalker, &FirstParameter, StrSize (CleanOriginal), TRUE))) {
|
|
//
|
|
// 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, CommandStatus);
|
|
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;
|
|
}
|
|
} else {
|
|
ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
|
|
SetLastError (SHELL_NOT_FOUND);
|
|
}
|
|
|
|
//
|
|
// Check whether the current file system still exists. If not exist, we need update "cwd" and gShellCurMapping.
|
|
//
|
|
CurDir = EfiShellGetCurDir (NULL);
|
|
if (CurDir != NULL) {
|
|
if (EFI_ERROR (ShellFileExists (CurDir))) {
|
|
//
|
|
// EfiShellSetCurDir() cannot set current directory to NULL.
|
|
// EfiShellSetEnv() is not allowed to set the "cwd" variable.
|
|
// Only InternalEfiShellSetEnv () is allowed setting the "cwd" variable.
|
|
//
|
|
InternalEfiShellSetEnv (L"cwd", NULL, TRUE);
|
|
gShellCurMapping = NULL;
|
|
}
|
|
}
|
|
|
|
SHELL_FREE_NON_NULL (CleanOriginal);
|
|
SHELL_FREE_NON_NULL (FirstParameter);
|
|
|
|
return (Status);
|
|
}
|
|
|
|
/**
|
|
Function will process and run a command line.
|
|
|
|
This will determine if the command line represents an internal shell
|
|
command or dispatch an external application.
|
|
|
|
@param[in] CmdLine The command line to parse.
|
|
|
|
@retval EFI_SUCCESS The command was completed.
|
|
@retval EFI_ABORTED The command's operation was aborted.
|
|
**/
|
|
EFI_STATUS
|
|
RunCommand (
|
|
IN CONST CHAR16 *CmdLine
|
|
)
|
|
{
|
|
return (RunShellCommand (CmdLine, NULL));
|
|
}
|
|
|
|
/**
|
|
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.
|
|
|
|
@retval EFI_SUCCESS the script completed successfully
|
|
**/
|
|
EFI_STATUS
|
|
RunScriptFileHandle (
|
|
IN SHELL_FILE_HANDLE Handle,
|
|
IN CONST CHAR16 *Name
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
SCRIPT_FILE *NewScriptFile;
|
|
UINTN LoopVar;
|
|
UINTN PrintBuffSize;
|
|
CHAR16 *CommandLine;
|
|
CHAR16 *CommandLine2;
|
|
CHAR16 *CommandLine3;
|
|
SCRIPT_COMMAND_LIST *LastCommand;
|
|
BOOLEAN Ascii;
|
|
BOOLEAN PreScriptEchoState;
|
|
BOOLEAN PreCommandEchoState;
|
|
CONST CHAR16 *CurDir;
|
|
UINTN LineCount;
|
|
CHAR16 LeString[50];
|
|
LIST_ENTRY OldBufferList;
|
|
|
|
ASSERT (!ShellCommandGetScriptExit ());
|
|
|
|
PreScriptEchoState = ShellCommandGetEchoState ();
|
|
PrintBuffSize = PcdGet16 (PcdShellPrintBufferSize);
|
|
|
|
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);
|
|
}
|
|
|
|
//
|
|
// Put the full path of the script file into Argv[0] as required by section
|
|
// 3.6.2 of version 2.2 of the shell specification.
|
|
//
|
|
NewScriptFile->Argv[0] = StrnCatGrow (&NewScriptFile->Argv[0], NULL, NewScriptFile->ScriptName, 0);
|
|
for (LoopVar = 1; 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 (PrintBuffSize);
|
|
if (CommandLine == NULL) {
|
|
DeleteScriptFileStruct (NewScriptFile);
|
|
return (EFI_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
CommandLine2 = AllocateZeroPool (PrintBuffSize);
|
|
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);
|
|
StrnCpyS (
|
|
CommandLine2,
|
|
PrintBuffSize/sizeof (CHAR16),
|
|
NewScriptFile->CurrentCommand->Cl,
|
|
PrintBuffSize/sizeof (CHAR16) - 1
|
|
);
|
|
|
|
SaveBufferList (&OldBufferList);
|
|
|
|
//
|
|
// NULL out comments
|
|
//
|
|
for (CommandLine3 = CommandLine2; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL; CommandLine3++) {
|
|
if (*CommandLine3 == L'^') {
|
|
if ( *(CommandLine3+1) == L':') {
|
|
CopyMem (CommandLine3, CommandLine3+1, StrSize (CommandLine3) - sizeof (CommandLine3[0]));
|
|
} else if (*(CommandLine3+1) == L'#') {
|
|
CommandLine3++;
|
|
}
|
|
} 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.
|
|
//
|
|
StrnCpyS (
|
|
CommandLine,
|
|
PrintBuffSize/sizeof (CHAR16),
|
|
CommandLine2,
|
|
PrintBuffSize/sizeof (CHAR16) - 1
|
|
);
|
|
|
|
//
|
|
// 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, PrintBuffSize, L"%9", NewScriptFile->Argv[9], FALSE, FALSE);
|
|
ASSERT_EFI_ERROR (Status);
|
|
case 9:
|
|
Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%8", NewScriptFile->Argv[8], FALSE, FALSE);
|
|
ASSERT_EFI_ERROR (Status);
|
|
case 8:
|
|
Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%7", NewScriptFile->Argv[7], FALSE, FALSE);
|
|
ASSERT_EFI_ERROR (Status);
|
|
case 7:
|
|
Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%6", NewScriptFile->Argv[6], FALSE, FALSE);
|
|
ASSERT_EFI_ERROR (Status);
|
|
case 6:
|
|
Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%5", NewScriptFile->Argv[5], FALSE, FALSE);
|
|
ASSERT_EFI_ERROR (Status);
|
|
case 5:
|
|
Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%4", NewScriptFile->Argv[4], FALSE, FALSE);
|
|
ASSERT_EFI_ERROR (Status);
|
|
case 4:
|
|
Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%3", NewScriptFile->Argv[3], FALSE, FALSE);
|
|
ASSERT_EFI_ERROR (Status);
|
|
case 3:
|
|
Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%2", NewScriptFile->Argv[2], FALSE, FALSE);
|
|
ASSERT_EFI_ERROR (Status);
|
|
case 2:
|
|
Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%1", NewScriptFile->Argv[1], FALSE, FALSE);
|
|
ASSERT_EFI_ERROR (Status);
|
|
case 1:
|
|
Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%0", NewScriptFile->Argv[0], FALSE, FALSE);
|
|
ASSERT_EFI_ERROR (Status);
|
|
break;
|
|
case 0:
|
|
break;
|
|
}
|
|
}
|
|
|
|
Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%1", L"\"\"", FALSE, FALSE);
|
|
Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%2", L"\"\"", FALSE, FALSE);
|
|
Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%3", L"\"\"", FALSE, FALSE);
|
|
Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%4", L"\"\"", FALSE, FALSE);
|
|
Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%5", L"\"\"", FALSE, FALSE);
|
|
Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%6", L"\"\"", FALSE, FALSE);
|
|
Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%7", L"\"\"", FALSE, FALSE);
|
|
Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%8", L"\"\"", FALSE, FALSE);
|
|
Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%9", L"\"\"", FALSE, FALSE);
|
|
|
|
StrnCpyS (
|
|
CommandLine2,
|
|
PrintBuffSize/sizeof (CHAR16),
|
|
CommandLine,
|
|
PrintBuffSize/sizeof (CHAR16) - 1
|
|
);
|
|
|
|
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);
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
if (ShellCommandGetScriptExit ()) {
|
|
//
|
|
// ShellCommandGetExitCode() always returns a UINT64
|
|
//
|
|
UnicodeSPrint (LeString, sizeof (LeString), L"0x%Lx", ShellCommandGetExitCode ());
|
|
DEBUG_CODE (
|
|
InternalEfiShellSetEnv (L"debuglasterror", LeString, TRUE);
|
|
);
|
|
InternalEfiShellSetEnv (L"lasterror", LeString, TRUE);
|
|
|
|
ShellCommandRegisterExit (FALSE, 0);
|
|
Status = EFI_SUCCESS;
|
|
RestoreBufferList (&OldBufferList);
|
|
break;
|
|
}
|
|
|
|
if (ShellGetExecutionBreakFlag ()) {
|
|
RestoreBufferList (&OldBufferList);
|
|
break;
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
RestoreBufferList (&OldBufferList);
|
|
break;
|
|
}
|
|
|
|
if (ShellCommandGetExit ()) {
|
|
RestoreBufferList (&OldBufferList);
|
|
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;
|
|
}
|
|
}
|
|
|
|
RestoreBufferList (&OldBufferList);
|
|
}
|
|
|
|
FreePool (CommandLine);
|
|
FreePool (CommandLine2);
|
|
ShellCommandSetNewScript (NULL);
|
|
|
|
//
|
|
// Only if this was the last script reset the state.
|
|
//
|
|
if (ShellCommandGetCurrentScriptFile () == NULL) {
|
|
ShellCommandSetEchoState (PreScriptEchoState);
|
|
}
|
|
|
|
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
|
|
|
|
@retval EFI_SUCCESS the script completed successfully
|
|
**/
|
|
EFI_STATUS
|
|
RunScriptFile (
|
|
IN CONST CHAR16 *ScriptPath,
|
|
IN SHELL_FILE_HANDLE Handle OPTIONAL,
|
|
IN CONST CHAR16 *CmdLine,
|
|
IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol
|
|
)
|
|
{
|
|
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, Script_File_Name, &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);
|
|
|
|
//
|
|
// now close the file
|
|
//
|
|
ShellCloseFile (&FileHandle);
|
|
}
|
|
} else {
|
|
Status = RunScriptFileHandle (Handle, ScriptPath);
|
|
}
|
|
}
|
|
|
|
//
|
|
// This is guaranteed 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);
|
|
}
|
|
|
|
/**
|
|
Return the pointer to the first occurrence of any character from a list of characters.
|
|
|
|
@param[in] String the string to parse
|
|
@param[in] CharacterList the list of character to look for
|
|
@param[in] EscapeCharacter An escape character to skip
|
|
|
|
@return the location of the first character in the string
|
|
@retval CHAR_NULL no instance of any character in CharacterList was found in String
|
|
**/
|
|
CONST CHAR16 *
|
|
FindFirstCharacter (
|
|
IN CONST CHAR16 *String,
|
|
IN CONST CHAR16 *CharacterList,
|
|
IN CONST CHAR16 EscapeCharacter
|
|
)
|
|
{
|
|
UINT32 WalkChar;
|
|
UINT32 WalkStr;
|
|
|
|
for (WalkStr = 0; WalkStr < StrLen (String); WalkStr++) {
|
|
if (String[WalkStr] == EscapeCharacter) {
|
|
WalkStr++;
|
|
continue;
|
|
}
|
|
|
|
for (WalkChar = 0; WalkChar < StrLen (CharacterList); WalkChar++) {
|
|
if (String[WalkStr] == CharacterList[WalkChar]) {
|
|
return (&String[WalkStr]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (String + StrLen (String));
|
|
}
|