summaryrefslogtreecommitdiff
path: root/ShellPkg/Application/Shell/Shell.c
diff options
context:
space:
mode:
Diffstat (limited to 'ShellPkg/Application/Shell/Shell.c')
-rw-r--r--ShellPkg/Application/Shell/Shell.c1707
1 files changed, 1707 insertions, 0 deletions
diff --git a/ShellPkg/Application/Shell/Shell.c b/ShellPkg/Application/Shell/Shell.c
new file mode 100644
index 0000000000..3401bc0644
--- /dev/null
+++ b/ShellPkg/Application/Shell/Shell.c
@@ -0,0 +1,1707 @@
+/** @file
+ This is THE shell (application)
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Shell.h"
+
+//
+// Initialize the global structure
+//
+SHELL_INFO ShellInfoObject = {
+ NULL,
+ NULL,
+ FALSE,
+ FALSE,
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ NULL
+ },
+ {0,0},
+ {
+ {0,0},
+ 0,
+ 0,
+ TRUE
+ },
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ {0,0,NULL,NULL},
+ {0,0},
+};
+
+STATIC CONST CHAR16 mScriptExtension[] = L".NSH";
+STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";
+
+/**
+ 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_INPUT_KEY Key;
+
+// gST->ConOut->OutputString(gST->ConOut, L"ReadKeyStroke Calling\r\n");
+//
+// Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+//
+// gST->ConOut->OutputString(gST->ConOut, L"ReadKeyStroke Done\r\n");
+// gBS->Stall (1000000);
+
+ if (PcdGet8(PcdShellSupportLevel) > 3) {
+ return (EFI_UNSUPPORTED);
+ }
+
+ //
+ // Clear the screen
+ //
+ Status = gST->ConOut->ClearScreen(gST->ConOut);
+ ASSERT_EFI_ERROR(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);
+ ASSERT_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) {
+ return (EFI_NOT_STARTED);
+ }
+ }
+
+ //
+ // create and install the EfiShellParametersProtocol
+ //
+ Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);
+ ASSERT_EFI_ERROR(Status);
+ ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL);
+
+ //
+ // create and install the EfiShellProtocol
+ //
+ Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol);
+ ASSERT_EFI_ERROR(Status);
+ ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL);
+
+ //
+ // Now initialize the shell library (it requires Shell Parameters protocol)
+ //
+ Status = ShellInitialize();
+ ASSERT_EFI_ERROR(Status);
+
+ Status = CommandInit();
+ ASSERT_EFI_ERROR(Status);
+
+ //
+ // Check the command line
+ //
+ Status = ProcessCommandLine();
+
+ //
+ // If shell support level is >= 1 create the mappings and paths
+ //
+ if (PcdGet8(PcdShellSupportLevel) >= 1) {
+ Status = ShellCommandCreateInitialMappingsAndPaths();
+ }
+
+ //
+ // save the device path for the loaded image and the device path for the filepath (under loaded image)
+ // These are where to look for the startup.nsh file
+ //
+ Status = GetDevicePathsForImageAndFile(&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);
+ ASSERT_EFI_ERROR(Status);
+
+ //
+ // Display the version
+ //
+ if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {
+ ShellPrintHiiEx (
+ 0,
+ gST->ConOut->Mode->CursorRow,
+ NULL,
+ STRING_TOKEN (STR_VER_OUTPUT_MAIN),
+ ShellInfoObject.HiiHandle,
+ SupportLevel[PcdGet8(PcdShellSupportLevel)],
+ gEfiShellProtocol->MajorVersion,
+ gEfiShellProtocol->MinorVersion,
+ (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...
+ //
+
+ ///@todo add support for using SimpleInputEx here
+ // if SimpleInputEx is not available display a warning.
+ }
+
+ if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) {
+ //
+ // process the startup script or launch the called app.
+ //
+ Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
+ }
+
+ if ((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);
+ ShellInfoObject.ConsoleInfo->Enabled = TRUE;
+ ShellInfoObject.ConsoleInfo->RowCounter = 0;
+
+ //
+ // Display Prompt
+ //
+ Status = DoShellPrompt();
+ } while (!ShellCommandGetExit());
+ }
+ }
+ //
+ // uninstall protocols / free memory / etc...
+ //
+ if (ShellInfoObject.UserBreakTimer != NULL) {
+ gBS->CloseEvent(ShellInfoObject.UserBreakTimer);
+ DEBUG_CODE(ShellInfoObject.UserBreakTimer = NULL;);
+ }
+
+ if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){
+ ShellInfoObject.NewEfiShellProtocol->SetEnv(L"cwd", L"", TRUE);
+ }
+
+ 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){
+ CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol);
+ DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);
+ }
+
+ if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
+ FreeBufferList(&ShellInfoObject.BufferToFreeList);
+ }
+
+ if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){
+ ASSERT(FALSE);
+ }
+
+ 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;);
+ }
+
+ return (Status);
+}
+
+/**
+ Sets all the alias' that were registered with the ShellCommandLib library.
+
+ @retval EFI_SUCCESS all init commands were run sucessfully.
+**/
+EFI_STATUS
+EFIAPI
+SetBuiltInAlias(
+ )
+{
+ EFI_STATUS Status;
+ CONST ALIAS_LIST *List;
+ ALIAS_LIST *Node;
+
+ //
+ // Get all the commands we want to run
+ //
+ List = ShellCommandGetInitAliasList();
+
+ //
+ // for each command in the List
+ //
+ for ( Node = (ALIAS_LIST*)GetFirstNode(&List->Link)
+ ; !IsNull (&List->Link, &Node->Link)
+ ; Node = (ALIAS_LIST *)GetNextNode(&List->Link, &Node->Link)
+ ){
+ //
+ // install the alias'
+ //
+ Status = InternalSetAlias(Node->CommandString, Node->Alias, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ }
+ return (EFI_SUCCESS);
+}
+
+/**
+ Internal function to determine if 2 command names are really the same.
+
+ @param[in] Command1 The pointer to the first command name.
+ @param[in] Command2 The pointer to the second command name.
+
+ @retval TRUE The 2 command names are the same.
+ @retval FALSE The 2 command names are not the same.
+**/
+BOOLEAN
+EFIAPI
+IsCommand(
+ IN CONST CHAR16 *Command1,
+ IN CONST CHAR16 *Command2
+ )
+{
+ if (StringNoCaseCompare(&Command1, &Command2) == 0) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/**
+ Internal function to determine if a command is a script only command.
+
+ @param[in] CommandName The pointer to the command name.
+
+ @retval TRUE The command is a script only command.
+ @retval FALSE The command is not a script only command.
+**/
+BOOLEAN
+EFIAPI
+IsScriptOnlyCommand(
+ IN CONST CHAR16 *CommandName
+ )
+{
+ if (IsCommand(CommandName, L"for")
+ ||IsCommand(CommandName, L"endfor")
+ ||IsCommand(CommandName, L"if")
+ ||IsCommand(CommandName, L"else")
+ ||IsCommand(CommandName, L"endif")
+ ||IsCommand(CommandName, L"goto")) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+
+
+/**
+ This function will populate the 2 device path protocol parameters based on the
+ global gImageHandle. The DevPath will point to the device path for the handle that has
+ loaded image protocol installed on it. The FilePath will point to the device path
+ for the file that was loaded.
+
+ @param[in,out] DevPath On a sucessful return the device path to the loaded image.
+ @param[in,out] FilePath On a sucessful return the device path to the file.
+
+ @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
+ @retval other A error from gBS->HandleProtocol.
+
+ @sa HandleProtocol
+**/
+EFI_STATUS
+EFIAPI
+GetDevicePathsForImageAndFile (
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
+ EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;
+
+ ASSERT(DevPath != NULL);
+ ASSERT(FilePath != NULL);
+
+ Status = gBS->OpenProtocol (
+ gImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID**)&LoadedImage,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = gBS->OpenProtocol (
+ LoadedImage->DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID**)&ImageDevicePath,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ *DevPath = DuplicateDevicePath (ImageDevicePath);
+ *FilePath = DuplicateDevicePath (LoadedImage->FilePath);
+ }
+ gBS->CloseProtocol(
+ gImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ gImageHandle,
+ NULL);
+ }
+ return (Status);
+}
+
+STATIC CONST SHELL_PARAM_ITEM mShellParamList[] = {
+ {L"-nostartup", TypeFlag},
+ {L"-startup", TypeFlag},
+ {L"-noconsoleout", TypeFlag},
+ {L"-noconsolein", TypeFlag},
+ {L"-nointerrupt", TypeFlag},
+ {L"-nomap", TypeFlag},
+ {L"-noversion", TypeFlag},
+ {L"-startup", TypeFlag},
+ {L"-delay", TypeValue},
+ {NULL, TypeMax}
+ };
+/**
+ Process all Uefi Shell 2.0 command line options.
+
+ see Uefi Shell 2.0 section 3.2 for full details.
+
+ the command line must resemble the following:
+
+ shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
+
+ ShellOpt-options Options which control the initialization behavior of the shell.
+ These options are read from the EFI global variable "ShellOpt"
+ and are processed before options or file-name.
+
+ options Options which control the initialization behavior of the shell.
+
+ file-name The name of a UEFI shell application or script to be executed
+ after initialization is complete. By default, if file-name is
+ specified, then -nostartup is implied. Scripts are not supported
+ by level 0.
+
+ file-name-options The command-line options that are passed to file-name when it
+ is invoked.
+
+ This will initialize the ShellInfoObject.ShellInitSettings global variable.
+
+ @retval EFI_SUCCESS The variable is initialized.
+**/
+EFI_STATUS
+EFIAPI
+ProcessCommandLine(
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY *Package;
+ UINTN Size;
+ CONST CHAR16 *TempConst;
+ UINTN Count;
+ UINTN LoopVar;
+ CHAR16 *ProblemParam;
+
+ Package = NULL;
+ ProblemParam = NULL;
+
+ Status = ShellCommandLineParse (mShellParamList, &Package, NULL, FALSE);
+
+ Count = 1;
+ Size = 0;
+ TempConst = ShellCommandLineGetRawValue(Package, Count++);
+ if (TempConst != NULL && StrLen(TempConst)) {
+ ShellInfoObject.ShellInitSettings.FileName = AllocatePool(StrSize(TempConst));
+ StrCpy(ShellInfoObject.ShellInitSettings.FileName, TempConst);
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;
+ for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
+ if (StrCmp(gEfiShellParametersProtocol->Argv[LoopVar], ShellInfoObject.ShellInitSettings.FileName)==0) {
+ LoopVar++;
+ //
+ // We found the file... add the rest of the params...
+ //
+ for ( ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
+ ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));
+ StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
+ &Size,
+ L" ",
+ 0);
+ StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
+ &Size,
+ gEfiShellParametersProtocol->Argv[LoopVar],
+ 0);
+ }
+ }
+ }
+ } else {
+ ShellCommandLineFreeVarList(Package);
+ Package = NULL;
+ Status = ShellCommandLineParse (mShellParamList, &Package, &ProblemParam, FALSE);
+ if (EFI_ERROR(Status)) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), ShellInfoObject.HiiHandle, ProblemParam);
+ FreePool(ProblemParam);
+ ShellCommandLineFreeVarList(Package);
+ return (EFI_INVALID_PARAMETER);
+ }
+ }
+
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = ShellCommandLineGetFlag(Package, L"-startup");
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = ShellCommandLineGetFlag(Package, L"-nostartup");
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = ShellCommandLineGetFlag(Package, L"-noconsoleout");
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = ShellCommandLineGetFlag(Package, L"-noconsolein");
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = ShellCommandLineGetFlag(Package, L"-nointerrupt");
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = ShellCommandLineGetFlag(Package, L"-nomap");
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = ShellCommandLineGetFlag(Package, L"-noversion");
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = ShellCommandLineGetFlag(Package, L"-delay");
+
+ if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay) {
+ TempConst = ShellCommandLineGetValue(Package, L"-delay");
+ if (TempConst != NULL) {
+ ShellInfoObject.ShellInitSettings.Delay = StrDecimalToUintn (TempConst);
+ } else {
+ ShellInfoObject.ShellInitSettings.Delay = 5;
+ }
+ } else {
+ ShellInfoObject.ShellInitSettings.Delay = 5;
+ }
+
+ ShellCommandLineFreeVarList(Package);
+
+ return (Status);
+}
+
+/**
+ 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
+EFIAPI
+DoStartupScript(
+ EFI_DEVICE_PATH_PROTOCOL *ImagePath,
+ EFI_DEVICE_PATH_PROTOCOL *FilePath
+ )
+{
+ EFI_STATUS Status;
+ UINTN Delay;
+ EFI_INPUT_KEY Key;
+ SHELL_FILE_HANDLE FileHandle;
+ EFI_DEVICE_PATH_PROTOCOL *NewPath;
+ EFI_DEVICE_PATH_PROTOCOL *NamePath;
+ CHAR16 *FileStringPath;
+ UINTN NewSize;
+
+ Key.UnicodeChar = CHAR_NULL;
+ Key.ScanCode = 0;
+ FileHandle = NULL;
+
+ if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) {
+ //
+ // launch something else instead
+ //
+ NewSize = StrSize(ShellInfoObject.ShellInitSettings.FileName);
+ if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
+ NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16);
+ }
+ FileStringPath = AllocateZeroPool(NewSize);
+ StrCpy(FileStringPath, ShellInfoObject.ShellInitSettings.FileName);
+ if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
+ StrCat (FileStringPath, L" ");
+ StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);
+ }
+ Status = RunCommand(FileStringPath);
+ 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);
+ }
+
+ //
+ // print out our warning and see if they press a key
+ //
+ for ( Status = EFI_UNSUPPORTED, Delay = (ShellInfoObject.ShellInitSettings.Delay * 10)
+ ; Delay > 0 && EFI_ERROR(Status)
+ ; Delay--
+ ){
+ ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay/10);
+ gBS->Stall (100000);
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+ }
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);
+
+ //
+ // ESC was pressed
+ //
+ if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) {
+ return (EFI_SUCCESS);
+ }
+
+ NamePath = FileDevicePath (NULL, L"startup.nsh");
+ //
+ // Try the first location
+ //
+ NewPath = AppendDevicePathNode (ImagePath, NamePath);
+ Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);
+ if (EFI_ERROR(Status)) {
+ //
+ // Try the second location
+ //
+ FreePool(NewPath);
+ NewPath = AppendDevicePathNode (FilePath , NamePath);
+ Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);
+ }
+
+ //
+ // If we got a file, run it
+ //
+ if (!EFI_ERROR(Status)) {
+ Status = RunScriptFileHandle (FileHandle, L"startup.nsh");
+ ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
+ } else {
+ //
+ // we return success since we dont need to have a startup script
+ //
+ Status = EFI_SUCCESS;
+ ASSERT(FileHandle == NULL);
+ }
+
+ FreePool(NamePath);
+ FreePool(NewPath);
+
+ return (Status);
+}
+
+/**
+ Function to perform the shell prompt looping. It will do a single prompt,
+ dispatch the result, and then return. It is expected that the caller will
+ call this function in a loop many times.
+
+ @retval EFI_SUCCESS
+ @retval RETURN_ABORTED
+**/
+EFI_STATUS
+EFIAPI
+DoShellPrompt (
+ VOID
+ )
+{
+ UINTN Column;
+ UINTN Row;
+ CHAR16 *CmdLine;
+ CONST CHAR16 *CurDir;
+ UINTN BufferSize;
+ EFI_STATUS Status;
+
+ CurDir = NULL;
+
+ //
+ // Get screen setting to decide size of the command line buffer
+ //
+ gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);
+ BufferSize = Column * Row * sizeof (CHAR16);
+ CmdLine = AllocateZeroPool (BufferSize);
+ if (CmdLine == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
+
+ //
+ // Prompt for input
+ //
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);
+
+ if (CurDir != NULL && StrLen(CurDir) > 1) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
+ } else {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
+ }
+
+ //
+ // Read a line from the console
+ //
+ Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);
+
+ //
+ // Null terminate the string and parse it
+ //
+ if (!EFI_ERROR (Status)) {
+ CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
+ Status = RunCommand(CmdLine);
+ }
+
+ //
+ // Done with this command
+ //
+ FreePool (CmdLine);
+ return Status;
+}
+
+/**
+ Add a buffer to the Buffer To Free List for safely returning buffers to other
+ places without risking letting them modify internal shell information.
+
+ @param Buffer Something to pass to FreePool when the shell is exiting.
+**/
+VOID*
+EFIAPI
+AddBufferToFreeList(
+ VOID *Buffer
+ )
+{
+ BUFFER_LIST *BufferListEntry;
+
+ if (Buffer == NULL) {
+ return (NULL);
+ }
+
+ BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST));
+ ASSERT(BufferListEntry != NULL);
+ BufferListEntry->Buffer = Buffer;
+ InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);
+ return (Buffer);
+}
+
+/**
+ Add a buffer to the Line History List
+
+ @param Buffer The line buffer to add.
+**/
+VOID
+EFIAPI
+AddLineToCommandHistory(
+ IN CONST CHAR16 *Buffer
+ )
+{
+ BUFFER_LIST *Node;
+
+ Node = AllocateZeroPool(sizeof(BUFFER_LIST));
+ ASSERT(Node != NULL);
+ Node->Buffer = AllocateZeroPool(StrSize(Buffer));
+ ASSERT(Node->Buffer != NULL);
+ StrCpy(Node->Buffer, Buffer);
+
+ InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
+}
+
+/**
+ Checks if a string is an alias for another command. If yes, then it replaces the alias name
+ with the correct command name.
+
+ @param[in,out] CommandString Upon entry the potential alias. Upon return the
+ command name if it was an alias. If it was not
+ an alias it will be unchanged. This function may
+ change the buffer to fit the command name.
+
+ @retval EFI_SUCCESS The name was changed.
+ @retval EFI_SUCCESS The name was not an alias.
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
+**/
+EFI_STATUS
+EFIAPI
+ShellConvertAlias(
+ IN OUT CHAR16 **CommandString
+ )
+{
+ CONST CHAR16 *NewString;
+
+ NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);
+ if (NewString == NULL) {
+ return (EFI_SUCCESS);
+ }
+ FreePool(*CommandString);
+ *CommandString = AllocatePool(StrSize(NewString));
+ if (*CommandString == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ StrCpy(*CommandString, NewString);
+ return (EFI_SUCCESS);
+}
+
+/**
+ Function allocates a new command line and replaces all instances of environment
+ variable names that are correctly preset to their values.
+
+ If the return value is not NULL the memory must be caller freed.
+
+ @param[in] OriginalCommandLine The original command line
+
+ @retval NULL An error ocurred.
+ @return The new command line with no environment variables present.
+**/
+CHAR16*
+EFIAPI
+ShellConvertVariables (
+ IN CONST CHAR16 *OriginalCommandLine
+ )
+{
+ CONST CHAR16 *MasterEnvList;
+ UINTN NewSize;
+ CHAR16 *NewCommandLine1;
+ CHAR16 *NewCommandLine2;
+ CHAR16 *Temp;
+ UINTN ItemSize;
+ CHAR16 *ItemTemp;
+ SCRIPT_FILE *CurrentScriptFile;
+ ALIAS_LIST *AliasListNode;
+
+ ASSERT(OriginalCommandLine != NULL);
+
+ ItemSize = 0;
+ NewSize = StrSize(OriginalCommandLine);
+ CurrentScriptFile = ShellCommandGetCurrentScriptFile();
+ Temp = NULL;
+
+ ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
+
+ //
+ // calculate the size required for the post-conversion string...
+ //
+ if (CurrentScriptFile != NULL) {
+ for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
+ ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
+ ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
+ ){
+ for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)
+ ; Temp != NULL
+ ; Temp = StrStr(Temp+1, AliasListNode->Alias)
+ ){
+ //
+ // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
+ //
+ if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {
+ NewSize += StrSize(AliasListNode->CommandString);
+ }
+ }
+ }
+ }
+
+ for (MasterEnvList = EfiShellGetEnv(NULL)
+ ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
+ ; MasterEnvList += StrLen(MasterEnvList) + 1
+ ){
+ if (StrSize(MasterEnvList) > ItemSize) {
+ ItemSize = StrSize(MasterEnvList);
+ }
+ for (Temp = StrStr(OriginalCommandLine, MasterEnvList)
+ ; Temp != NULL
+ ; Temp = StrStr(Temp+1, MasterEnvList)
+ ){
+ //
+ // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
+ //
+ if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&
+ ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {
+ NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));
+ }
+ }
+ }
+
+ //
+ // Quick out if none were found...
+ //
+ if (NewSize == StrSize(OriginalCommandLine)) {
+ ASSERT(Temp == NULL);
+ Temp = StrnCatGrow(&Temp, NULL, OriginalCommandLine, 0);
+ return (Temp);
+ }
+
+ //
+ // now do the replacements...
+ //
+ NewCommandLine1 = AllocateZeroPool(NewSize);
+ NewCommandLine2 = AllocateZeroPool(NewSize);
+ ItemTemp = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));
+ StrCpy(NewCommandLine1, OriginalCommandLine);
+ for (MasterEnvList = EfiShellGetEnv(NULL)
+ ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
+ ; MasterEnvList += StrLen(MasterEnvList) + 1
+ ){
+ StrCpy(ItemTemp, L"%");
+ StrCat(ItemTemp, MasterEnvList);
+ StrCat(ItemTemp, L"%");
+ ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);
+ StrCpy(NewCommandLine1, NewCommandLine2);
+ }
+ if (CurrentScriptFile != NULL) {
+ for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
+ ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
+ ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
+ ){
+ ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);
+ StrCpy(NewCommandLine1, NewCommandLine2);
+ }
+ }
+
+ 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
+EFIAPI
+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[0] != CHAR_NULL &&
+ NextCommandLine[0] == L'a' &&
+ NextCommandLine[1] == L' '
+ ){
+ CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));
+ Unicode = FALSE;
+ } else {
+ Unicode = TRUE;
+ }
+
+
+ //
+ // make a SPLIT_LIST item and add to list
+ //
+ Split = AllocateZeroPool(sizeof(SPLIT_LIST));
+ ASSERT(Split != NULL);
+ Split->SplitStdIn = StdIn;
+ Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);
+ ASSERT(Split->SplitStdOut != NULL);
+ InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);
+
+ ASSERT(StrStr(OurCommandLine, L"|") == NULL);
+ 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(ConvertShellHandleToEfiFileProtocol(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 && Split->SplitStdOut != StdIn) {
+ ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));
+ }
+ if (Split->SplitStdIn != NULL) {
+ ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));
+ }
+
+ FreePool(Split);
+ FreePool(NextCommandLine);
+ FreePool(OurCommandLine);
+
+ return (Status);
+}
+
+/**
+ 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
+EFIAPI
+RunCommand(
+ IN CONST CHAR16 *CmdLine
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *CommandName;
+ SHELL_STATUS ShellStatus;
+ UINTN Argc;
+ CHAR16 **Argv;
+ BOOLEAN LastError;
+ CHAR16 LeString[11];
+ CHAR16 *PostAliasCmdLine;
+ UINTN PostAliasSize;
+ CHAR16 *PostVariableCmdLine;
+ CHAR16 *CommandWithPath;
+ EFI_DEVICE_PATH_PROTOCOL *DevPath;
+ CONST CHAR16 *TempLocation;
+ CONST CHAR16 *TempLocation2;
+ SHELL_FILE_HANDLE OriginalStdIn;
+ SHELL_FILE_HANDLE OriginalStdOut;
+ SHELL_FILE_HANDLE OriginalStdErr;
+ CHAR16 *TempLocation3;
+ UINTN Count;
+ UINTN Count2;
+ CHAR16 *CleanOriginal;
+ SPLIT_LIST *Split;
+
+ ASSERT(CmdLine != NULL);
+ if (StrLen(CmdLine) == 0) {
+ return (EFI_SUCCESS);
+ }
+
+ CommandName = NULL;
+ PostVariableCmdLine = NULL;
+ PostAliasCmdLine = NULL;
+ CommandWithPath = NULL;
+ DevPath = NULL;
+ Status = EFI_SUCCESS;
+ CleanOriginal = NULL;
+ Split = NULL;
+
+ CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);
+ while (CleanOriginal[StrLen(CleanOriginal)-1] == L' ') {
+ CleanOriginal[StrLen(CleanOriginal)-1] = CHAR_NULL;
+ }
+ while (CleanOriginal[0] == L' ') {
+ CopyMem(CleanOriginal, CleanOriginal+1, StrSize(CleanOriginal) - sizeof(CleanOriginal[0]));
+ }
+
+ CommandName = NULL;
+ if (StrStr(CleanOriginal, L" ") == NULL){
+ StrnCatGrow(&CommandName, NULL, CleanOriginal, 0);
+ } else {
+ StrnCatGrow(&CommandName, NULL, CleanOriginal, StrStr(CleanOriginal, L" ") - CleanOriginal);
+ }
+
+ ASSERT(PostAliasCmdLine == NULL);
+ if (!ShellCommandIsCommandOnList(CommandName)) {
+ //
+ // Convert via alias
+ //
+ Status = ShellConvertAlias(&CommandName);
+ PostAliasSize = 0;
+ PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, CommandName, 0);
+ PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, StrStr(CleanOriginal, L" "), 0);
+ ASSERT_EFI_ERROR(Status);
+ } else {
+ PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, NULL, CleanOriginal, 0);
+ }
+
+ if (CleanOriginal != NULL) {
+ FreePool(CleanOriginal);
+ CleanOriginal = NULL;
+ }
+
+ if (CommandName != NULL) {
+ FreePool(CommandName);
+ CommandName = NULL;
+ }
+
+ PostVariableCmdLine = ShellConvertVariables(PostAliasCmdLine);
+
+ //
+ // we can now free the modified by alias command line
+ //
+ if (PostAliasCmdLine != NULL) {
+ FreePool(PostAliasCmdLine);
+ PostAliasCmdLine = NULL;
+ }
+
+ while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {
+ PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;
+ }
+ while (PostVariableCmdLine[0] == L' ') {
+ CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));
+ }
+
+ //
+ // We dont do normal processing with a split command line (output from one command input to another)
+ //
+ TempLocation3 = NULL;
+ if (StrStr(PostVariableCmdLine, L"|") != NULL) {
+ for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) {
+ if (*TempLocation3 == L'^' && *(TempLocation3+1) == L'|') {
+ TempLocation3++;
+ } else if (*TempLocation3 == L'|') {
+ break;
+ }
+ }
+ }
+ if (TempLocation3 != NULL && *TempLocation3 != CHAR_NULL) {
+ //
+ // are we in an existing split???
+ //
+ if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {
+ Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);
+ }
+
+ if (Split == NULL) {
+ Status = RunSplitCommand(PostVariableCmdLine, NULL, NULL);
+ } else {
+ Status = RunSplitCommand(PostVariableCmdLine, Split->SplitStdIn, Split->SplitStdOut);
+ }
+ } else {
+
+ //
+ // If this is a mapped drive change handle that...
+ //
+ if (PostVariableCmdLine[(StrLen(PostVariableCmdLine)-1)] == L':' && StrStr(PostVariableCmdLine, L" ") == NULL) {
+ Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, PostVariableCmdLine);
+ if (EFI_ERROR(Status)) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, PostVariableCmdLine);
+ }
+ FreePool(PostVariableCmdLine);
+ return (Status);
+ }
+
+ ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...
+ /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...
+
+
+
+ Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr);
+ if (EFI_ERROR(Status)) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);
+ } else {
+ //
+ // remove the < and/or > from the command line now
+ //
+ for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) {
+ if (*TempLocation3 == L'^') {
+ if (*(TempLocation3+1) == L'<' || *(TempLocation3+1) == L'>') {
+ CopyMem(TempLocation3, TempLocation3+1, StrSize(TempLocation3) - sizeof(TempLocation3[0]));
+ }
+ } else if (*TempLocation3 == L'>') {
+ *TempLocation3 = CHAR_NULL;
+ } else if ((*TempLocation3 == L'1' || *TempLocation3 == L'2')&&(*(TempLocation3+1) == L'>')) {
+ *TempLocation3 = CHAR_NULL;
+ }
+ }
+
+ while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {
+ PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;
+ }
+ while (PostVariableCmdLine[0] == L' ') {
+ CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));
+ }
+
+ //
+ // get the argc and argv updated for internal commands
+ //
+ Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &Argv, &Argc);
+ ASSERT_EFI_ERROR(Status);
+
+ for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) {
+ if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[Count], L"-?") == ShellInfoObject.NewShellParametersProtocol->Argv[Count]
+ || (ShellInfoObject.NewShellParametersProtocol->Argv[0][0] == L'?' && ShellInfoObject.NewShellParametersProtocol->Argv[0][1] == CHAR_NULL)
+ ) {
+ //
+ // We need to redo the arguments since a parameter was -?
+ // move them all down 1 to the end, then up one then replace the first with help
+ //
+ FreePool(ShellInfoObject.NewShellParametersProtocol->Argv[Count]);
+ ShellInfoObject.NewShellParametersProtocol->Argv[Count] = NULL;
+ for (Count2 = Count ; (Count2 + 1) < ShellInfoObject.NewShellParametersProtocol->Argc ; Count2++) {
+ ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2+1];
+ }
+ ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = NULL;
+ for (Count2 = ShellInfoObject.NewShellParametersProtocol->Argc -1 ; Count2 > 0 ; Count2--) {
+ ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2-1];
+ }
+ ShellInfoObject.NewShellParametersProtocol->Argv[0] = NULL;
+ ShellInfoObject.NewShellParametersProtocol->Argv[0] = StrnCatGrow(&ShellInfoObject.NewShellParametersProtocol->Argv[0], NULL, L"help", 0);
+ break;
+ }
+ }
+
+ //
+ // command or file?
+ //
+ if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {
+ //
+ // Run the command (which was converted if it was an alias)
+ //
+ if (!EFI_ERROR(Status)) {
+ Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError);
+ ASSERT_EFI_ERROR(Status);
+ UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%08x", ShellStatus);
+ DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););
+ if (LastError) {
+ InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);
+ }
+ //
+ // Pass thru the exitcode from the app.
+ //
+ if (ShellCommandGetExit()) {
+ Status = ShellStatus;
+ } else if (ShellStatus != 0 && IsScriptOnlyCommand(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {
+ Status = EFI_ABORTED;
+ }
+ }
+ } else {
+ //
+ // run an external file (or script)
+ //
+ if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[0], L":") != NULL) {
+ ASSERT (CommandWithPath == NULL);
+ if (ShellIsFile(ShellInfoObject.NewShellParametersProtocol->Argv[0]) == EFI_SUCCESS) {
+ CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);
+ }
+ }
+ if (CommandWithPath == NULL) {
+ CommandWithPath = ShellFindFilePathEx(ShellInfoObject.NewShellParametersProtocol->Argv[0], mExecutableExtensions);
+ }
+ if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]);
+ } else {
+ //
+ // Check if it's a NSH (script) file.
+ //
+ TempLocation = CommandWithPath+StrLen(CommandWithPath)-4;
+ TempLocation2 = mScriptExtension;
+ if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) {
+ Status = RunScriptFile (CommandWithPath);
+ } else {
+ DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);
+ ASSERT(DevPath != NULL);
+ Status = InternalShellExecuteDevicePath(
+ &gImageHandle,
+ DevPath,
+ PostVariableCmdLine,
+ NULL,
+ NULL
+ );
+ }
+ }
+ }
+ CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);
+
+ RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);
+
+ RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr);
+ }
+ if (CommandName != NULL) {
+ if (ShellCommandGetCurrentScriptFile() != NULL && !IsScriptOnlyCommand(CommandName)) {
+ //
+ // if this is NOT a scipt only command return success so the script won't quit.
+ // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)
+ //
+ Status = EFI_SUCCESS;
+ }
+ }
+ }
+
+ SHELL_FREE_NON_NULL(CommandName);
+ SHELL_FREE_NON_NULL(CommandWithPath);
+ SHELL_FREE_NON_NULL(PostVariableCmdLine);
+ SHELL_FREE_NON_NULL(DevPath);
+
+ return (Status);
+}
+
+STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};
+/**
+ Function determins if the CommandName COULD be a valid command. It does not determine whether
+ this is a valid command. It only checks for invalid characters.
+
+ @param[in] CommandName The name to check
+
+ @retval TRUE CommandName could be a command name
+ @retval FALSE CommandName could not be a valid command name
+**/
+BOOLEAN
+EFIAPI
+IsValidCommandName(
+ IN CONST CHAR16 *CommandName
+ )
+{
+ UINTN Count;
+ if (CommandName == NULL) {
+ ASSERT(FALSE);
+ return (FALSE);
+ }
+ for ( Count = 0
+ ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])
+ ; Count++
+ ){
+ if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
+
+/**
+ Function to process a NSH script file via SHELL_FILE_HANDLE.
+
+ @param[in] Handle The handle to the already opened file.
+ @param[in] Name The name of the script file.
+
+ @retval EFI_SUCCESS the script completed sucessfully
+**/
+EFI_STATUS
+EFIAPI
+RunScriptFileHandle (
+ IN SHELL_FILE_HANDLE Handle,
+ IN CONST CHAR16 *Name
+ )
+{
+ EFI_STATUS Status;
+ SCRIPT_FILE *NewScriptFile;
+ UINTN LoopVar;
+ CHAR16 *CommandLine;
+ CHAR16 *CommandLine2;
+ CHAR16 *CommandLine3;
+ SCRIPT_COMMAND_LIST *LastCommand;
+ BOOLEAN Ascii;
+ BOOLEAN PreScriptEchoState;
+ CONST CHAR16 *CurDir;
+ UINTN LineCount;
+
+ ASSERT(!ShellCommandGetScriptExit());
+
+ PreScriptEchoState = ShellCommandGetEchoState();
+
+ NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));
+ ASSERT(NewScriptFile != NULL);
+
+ //
+ // Set up the name
+ //
+ ASSERT(NewScriptFile->ScriptName == NULL);
+ NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);
+
+ //
+ // 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*));
+ ASSERT(NewScriptFile->Argv != NULL);
+ for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {
+ ASSERT(NewScriptFile->Argv[LoopVar] == NULL);
+ NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);
+ }
+ } 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) {
+ continue;
+ }
+ NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));
+ ASSERT(NewScriptFile->CurrentCommand != NULL);
+
+ 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 = AllocatePool(PcdGet16(PcdShellPrintBufferSize));
+ CommandLine2 = AllocatePool(PcdGet16(PcdShellPrintBufferSize));
+
+ for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)
+ ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)
+ ; // conditional increment in the body of the loop
+ ){
+ StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl);
+
+ //
+ // NULL out comments
+ //
+ for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {
+ if (*CommandLine3 == L'^') {
+ if (*(CommandLine3+1) == L'#' || *(CommandLine3+1) == L':') {
+ CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));
+ }
+ } else if (*CommandLine3 == L'#') {
+ *CommandLine3 = CHAR_NULL;
+ }
+ }
+
+ if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {
+ //
+ // Due to variability in starting the find and replace action we need to have both buffers the same.
+ //
+ StrCpy(CommandLine, CommandLine2);
+
+ //
+ // Remove the %0 to %9 from the command line (if we have some arguments)
+ //
+ if (NewScriptFile->Argv != NULL) {
+ switch (NewScriptFile->Argc) {
+ default:
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", NewScriptFile->Argv[9], FALSE, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ case 9:
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", NewScriptFile->Argv[8], FALSE, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ case 8:
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", NewScriptFile->Argv[7], FALSE, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ case 7:
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", NewScriptFile->Argv[6], FALSE, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ case 6:
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", NewScriptFile->Argv[5], FALSE, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ case 5:
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", NewScriptFile->Argv[4], FALSE, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ case 4:
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", NewScriptFile->Argv[3], FALSE, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ case 3:
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", NewScriptFile->Argv[2], FALSE, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ case 2:
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", NewScriptFile->Argv[1], FALSE, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ case 1:
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ break;
+ case 0:
+ break;
+ }
+ }
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", L"\"\"", FALSE, FALSE);
+
+ StrCpy(CommandLine2, CommandLine);
+
+ LastCommand = NewScriptFile->CurrentCommand;
+
+ for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);
+
+ if (CommandLine3[0] == L':' ) {
+ //
+ // This line is a goto target / label
+ //
+ } else {
+ if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {
+ 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()) {
+ ShellCommandRegisterExit(FALSE);
+ Status = EFI_SUCCESS;
+ break;
+ }
+ if (EFI_ERROR(Status)) {
+ break;
+ }
+ if (ShellCommandGetExit()) {
+ 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;
+ }
+ }
+ }
+
+ ShellCommandSetEchoState(PreScriptEchoState);
+
+ FreePool(CommandLine);
+ FreePool(CommandLine2);
+ ShellCommandSetNewScript (NULL);
+ return (EFI_SUCCESS);
+}
+
+/**
+ Function to process a NSH script file.
+
+ @param[in] ScriptPath Pointer to the script file name (including file system path).
+
+ @retval EFI_SUCCESS the script completed sucessfully
+**/
+EFI_STATUS
+EFIAPI
+RunScriptFile (
+ IN CONST CHAR16 *ScriptPath
+ )
+{
+ EFI_STATUS Status;
+ SHELL_FILE_HANDLE FileHandle;
+
+ if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+
+ Status = RunScriptFileHandle(FileHandle, ScriptPath);
+
+ ShellCloseFile(&FileHandle);
+
+ return (Status);
+}