diff options
Diffstat (limited to 'ShellPkg/Application/Shell/Shell.c')
| -rw-r--r-- | ShellPkg/Application/Shell/Shell.c | 6700 |
1 files changed, 3350 insertions, 3350 deletions
diff --git a/ShellPkg/Application/Shell/Shell.c b/ShellPkg/Application/Shell/Shell.c index 01b4e37871..15434bc026 100644 --- a/ShellPkg/Application/Shell/Shell.c +++ b/ShellPkg/Application/Shell/Shell.c @@ -1,3350 +1,3350 @@ -/** @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>
- Copyright (C) 2023, Apple Inc. All rights reserved.<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 = PcdGet32 (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));
-}
+/** @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> + Copyright (C) 2023, Apple Inc. All rights reserved.<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 = PcdGet32 (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)); +} |
