/** @file
  This is THE shell (application)

  Copyright (c) 2009 - 2011, 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},
  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";

/**
  Function to start monitoring for CTRL-S using SimpleTextInputEx.  This 
  feature's enabled state was not known when the shell initially launched.

  @retval EFI_SUCCESS           The feature is enabled.
  @retval EFI_OUT_OF_RESOURCES  There is not enough mnemory available.
**/
EFI_STATUS
EFIAPI
InternalEfiShellStartCtrlSMonitor(
  VOID
  )
{
  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
  EFI_KEY_DATA                      KeyData;
  EFI_STATUS                        Status;

  Status = gBS->OpenProtocol(
    gST->ConsoleInHandle,
    &gEfiSimpleTextInputExProtocolGuid,
    (VOID**)&SimpleEx,
    gImageHandle,
    NULL,
    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
  if (EFI_ERROR(Status)) {
    ShellPrintHiiEx(
      -1, 
      -1, 
      NULL,
      STRING_TOKEN (STR_SHELL_NO_IN_EX),
      ShellInfoObject.HiiHandle);
    return (EFI_SUCCESS);
  }

  KeyData.KeyState.KeyToggleState = 0;
  KeyData.Key.ScanCode            = 0;
  KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
  KeyData.Key.UnicodeChar         = L's';

  Status = SimpleEx->RegisterKeyNotify(
    SimpleEx,
    &KeyData,
    NotificationFunction,
    &ShellInfoObject.CtrlSNotifyHandle1);
  
  KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
  if (!EFI_ERROR(Status)) {
    Status = SimpleEx->RegisterKeyNotify(
      SimpleEx,
      &KeyData,
      NotificationFunction,
      &ShellInfoObject.CtrlSNotifyHandle2);
  }
  KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
  KeyData.Key.UnicodeChar         = 19;

  if (!EFI_ERROR(Status)) {
    Status = SimpleEx->RegisterKeyNotify(
      SimpleEx,
      &KeyData,
      NotificationFunction,
      &ShellInfoObject.CtrlSNotifyHandle2);
  }  
  KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
  if (!EFI_ERROR(Status)) {
    Status = SimpleEx->RegisterKeyNotify(
      SimpleEx,
      &KeyData,
      NotificationFunction,
      &ShellInfoObject.CtrlSNotifyHandle2);
  }
  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;

  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) {
        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...
        //
        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 (!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);
          ShellInfoObject.ConsoleInfo->Enabled    = TRUE;
          ShellInfoObject.ConsoleInfo->RowCounter = 0;

          //
          // Reset the CTRL-C event (yes we ignore the return values)
          //
          Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);

          //
          // Display Prompt
          //
          Status = DoShellPrompt();
        } while (!ShellCommandGetExit());
      }
      if (OldConIn != NULL && ConInHandle != NULL) {
        CloseSimpleTextInOnFile (gST->ConIn);
        gST->ConIn            = OldConIn;
        gST->ConsoleInHandle  = ConInHandle;
      }
    }
  }

  //
  // 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()){
      ShellInfoObject.NewEfiShellProtocol->SetEnv(L"cwd", L"", TRUE);
    }
    CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol);
    DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);
  }

  if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
    FreeBufferList(&ShellInfoObject.BufferToFreeList);
  }

  if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){
    ASSERT(FALSE); ///@todo finish this de-allocation.
  }

  if (ShellInfoObject.ShellInitSettings.FileName != NULL) {
    FreePool(ShellInfoObject.ShellInitSettings.FileName);
    DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileName = NULL;);
  }

  if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
    FreePool(ShellInfoObject.ShellInitSettings.FileOptions);
    DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileOptions = NULL;);
  }

  if (ShellInfoObject.HiiHandle != NULL) {
    HiiRemovePackages(ShellInfoObject.HiiHandle);
    DEBUG_CODE(ShellInfoObject.HiiHandle = NULL;);
  }

  if (!IsListEmpty(&ShellInfoObject.ViewingSettings.CommandHistory.Link)){
    FreeBufferList(&ShellInfoObject.ViewingSettings.CommandHistory);
  }

  ASSERT(ShellInfoObject.ConsoleInfo != NULL);
  if (ShellInfoObject.ConsoleInfo != NULL) {
    ConsoleLoggerUninstall(ShellInfoObject.ConsoleInfo);
    FreePool(ShellInfoObject.ConsoleInfo);
    DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);
  }

  if (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 sucessfully.
**/
EFI_STATUS
EFIAPI
SetBuiltInAlias(
  )
{
  EFI_STATUS          Status;
  CONST ALIAS_LIST    *List;
  ALIAS_LIST          *Node;

  //
  // Get all the commands we want to run
  //
  List = ShellCommandGetInitAliasList();

  //
  // for each command in the List
  //
  for ( Node = (ALIAS_LIST*)GetFirstNode(&List->Link)
      ; !IsNull (&List->Link, &Node->Link)
      ; Node = (ALIAS_LIST *)GetNextNode(&List->Link, &Node->Link)
   ){
    //
    // install the alias'
    //
    Status = InternalSetAlias(Node->CommandString, Node->Alias, TRUE);
    ASSERT_EFI_ERROR(Status);
  }
  return (EFI_SUCCESS);
}

/**
  Internal function to determine if 2 command names are really the same.

  @param[in] Command1       The pointer to the first command name.
  @param[in] Command2       The pointer to the second command name.

  @retval TRUE              The 2 command names are the same.
  @retval FALSE             The 2 command names are not the same.
**/
BOOLEAN
EFIAPI
IsCommand(
  IN CONST CHAR16 *Command1,
  IN CONST CHAR16 *Command2
  )
{
  if (StringNoCaseCompare(&Command1, &Command2) == 0) {
    return (TRUE);
  }
  return (FALSE);
}

/**
  Internal function to determine if a command is a script only command.

  @param[in] CommandName    The pointer to the command name.

  @retval TRUE              The command is a script only command.
  @retval FALSE             The command is not a script only command.
**/
BOOLEAN
EFIAPI
IsScriptOnlyCommand(
  IN CONST CHAR16 *CommandName
  )
{
  if (IsCommand(CommandName, L"for")
    ||IsCommand(CommandName, L"endfor")
    ||IsCommand(CommandName, L"if")
    ||IsCommand(CommandName, L"else")
    ||IsCommand(CommandName, L"endif")
    ||IsCommand(CommandName, L"goto")) {
    return (TRUE);
  }
  return (FALSE);
}

/**
  This function will populate the 2 device path protocol parameters based on the
  global gImageHandle.  The DevPath will point to the device path for the handle that has
  loaded image protocol installed on it.  The FilePath will point to the device path
  for the file that was loaded.

  @param[in, out] DevPath       On a sucessful return the device path to the loaded image.
  @param[in, out] FilePath      On a sucessful return the device path to the file.

  @retval EFI_SUCCESS           The 2 device paths were sucessfully returned.
  @retval other                 A error from gBS->HandleProtocol.

  @sa HandleProtocol
**/
EFI_STATUS
EFIAPI
GetDevicePathsForImageAndFile (
  IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,
  IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
  )
{
  EFI_STATUS                Status;
  EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
  EFI_DEVICE_PATH_PROTOCOL  *ImageDevicePath;

  ASSERT(DevPath  != NULL);
  ASSERT(FilePath != NULL);

  Status = gBS->OpenProtocol (
                gImageHandle,
                &gEfiLoadedImageProtocolGuid,
                (VOID**)&LoadedImage,
                gImageHandle,
                NULL,
                EFI_OPEN_PROTOCOL_GET_PROTOCOL
               );
  if (!EFI_ERROR (Status)) {
    Status = gBS->OpenProtocol (
                  LoadedImage->DeviceHandle,
                  &gEfiDevicePathProtocolGuid,
                  (VOID**)&ImageDevicePath,
                  gImageHandle,
                  NULL,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                 );
    if (!EFI_ERROR (Status)) {
      *DevPath  = DuplicateDevicePath (ImageDevicePath);
      *FilePath = DuplicateDevicePath (LoadedImage->FilePath);
      gBS->CloseProtocol(
                  LoadedImage->DeviceHandle,
                  &gEfiDevicePathProtocolGuid,
                  gImageHandle,
                  NULL);
    }
    gBS->CloseProtocol(
                gImageHandle,
                &gEfiLoadedImageProtocolGuid,
                gImageHandle,
                NULL);
  }
  return (Status);
}

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;
  UINT64        Intermediate;

  Package       = NULL;
  ProblemParam  = NULL;
  
  Status = ShellCommandLineParse (mShellParamList, &Package, NULL, FALSE);

  Count = 1;
  Size = 0;
  TempConst = ShellCommandLineGetRawValue(Package, Count++);
  if (TempConst != NULL && StrLen(TempConst)) {
    ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(TempConst));
    if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
      return (EFI_OUT_OF_RESOURCES);
    }
    StrCpy(ShellInfoObject.ShellInitSettings.FileName, TempConst);
    ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;
    for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
      if (StrCmp(gEfiShellParametersProtocol->Argv[LoopVar], ShellInfoObject.ShellInitSettings.FileName)==0) {
        LoopVar++;
        //
        // We found the file... add the rest of the params...
        //
        for ( ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
          ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));
          StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
                      &Size,
                      L" ",
                      0);
          if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
            SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
            return (EFI_OUT_OF_RESOURCES);
          }
          StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
                      &Size,
                      gEfiShellParametersProtocol->Argv[LoopVar],
                      0);
          if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
            SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
            return (EFI_OUT_OF_RESOURCES);
          }
        }
      }
    }
  } else {
    ShellCommandLineFreeVarList(Package);
    Package = NULL;
    Status = ShellCommandLineParse (mShellParamList, &Package, &ProblemParam, FALSE);
    if (EFI_ERROR(Status)) {
      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), ShellInfoObject.HiiHandle, ProblemParam);
      FreePool(ProblemParam);
      ShellCommandLineFreeVarList(Package);
      return (EFI_INVALID_PARAMETER);
    }
  }

  ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup      = ShellCommandLineGetFlag(Package, L"-startup");
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup    = ShellCommandLineGetFlag(Package, L"-nostartup");
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = ShellCommandLineGetFlag(Package, L"-noconsoleout");
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn  = ShellCommandLineGetFlag(Package, L"-noconsolein");
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt  = ShellCommandLineGetFlag(Package, L"-nointerrupt");
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap        = ShellCommandLineGetFlag(Package, L"-nomap");
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion    = ShellCommandLineGetFlag(Package, L"-noversion");
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay        = ShellCommandLineGetFlag(Package, L"-delay");

  ShellInfoObject.ShellInitSettings.Delay = 5;

  if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
    ShellInfoObject.ShellInitSettings.Delay = 0;
  } else if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay) {
    TempConst = ShellCommandLineGetValue(Package, L"-delay");
    if (TempConst != NULL && *TempConst == L':') {
      TempConst++;
    }
    if (TempConst != NULL && !EFI_ERROR(ShellConvertStringToUint64(TempConst, &Intermediate, FALSE, FALSE))) {
      ShellInfoObject.ShellInitSettings.Delay = (UINTN)Intermediate;
    }
  }
  ShellCommandLineFreeVarList(Package);

  return (Status);
}

/**
  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;
  CHAR16                        *TempSpot;
  UINTN                         NewSize;
  CONST CHAR16                  *MapName;

  Key.UnicodeChar = CHAR_NULL;
  Key.ScanCode    = 0;
  FileHandle      = NULL;

  if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) {
    //
    // launch something else instead
    //
    NewSize = StrSize(ShellInfoObject.ShellInitSettings.FileName);
    if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
      NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16);
    }
    FileStringPath = AllocateZeroPool(NewSize);
    if (FileStringPath == NULL) {
      return (EFI_OUT_OF_RESOURCES);
    }
    StrCpy(FileStringPath, ShellInfoObject.ShellInitSettings.FileName);
    if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
      StrCat(FileStringPath, L" ");
      StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);
    }
    Status = RunCommand(FileStringPath);
    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);
    if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
      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);
  }

  //
  // Try the first location (must be file system)
  //
  MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath(&ImagePath);
  if (MapName != NULL) {
    FileStringPath = NULL;
    NewSize = 0;
    FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0);
    if (FileStringPath == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
    } else {
      TempSpot = StrStr(FileStringPath, L";");
      if (TempSpot != NULL) {
        *TempSpot = CHAR_NULL;
      }
      FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);
      PathRemoveLastItem(FileStringPath);
      FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);
      Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);
      FreePool(FileStringPath);
    }
  }
  if (EFI_ERROR(Status)) {
    NamePath = FileDevicePath (NULL, mStartupScript);
    NewPath = AppendDevicePathNode (ImagePath, NamePath);
    FreePool(NamePath);

    //
    // Try the location
    //
    Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);
    FreePool(NewPath);
  }
  //
  // If we got a file, run it
  //
  if (!EFI_ERROR(Status) && FileHandle != NULL) {
    Status = RunScriptFileHandle (FileHandle, mStartupScript);
    ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
  } else {
    FileStringPath = ShellFindFilePath(mStartupScript);
    if (FileStringPath == NULL) {
      //
      // we return success since we dont need to have a startup script
      //
      Status = EFI_SUCCESS;
      ASSERT(FileHandle == NULL);
    } else {
      Status = RunScriptFile(FileStringPath);
      FreePool(FileStringPath);
    }
  }


  return (Status);
}

/**
  Function to perform the shell prompt looping.  It will do a single prompt,
  dispatch the result, and then return.  It is expected that the caller will
  call this function in a loop many times.

  @retval EFI_SUCCESS
  @retval RETURN_ABORTED
**/
EFI_STATUS
EFIAPI
DoShellPrompt (
  VOID
  )
{
  UINTN         Column;
  UINTN         Row;
  CHAR16        *CmdLine;
  CONST CHAR16  *CurDir;
  UINTN         BufferSize;
  EFI_STATUS    Status;

  CurDir  = NULL;

  //
  // Get screen setting to decide size of the command line buffer
  //
  gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);
  BufferSize  = Column * Row * sizeof (CHAR16);
  CmdLine     = AllocateZeroPool (BufferSize);
  if (CmdLine == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");

  //
  // Prompt for input
  //
  gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);

  if (CurDir != NULL && StrLen(CurDir) > 1) {
    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
  } else {
    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
  }

  //
  // Read a line from the console
  //
  Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);

  //
  // Null terminate the string and parse it
  //
  if (!EFI_ERROR (Status)) {
    CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
    Status = RunCommand(CmdLine);
  }

  //
  // Done with this command
  //
  FreePool (CmdLine);
  return Status;
}

/**
  Add a buffer to the Buffer To Free List for safely returning buffers to other
  places without risking letting them modify internal shell information.

  @param Buffer   Something to pass to FreePool when the shell is exiting.
**/
VOID*
EFIAPI
AddBufferToFreeList(
  VOID *Buffer
  )
{
  BUFFER_LIST   *BufferListEntry;

  if (Buffer == NULL) {
    return (NULL);
  }

  BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST));
  ASSERT(BufferListEntry != NULL);
  BufferListEntry->Buffer = Buffer;
  InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);
  return (Buffer);
}

/**
  Add a buffer to the Line History List

  @param Buffer     The line buffer to add.
**/
VOID
EFIAPI
AddLineToCommandHistory(
  IN CONST CHAR16 *Buffer
  )
{
  BUFFER_LIST *Node;

  Node = AllocateZeroPool(sizeof(BUFFER_LIST));
  ASSERT(Node != NULL);
  Node->Buffer = AllocateZeroPool(StrSize(Buffer));
  ASSERT(Node->Buffer != NULL);
  StrCpy(Node->Buffer, Buffer);

  InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
}

/**
  Checks if a string is an alias for another command.  If yes, then it replaces the alias name
  with the correct command name.

  @param[in, out] CommandString    Upon entry the potential alias.  Upon return the
                                   command name if it was an alias.  If it was not
                                   an alias it will be unchanged.  This function may
                                   change the buffer to fit the command name.

  @retval EFI_SUCCESS             The name was changed.
  @retval EFI_SUCCESS             The name was not an alias.
  @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
**/
EFI_STATUS
EFIAPI
ShellConvertAlias(
  IN OUT CHAR16 **CommandString
  )
{
  CONST CHAR16  *NewString;

  NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);
  if (NewString == NULL) {
    return (EFI_SUCCESS);
  }
  FreePool(*CommandString);
  *CommandString = AllocateZeroPool(StrSize(NewString));
  if (*CommandString == NULL) {
    return (EFI_OUT_OF_RESOURCES);
  }
  StrCpy(*CommandString, NewString);
  return (EFI_SUCCESS);
}

/**
  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)));
  if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {
    SHELL_FREE_NON_NULL(NewCommandLine1);
    SHELL_FREE_NON_NULL(NewCommandLine2);
    SHELL_FREE_NON_NULL(ItemTemp);
    return (NULL);
  }
  StrCpy(NewCommandLine1, OriginalCommandLine);
  for (MasterEnvList = EfiShellGetEnv(NULL)
    ;  MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
    ;  MasterEnvList += StrLen(MasterEnvList) + 1
   ){
    StrCpy(ItemTemp, L"%");
    StrCat(ItemTemp, MasterEnvList);
    StrCat(ItemTemp, L"%");
    ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);
    StrCpy(NewCommandLine1, NewCommandLine2);
  }
  if (CurrentScriptFile != NULL) {
    for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
      ;  !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
      ;  AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
   ){
    ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);
    StrCpy(NewCommandLine1, NewCommandLine2);
    }
  }

  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 == NULL || OurCommandLine == NULL) {
    SHELL_FREE_NON_NULL(OurCommandLine);
    SHELL_FREE_NON_NULL(NextCommandLine);
    return (EFI_OUT_OF_RESOURCES);
  }
  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;
  CONST EFI_DEVICE_PATH_PROTOCOL  *DevPath;
  CONST CHAR16              *TempLocation;
  CONST CHAR16              *TempLocation2;
  SHELL_FILE_HANDLE         OriginalStdIn;
  SHELL_FILE_HANDLE         OriginalStdOut;
  SHELL_FILE_HANDLE         OriginalStdErr;
  SYSTEM_TABLE_INFO         OriginalSystemTableInfo;
  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);
  if (CleanOriginal == NULL) {
    return (EFI_OUT_OF_RESOURCES);
  }
  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;
  }

  if (PostVariableCmdLine == NULL) {
    return (EFI_OUT_OF_RESOURCES);
  }

  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, &OriginalSystemTableInfo);
    if (EFI_ERROR(Status)) {
      if (Status == EFI_NOT_FOUND) {
        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle);
      } else {
        ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);
      }
    } else {
      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, &OriginalSystemTableInfo);
    }
    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);

  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;
  BOOLEAN             PreCommandEchoState;
  CONST CHAR16        *CurDir;
  UINTN               LineCount;
  CHAR16              LeString[50];

  ASSERT(!ShellCommandGetScriptExit());

  PreScriptEchoState = ShellCommandGetEchoState();

  NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));
  if (NewScriptFile == NULL) {
    return (EFI_OUT_OF_RESOURCES);
  }

  //
  // Set up the name
  //
  ASSERT(NewScriptFile->ScriptName == NULL);
  NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);
  if (NewScriptFile->ScriptName == NULL) {
    DeleteScriptFileStruct(NewScriptFile);
    return (EFI_OUT_OF_RESOURCES);
  }

  //
  // Save the parameters (used to replace %0 to %9 later on)
  //
  NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;
  if (NewScriptFile->Argc != 0) {
    NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));
    if (NewScriptFile->Argv == NULL) {
      DeleteScriptFileStruct(NewScriptFile);
      return (EFI_OUT_OF_RESOURCES);
    }
    for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {
      ASSERT(NewScriptFile->Argv[LoopVar] == NULL);
      NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);
      if (NewScriptFile->Argv[LoopVar] == NULL) {
        DeleteScriptFileStruct(NewScriptFile);
        return (EFI_OUT_OF_RESOURCES);
      }
    }
  } else {
    NewScriptFile->Argv = NULL;
  }

  InitializeListHead(&NewScriptFile->CommandList);
  InitializeListHead(&NewScriptFile->SubstList);

  //
  // Now build the list of all script commands.
  //
  LineCount = 0;
  while(!ShellFileHandleEof(Handle)) {
    CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);
    LineCount++;
    if (CommandLine == NULL || StrLen(CommandLine) == 0) {
      continue;
    }
    NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));
    if (NewScriptFile->CurrentCommand == NULL) {
      DeleteScriptFileStruct(NewScriptFile);
      return (EFI_OUT_OF_RESOURCES);
    }

    NewScriptFile->CurrentCommand->Cl   = CommandLine;
    NewScriptFile->CurrentCommand->Data = NULL;
    NewScriptFile->CurrentCommand->Line = LineCount;

    InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
  }

  //
  // Add this as the topmost script file
  //
  ShellCommandSetNewScript (NewScriptFile);

  //
  // Now enumerate through the commands and run each one.
  //
  CommandLine = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));
  if (CommandLine == NULL) {
    DeleteScriptFileStruct(NewScriptFile);
    return (EFI_OUT_OF_RESOURCES);
  }
  CommandLine2 = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));
  if (CommandLine2 == NULL) {
    FreePool(CommandLine);
    DeleteScriptFileStruct(NewScriptFile);
    return (EFI_OUT_OF_RESOURCES);
  }

  for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)
      ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)
      ; // conditional increment in the body of the loop
  ){
    ASSERT(CommandLine2 != NULL);
    StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl);

    //
    // NULL out comments
    //
    for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {
      if (*CommandLine3 == L'^') {
        if (*(CommandLine3+1) == L'#' || *(CommandLine3+1) == L':') {
          CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));
        }
      } else if (*CommandLine3 == L'#') {
        *CommandLine3 = CHAR_NULL;
      }
    }

    if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {
      //
      // Due to variability in starting the find and replace action we need to have both buffers the same.
      //
      StrCpy(CommandLine, CommandLine2);

      //
      // Remove the %0 to %9 from the command line (if we have some arguments)
      //
      if (NewScriptFile->Argv != NULL) {
        switch (NewScriptFile->Argc) {
          default:
            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", NewScriptFile->Argv[9], FALSE, TRUE);
            ASSERT_EFI_ERROR(Status);
          case 9:
            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", NewScriptFile->Argv[8], FALSE, TRUE);
            ASSERT_EFI_ERROR(Status);
          case 8:
            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", NewScriptFile->Argv[7], FALSE, TRUE);
            ASSERT_EFI_ERROR(Status);
          case 7:
            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", NewScriptFile->Argv[6], FALSE, TRUE);
            ASSERT_EFI_ERROR(Status);
          case 6:
            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", NewScriptFile->Argv[5], FALSE, TRUE);
            ASSERT_EFI_ERROR(Status);
          case 5:
            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", NewScriptFile->Argv[4], FALSE, TRUE);
            ASSERT_EFI_ERROR(Status);
          case 4:
            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", NewScriptFile->Argv[3], FALSE, TRUE);
            ASSERT_EFI_ERROR(Status);
          case 3:
            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", NewScriptFile->Argv[2], FALSE, TRUE);
            ASSERT_EFI_ERROR(Status);
          case 2:
            Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", NewScriptFile->Argv[1], FALSE, TRUE);
            ASSERT_EFI_ERROR(Status);
          case 1:
            Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, TRUE);
            ASSERT_EFI_ERROR(Status);
            break;
          case 0:
            break;
        }
      }
      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", L"\"\"", FALSE, FALSE);
      Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", L"\"\"", FALSE, FALSE);
      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", L"\"\"", FALSE, FALSE);
      Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", L"\"\"", FALSE, FALSE);
      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", L"\"\"", FALSE, FALSE);
      Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", L"\"\"", FALSE, FALSE);
      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", L"\"\"", FALSE, FALSE);
      Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", L"\"\"", FALSE, FALSE);
      Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", L"\"\"", FALSE, FALSE);

      StrCpy(CommandLine2, CommandLine);

      LastCommand = NewScriptFile->CurrentCommand;

      for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);

      if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {
        //
        // This line is a goto target / label
        //
      } else {
        if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {
          if (CommandLine3[0] == L'@') {
            //
            // We need to save the current echo state
            // and disable echo for just this command.
            //
            PreCommandEchoState = ShellCommandGetEchoState();
            ShellCommandSetEchoState(FALSE);
            Status = RunCommand(CommandLine3+1);

            //
            // 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()) {
          UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%Lx", ShellCommandGetExitCode());
          DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););
          InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);

          ShellCommandRegisterExit(FALSE, 0);
          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;
      }
    }
  }


  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).

  @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);
}
