mirror of
				https://github.com/acidanthera/audk.git
				synced 2025-11-03 21:17:23 +01:00 
			
		
		
		
	Increase max buffer size to support more than 64K. Signed-off-by: Giri Mudusuru <girim@apple.com> Cc: Ray Ni <ray.ni@intel.com> Cc: Zhichao Gao <zhichao.gao@intel.com> Cc: Andrew Fish <afish@apple.com> Reviewed-by: Zhichao Gao <zhichao.gao@intel.com>
		
			
				
	
	
		
			3351 lines
		
	
	
		
			102 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3351 lines
		
	
	
		
			102 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  This is THE shell (application)
 | 
						|
 | 
						|
  Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
 | 
						|
  (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.<BR>
 | 
						|
  Copyright 2015-2018 Dell Technologies.<BR>
 | 
						|
  Copyright (C) 2023, Apple Inc. All rights reserved.<BR>
 | 
						|
 | 
						|
  SPDX-License-Identifier: BSD-2-Clause-Patent
 | 
						|
**/
 | 
						|
 | 
						|
#include "Shell.h"
 | 
						|
 | 
						|
//
 | 
						|
// Initialize the global structure
 | 
						|
//
 | 
						|
SHELL_INFO  ShellInfoObject = {
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  FALSE,
 | 
						|
  FALSE,
 | 
						|
  {
 | 
						|
    {
 | 
						|
      {
 | 
						|
        0,
 | 
						|
        0,
 | 
						|
        0,
 | 
						|
        0,
 | 
						|
        0,
 | 
						|
        0,
 | 
						|
        0,
 | 
						|
        0,
 | 
						|
        0,
 | 
						|
        0
 | 
						|
      }
 | 
						|
    },
 | 
						|
    0,
 | 
						|
    NULL,
 | 
						|
    NULL
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { NULL,NULL   }, NULL
 | 
						|
  },
 | 
						|
  {
 | 
						|
    {
 | 
						|
      { NULL,NULL   }, NULL
 | 
						|
    },
 | 
						|
    0,
 | 
						|
    0,
 | 
						|
    TRUE
 | 
						|
  },
 | 
						|
  NULL,
 | 
						|
  0,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  {
 | 
						|
    { NULL,NULL   }, NULL, NULL
 | 
						|
  },
 | 
						|
  {
 | 
						|
    { NULL,NULL   }, NULL, NULL
 | 
						|
  },
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  NULL,
 | 
						|
  FALSE
 | 
						|
};
 | 
						|
 | 
						|
STATIC CONST CHAR16  mScriptExtension[]      = L".NSH";
 | 
						|
STATIC CONST CHAR16  mExecutableExtensions[] = L".NSH;.EFI";
 | 
						|
STATIC CONST CHAR16  mStartupScript[]        = L"startup.nsh";
 | 
						|
CONST CHAR16         mNoNestingEnvVarName[]  = L"nonesting";
 | 
						|
CONST CHAR16         mNoNestingTrue[]        = L"True";
 | 
						|
CONST CHAR16         mNoNestingFalse[]       = L"False";
 | 
						|
 | 
						|
/**
 | 
						|
  Cleans off leading and trailing spaces and tabs.
 | 
						|
 | 
						|
  @param[in] String pointer to the string to trim them off.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
TrimSpaces (
 | 
						|
  IN CHAR16  **String
 | 
						|
  )
 | 
						|
{
 | 
						|
  ASSERT (String != NULL);
 | 
						|
  ASSERT (*String != NULL);
 | 
						|
  //
 | 
						|
  // Remove any spaces and tabs at the beginning of the (*String).
 | 
						|
  //
 | 
						|
  while (((*String)[0] == L' ') || ((*String)[0] == L'\t')) {
 | 
						|
    CopyMem ((*String), (*String)+1, StrSize ((*String)) - sizeof ((*String)[0]));
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Remove any spaces and tabs at the end of the (*String).
 | 
						|
  //
 | 
						|
  while ((StrLen (*String) > 0) && (((*String)[StrLen ((*String))-1] == L' ') || ((*String)[StrLen ((*String))-1] == L'\t'))) {
 | 
						|
    (*String)[StrLen ((*String))-1] = CHAR_NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  return (EFI_SUCCESS);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Parse for the next instance of one string within another string. Can optionally make sure that
 | 
						|
  the string was not escaped (^ character) per the shell specification.
 | 
						|
 | 
						|
  @param[in] SourceString             The string to search within
 | 
						|
  @param[in] FindString               The string to look for
 | 
						|
  @param[in] CheckForEscapeCharacter  TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances
 | 
						|
**/
 | 
						|
CHAR16 *
 | 
						|
FindNextInstance (
 | 
						|
  IN CONST CHAR16   *SourceString,
 | 
						|
  IN CONST CHAR16   *FindString,
 | 
						|
  IN CONST BOOLEAN  CheckForEscapeCharacter
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR16  *Temp;
 | 
						|
 | 
						|
  if (SourceString == NULL) {
 | 
						|
    return (NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  Temp = StrStr (SourceString, FindString);
 | 
						|
 | 
						|
  //
 | 
						|
  // If nothing found, or we don't care about escape characters
 | 
						|
  //
 | 
						|
  if ((Temp == NULL) || !CheckForEscapeCharacter) {
 | 
						|
    return (Temp);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // If we found an escaped character, try again on the remainder of the string
 | 
						|
  //
 | 
						|
  if ((Temp > (SourceString)) && (*(Temp-1) == L'^')) {
 | 
						|
    return FindNextInstance (Temp+1, FindString, CheckForEscapeCharacter);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // we found the right character
 | 
						|
  //
 | 
						|
  return (Temp);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Check whether the string between a pair of % is a valid environment variable name.
 | 
						|
 | 
						|
  @param[in] BeginPercent       pointer to the first percent.
 | 
						|
  @param[in] EndPercent          pointer to the last percent.
 | 
						|
 | 
						|
  @retval TRUE                          is a valid environment variable name.
 | 
						|
  @retval FALSE                         is NOT a valid environment variable name.
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
IsValidEnvironmentVariableName (
 | 
						|
  IN CONST CHAR16  *BeginPercent,
 | 
						|
  IN CONST CHAR16  *EndPercent
 | 
						|
  )
 | 
						|
{
 | 
						|
  CONST CHAR16  *Walker;
 | 
						|
 | 
						|
  Walker = NULL;
 | 
						|
 | 
						|
  ASSERT (BeginPercent != NULL);
 | 
						|
  ASSERT (EndPercent != NULL);
 | 
						|
  ASSERT (BeginPercent < EndPercent);
 | 
						|
 | 
						|
  if ((BeginPercent + 1) == EndPercent) {
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  for (Walker = BeginPercent + 1; Walker < EndPercent; Walker++) {
 | 
						|
    if (
 | 
						|
        ((*Walker >= L'0') && (*Walker <= L'9')) ||
 | 
						|
        ((*Walker >= L'A') && (*Walker <= L'Z')) ||
 | 
						|
        ((*Walker >= L'a') && (*Walker <= L'z')) ||
 | 
						|
        (*Walker == L'_')
 | 
						|
        )
 | 
						|
    {
 | 
						|
      if ((Walker == BeginPercent + 1) && ((*Walker >= L'0') && (*Walker <= L'9'))) {
 | 
						|
        return FALSE;
 | 
						|
      } else {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      return FALSE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Determine if a command line contains a split operation
 | 
						|
 | 
						|
  @param[in] CmdLine      The command line to parse.
 | 
						|
 | 
						|
  @retval TRUE            CmdLine has a valid split.
 | 
						|
  @retval FALSE           CmdLine does not have a valid split.
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
ContainsSplit (
 | 
						|
  IN CONST CHAR16  *CmdLine
 | 
						|
  )
 | 
						|
{
 | 
						|
  CONST CHAR16  *TempSpot;
 | 
						|
  CONST CHAR16  *FirstQuote;
 | 
						|
  CONST CHAR16  *SecondQuote;
 | 
						|
 | 
						|
  FirstQuote  = FindNextInstance (CmdLine, L"\"", TRUE);
 | 
						|
  SecondQuote = NULL;
 | 
						|
  TempSpot    = FindFirstCharacter (CmdLine, L"|", L'^');
 | 
						|
 | 
						|
  if ((FirstQuote == NULL) ||
 | 
						|
      (TempSpot == NULL) ||
 | 
						|
      (TempSpot == CHAR_NULL) ||
 | 
						|
      (FirstQuote > TempSpot)
 | 
						|
      )
 | 
						|
  {
 | 
						|
    return (BOOLEAN)((TempSpot != NULL) && (*TempSpot != CHAR_NULL));
 | 
						|
  }
 | 
						|
 | 
						|
  while ((TempSpot != NULL) && (*TempSpot != CHAR_NULL)) {
 | 
						|
    if ((FirstQuote == NULL) || (FirstQuote > TempSpot)) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    SecondQuote = FindNextInstance (FirstQuote + 1, L"\"", TRUE);
 | 
						|
    if (SecondQuote == NULL) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    if (SecondQuote < TempSpot) {
 | 
						|
      FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);
 | 
						|
      continue;
 | 
						|
    } else {
 | 
						|
      FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);
 | 
						|
      TempSpot   = FindFirstCharacter (TempSpot + 1, L"|", L'^');
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return (BOOLEAN)((TempSpot != NULL) && (*TempSpot != CHAR_NULL));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Function to start monitoring for CTRL-S using SimpleTextInputEx.  This
 | 
						|
  feature's enabled state was not known when the shell initially launched.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The feature is enabled.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  There is not enough memory available.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
InternalEfiShellStartCtrlSMonitor (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *SimpleEx;
 | 
						|
  EFI_KEY_DATA                       KeyData;
 | 
						|
  EFI_STATUS                         Status;
 | 
						|
 | 
						|
  Status = gBS->OpenProtocol (
 | 
						|
                  gST->ConsoleInHandle,
 | 
						|
                  &gEfiSimpleTextInputExProtocolGuid,
 | 
						|
                  (VOID **)&SimpleEx,
 | 
						|
                  gImageHandle,
 | 
						|
                  NULL,
 | 
						|
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
 | 
						|
                  );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    ShellPrintHiiEx (
 | 
						|
      -1,
 | 
						|
      -1,
 | 
						|
      NULL,
 | 
						|
      STRING_TOKEN (STR_SHELL_NO_IN_EX),
 | 
						|
      ShellInfoObject.HiiHandle
 | 
						|
      );
 | 
						|
    return (EFI_SUCCESS);
 | 
						|
  }
 | 
						|
 | 
						|
  KeyData.KeyState.KeyToggleState = 0;
 | 
						|
  KeyData.Key.ScanCode            = 0;
 | 
						|
  KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
 | 
						|
  KeyData.Key.UnicodeChar         = L's';
 | 
						|
 | 
						|
  Status = SimpleEx->RegisterKeyNotify (
 | 
						|
                       SimpleEx,
 | 
						|
                       &KeyData,
 | 
						|
                       NotificationFunction,
 | 
						|
                       &ShellInfoObject.CtrlSNotifyHandle1
 | 
						|
                       );
 | 
						|
 | 
						|
  KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    Status = SimpleEx->RegisterKeyNotify (
 | 
						|
                         SimpleEx,
 | 
						|
                         &KeyData,
 | 
						|
                         NotificationFunction,
 | 
						|
                         &ShellInfoObject.CtrlSNotifyHandle2
 | 
						|
                         );
 | 
						|
  }
 | 
						|
 | 
						|
  KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
 | 
						|
  KeyData.Key.UnicodeChar        = 19;
 | 
						|
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    Status = SimpleEx->RegisterKeyNotify (
 | 
						|
                         SimpleEx,
 | 
						|
                         &KeyData,
 | 
						|
                         NotificationFunction,
 | 
						|
                         &ShellInfoObject.CtrlSNotifyHandle3
 | 
						|
                         );
 | 
						|
  }
 | 
						|
 | 
						|
  KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    Status = SimpleEx->RegisterKeyNotify (
 | 
						|
                         SimpleEx,
 | 
						|
                         &KeyData,
 | 
						|
                         NotificationFunction,
 | 
						|
                         &ShellInfoObject.CtrlSNotifyHandle4
 | 
						|
                         );
 | 
						|
  }
 | 
						|
 | 
						|
  return (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  The entry point for the application.
 | 
						|
 | 
						|
  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
 | 
						|
  @param[in] SystemTable    A pointer to the EFI System Table.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS       The entry point is executed successfully.
 | 
						|
  @retval other             Some error occurs when executing this entry point.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
UefiMain (
 | 
						|
  IN EFI_HANDLE        ImageHandle,
 | 
						|
  IN EFI_SYSTEM_TABLE  *SystemTable
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                      Status;
 | 
						|
  CHAR16                          *TempString;
 | 
						|
  UINTN                           Size;
 | 
						|
  EFI_HANDLE                      ConInHandle;
 | 
						|
  EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *OldConIn;
 | 
						|
  SPLIT_LIST                      *Split;
 | 
						|
 | 
						|
  if (PcdGet8 (PcdShellSupportLevel) > 3) {
 | 
						|
    return (EFI_UNSUPPORTED);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Clear the screen
 | 
						|
  //
 | 
						|
  Status = gST->ConOut->ClearScreen (gST->ConOut);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Populate the global structure from PCDs
 | 
						|
  //
 | 
						|
  ShellInfoObject.ImageDevPath               = NULL;
 | 
						|
  ShellInfoObject.FileDevPath                = NULL;
 | 
						|
  ShellInfoObject.PageBreakEnabled           = PcdGetBool (PcdShellPageBreakDefault);
 | 
						|
  ShellInfoObject.ViewingSettings.InsertMode = PcdGetBool (PcdShellInsertModeDefault);
 | 
						|
  ShellInfoObject.LogScreenCount             = PcdGet8 (PcdShellScreenLogCount);
 | 
						|
 | 
						|
  //
 | 
						|
  // verify we dont allow for spec violation
 | 
						|
  //
 | 
						|
  ASSERT (ShellInfoObject.LogScreenCount >= 3);
 | 
						|
 | 
						|
  //
 | 
						|
  // Initialize the LIST ENTRY objects...
 | 
						|
  //
 | 
						|
  InitializeListHead (&ShellInfoObject.BufferToFreeList.Link);
 | 
						|
  InitializeListHead (&ShellInfoObject.ViewingSettings.CommandHistory.Link);
 | 
						|
  InitializeListHead (&ShellInfoObject.SplitList.Link);
 | 
						|
 | 
						|
  //
 | 
						|
  // Check PCDs for optional features that are not implemented yet.
 | 
						|
  //
 | 
						|
  if (  PcdGetBool (PcdShellSupportOldProtocols)
 | 
						|
     || !FeaturePcdGet (PcdShellRequireHiiPlatform)
 | 
						|
     || FeaturePcdGet (PcdShellSupportFrameworkHii)
 | 
						|
        )
 | 
						|
  {
 | 
						|
    return (EFI_UNSUPPORTED);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // turn off the watchdog timer
 | 
						|
  //
 | 
						|
  gBS->SetWatchdogTimer (0, 0, 0, NULL);
 | 
						|
 | 
						|
  //
 | 
						|
  // install our console logger.  This will keep a log of the output for back-browsing
 | 
						|
  //
 | 
						|
  Status = ConsoleLoggerInstall (ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    //
 | 
						|
    // Enable the cursor to be visible
 | 
						|
    //
 | 
						|
    gST->ConOut->EnableCursor (gST->ConOut, TRUE);
 | 
						|
 | 
						|
    //
 | 
						|
    // If supporting EFI 1.1 we need to install HII protocol
 | 
						|
    // only do this if PcdShellRequireHiiPlatform == FALSE
 | 
						|
    //
 | 
						|
    // remove EFI_UNSUPPORTED check above when complete.
 | 
						|
    /// @todo add support for Framework HII
 | 
						|
 | 
						|
    //
 | 
						|
    // install our (solitary) HII package
 | 
						|
    //
 | 
						|
    ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);
 | 
						|
    if (ShellInfoObject.HiiHandle == NULL) {
 | 
						|
      if (PcdGetBool (PcdShellSupportFrameworkHii)) {
 | 
						|
        /// @todo Add our package into Framework HII
 | 
						|
      }
 | 
						|
 | 
						|
      if (ShellInfoObject.HiiHandle == NULL) {
 | 
						|
        Status = EFI_NOT_STARTED;
 | 
						|
        goto FreeResources;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // create and install the EfiShellParametersProtocol
 | 
						|
    //
 | 
						|
    Status = CreatePopulateInstallShellParametersProtocol (&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
    ASSERT (ShellInfoObject.NewShellParametersProtocol != NULL);
 | 
						|
 | 
						|
    //
 | 
						|
    // create and install the EfiShellProtocol
 | 
						|
    //
 | 
						|
    Status = CreatePopulateInstallShellProtocol (&ShellInfoObject.NewEfiShellProtocol);
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
    ASSERT (ShellInfoObject.NewEfiShellProtocol != NULL);
 | 
						|
 | 
						|
    //
 | 
						|
    // Now initialize the shell library (it requires Shell Parameters protocol)
 | 
						|
    //
 | 
						|
    Status = ShellInitialize ();
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
    Status = CommandInit ();
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
    Status = ShellInitEnvVarList ();
 | 
						|
 | 
						|
    //
 | 
						|
    // Check the command line
 | 
						|
    //
 | 
						|
    Status = ProcessCommandLine ();
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto FreeResources;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // If shell support level is >= 1 create the mappings and paths
 | 
						|
    //
 | 
						|
    if (PcdGet8 (PcdShellSupportLevel) >= 1) {
 | 
						|
      Status = ShellCommandCreateInitialMappingsAndPaths ();
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Set the environment variable for nesting support
 | 
						|
    //
 | 
						|
    Size       = 0;
 | 
						|
    TempString = NULL;
 | 
						|
    if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) {
 | 
						|
      //
 | 
						|
      // No change.  require nesting in Shell Protocol Execute()
 | 
						|
      //
 | 
						|
      StrnCatGrow (
 | 
						|
        &TempString,
 | 
						|
        &Size,
 | 
						|
        L"False",
 | 
						|
        0
 | 
						|
        );
 | 
						|
    } else {
 | 
						|
      StrnCatGrow (
 | 
						|
        &TempString,
 | 
						|
        &Size,
 | 
						|
        mNoNestingTrue,
 | 
						|
        0
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    Status = InternalEfiShellSetEnv (mNoNestingEnvVarName, TempString, TRUE);
 | 
						|
    SHELL_FREE_NON_NULL (TempString);
 | 
						|
    Size = 0;
 | 
						|
 | 
						|
    //
 | 
						|
    // save the device path for the loaded image and the device path for the filepath (under loaded image)
 | 
						|
    // These are where to look for the startup.nsh file
 | 
						|
    //
 | 
						|
    Status = GetDevicePathsForImageAndFile (&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
    //
 | 
						|
    // Display the version
 | 
						|
    //
 | 
						|
    if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {
 | 
						|
      ShellPrintHiiEx (
 | 
						|
        0,
 | 
						|
        gST->ConOut->Mode->CursorRow,
 | 
						|
        NULL,
 | 
						|
        STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL),
 | 
						|
        ShellInfoObject.HiiHandle,
 | 
						|
        SupportLevel[PcdGet8 (PcdShellSupportLevel)],
 | 
						|
        gEfiShellProtocol->MajorVersion,
 | 
						|
        gEfiShellProtocol->MinorVersion
 | 
						|
        );
 | 
						|
 | 
						|
      ShellPrintHiiEx (
 | 
						|
        -1,
 | 
						|
        -1,
 | 
						|
        NULL,
 | 
						|
        STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER),
 | 
						|
        ShellInfoObject.HiiHandle,
 | 
						|
        (CHAR16 *)PcdGetPtr (PcdShellSupplier)
 | 
						|
        );
 | 
						|
 | 
						|
      ShellPrintHiiEx (
 | 
						|
        -1,
 | 
						|
        -1,
 | 
						|
        NULL,
 | 
						|
        STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI),
 | 
						|
        ShellInfoObject.HiiHandle,
 | 
						|
        (gST->Hdr.Revision&0xffff0000)>>16,
 | 
						|
        (gST->Hdr.Revision&0x0000ffff),
 | 
						|
        gST->FirmwareVendor,
 | 
						|
        gST->FirmwareRevision
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Display the mapping
 | 
						|
    //
 | 
						|
    if ((PcdGet8 (PcdShellSupportLevel) >= 2) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {
 | 
						|
      Status = RunCommand (L"map");
 | 
						|
      ASSERT_EFI_ERROR (Status);
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // init all the built in alias'
 | 
						|
    //
 | 
						|
    Status = SetBuiltInAlias ();
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
    //
 | 
						|
    // Initialize environment variables
 | 
						|
    //
 | 
						|
    if (ShellCommandGetProfileList () != NULL) {
 | 
						|
      Status = InternalEfiShellSetEnv (L"profiles", ShellCommandGetProfileList (), TRUE);
 | 
						|
      ASSERT_EFI_ERROR (Status);
 | 
						|
    }
 | 
						|
 | 
						|
    Size       = 100;
 | 
						|
    TempString = AllocateZeroPool (Size);
 | 
						|
 | 
						|
    UnicodeSPrint (TempString, Size, L"%d", PcdGet8 (PcdShellSupportLevel));
 | 
						|
    Status = InternalEfiShellSetEnv (L"uefishellsupport", TempString, TRUE);
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
    UnicodeSPrint (TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);
 | 
						|
    Status = InternalEfiShellSetEnv (L"uefishellversion", TempString, TRUE);
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
    UnicodeSPrint (TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);
 | 
						|
    Status = InternalEfiShellSetEnv (L"uefiversion", TempString, TRUE);
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
    FreePool (TempString);
 | 
						|
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
 | 
						|
        //
 | 
						|
        // Set up the event for CTRL-C monitoring...
 | 
						|
        //
 | 
						|
        Status = InernalEfiShellStartMonitor ();
 | 
						|
      }
 | 
						|
 | 
						|
      if (!EFI_ERROR (Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
 | 
						|
        //
 | 
						|
        // Set up the event for CTRL-S monitoring...
 | 
						|
        //
 | 
						|
        Status = InternalEfiShellStartCtrlSMonitor ();
 | 
						|
      }
 | 
						|
 | 
						|
      if (!EFI_ERROR (Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
 | 
						|
        //
 | 
						|
        // close off the gST->ConIn
 | 
						|
        //
 | 
						|
        OldConIn    = gST->ConIn;
 | 
						|
        ConInHandle = gST->ConsoleInHandle;
 | 
						|
        gST->ConIn  = CreateSimpleTextInOnFile ((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle);
 | 
						|
      } else {
 | 
						|
        OldConIn    = NULL;
 | 
						|
        ConInHandle = NULL;
 | 
						|
      }
 | 
						|
 | 
						|
      if (!EFI_ERROR (Status) && (PcdGet8 (PcdShellSupportLevel) >= 1)) {
 | 
						|
        //
 | 
						|
        // process the startup script or launch the called app.
 | 
						|
        //
 | 
						|
        Status = DoStartupScript (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
 | 
						|
      }
 | 
						|
 | 
						|
      if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit () && ((PcdGet8 (PcdShellSupportLevel) >= 3) || PcdGetBool (PcdShellForceConsole)) && !EFI_ERROR (Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
 | 
						|
        //
 | 
						|
        // begin the UI waiting loop
 | 
						|
        //
 | 
						|
        do {
 | 
						|
          //
 | 
						|
          // clean out all the memory allocated for CONST <something> * return values
 | 
						|
          // between each shell prompt presentation
 | 
						|
          //
 | 
						|
          if (!IsListEmpty (&ShellInfoObject.BufferToFreeList.Link)) {
 | 
						|
            FreeBufferList (&ShellInfoObject.BufferToFreeList);
 | 
						|
          }
 | 
						|
 | 
						|
          //
 | 
						|
          // Reset page break back to default.
 | 
						|
          //
 | 
						|
          ShellInfoObject.PageBreakEnabled = PcdGetBool (PcdShellPageBreakDefault);
 | 
						|
          ASSERT (ShellInfoObject.ConsoleInfo != NULL);
 | 
						|
          ShellInfoObject.ConsoleInfo->Enabled    = TRUE;
 | 
						|
          ShellInfoObject.ConsoleInfo->RowCounter = 0;
 | 
						|
 | 
						|
          //
 | 
						|
          // Display Prompt
 | 
						|
          //
 | 
						|
          Status = DoShellPrompt ();
 | 
						|
        } while (!ShellCommandGetExit ());
 | 
						|
      }
 | 
						|
 | 
						|
      if ((OldConIn != NULL) && (ConInHandle != NULL)) {
 | 
						|
        CloseSimpleTextInOnFile (gST->ConIn);
 | 
						|
        gST->ConIn           = OldConIn;
 | 
						|
        gST->ConsoleInHandle = ConInHandle;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
FreeResources:
 | 
						|
  //
 | 
						|
  // uninstall protocols / free memory / etc...
 | 
						|
  //
 | 
						|
  if (ShellInfoObject.UserBreakTimer != NULL) {
 | 
						|
    gBS->CloseEvent (ShellInfoObject.UserBreakTimer);
 | 
						|
    DEBUG_CODE (
 | 
						|
      ShellInfoObject.UserBreakTimer = NULL;
 | 
						|
      );
 | 
						|
  }
 | 
						|
 | 
						|
  if (ShellInfoObject.ImageDevPath != NULL) {
 | 
						|
    FreePool (ShellInfoObject.ImageDevPath);
 | 
						|
    DEBUG_CODE (
 | 
						|
      ShellInfoObject.ImageDevPath = NULL;
 | 
						|
      );
 | 
						|
  }
 | 
						|
 | 
						|
  if (ShellInfoObject.FileDevPath != NULL) {
 | 
						|
    FreePool (ShellInfoObject.FileDevPath);
 | 
						|
    DEBUG_CODE (
 | 
						|
      ShellInfoObject.FileDevPath = NULL;
 | 
						|
      );
 | 
						|
  }
 | 
						|
 | 
						|
  if (ShellInfoObject.NewShellParametersProtocol != NULL) {
 | 
						|
    CleanUpShellParametersProtocol (ShellInfoObject.NewShellParametersProtocol);
 | 
						|
    DEBUG_CODE (
 | 
						|
      ShellInfoObject.NewShellParametersProtocol = NULL;
 | 
						|
      );
 | 
						|
  }
 | 
						|
 | 
						|
  if (ShellInfoObject.NewEfiShellProtocol != NULL) {
 | 
						|
    if (ShellInfoObject.NewEfiShellProtocol->IsRootShell ()) {
 | 
						|
      InternalEfiShellSetEnv (L"cwd", NULL, TRUE);
 | 
						|
    }
 | 
						|
 | 
						|
    CleanUpShellEnvironment (ShellInfoObject.NewEfiShellProtocol);
 | 
						|
    DEBUG_CODE (
 | 
						|
      ShellInfoObject.NewEfiShellProtocol = NULL;
 | 
						|
      );
 | 
						|
  }
 | 
						|
 | 
						|
  if (!IsListEmpty (&ShellInfoObject.BufferToFreeList.Link)) {
 | 
						|
    FreeBufferList (&ShellInfoObject.BufferToFreeList);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!IsListEmpty (&ShellInfoObject.SplitList.Link)) {
 | 
						|
    ASSERT (FALSE); /// @todo finish this de-allocation (free SplitStdIn/Out when needed).
 | 
						|
 | 
						|
    for ( Split = (SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link)
 | 
						|
          ; !IsNull (&ShellInfoObject.SplitList.Link, &Split->Link)
 | 
						|
          ; Split = (SPLIT_LIST *)GetNextNode (&ShellInfoObject.SplitList.Link, &Split->Link)
 | 
						|
          )
 | 
						|
    {
 | 
						|
      RemoveEntryList (&Split->Link);
 | 
						|
      FreePool (Split);
 | 
						|
    }
 | 
						|
 | 
						|
    DEBUG_CODE (
 | 
						|
      InitializeListHead (&ShellInfoObject.SplitList.Link);
 | 
						|
      );
 | 
						|
  }
 | 
						|
 | 
						|
  if (ShellInfoObject.ShellInitSettings.FileName != NULL) {
 | 
						|
    FreePool (ShellInfoObject.ShellInitSettings.FileName);
 | 
						|
    DEBUG_CODE (
 | 
						|
      ShellInfoObject.ShellInitSettings.FileName = NULL;
 | 
						|
      );
 | 
						|
  }
 | 
						|
 | 
						|
  if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
 | 
						|
    FreePool (ShellInfoObject.ShellInitSettings.FileOptions);
 | 
						|
    DEBUG_CODE (
 | 
						|
      ShellInfoObject.ShellInitSettings.FileOptions = NULL;
 | 
						|
      );
 | 
						|
  }
 | 
						|
 | 
						|
  if (ShellInfoObject.HiiHandle != NULL) {
 | 
						|
    HiiRemovePackages (ShellInfoObject.HiiHandle);
 | 
						|
    DEBUG_CODE (
 | 
						|
      ShellInfoObject.HiiHandle = NULL;
 | 
						|
      );
 | 
						|
  }
 | 
						|
 | 
						|
  if (!IsListEmpty (&ShellInfoObject.ViewingSettings.CommandHistory.Link)) {
 | 
						|
    FreeBufferList (&ShellInfoObject.ViewingSettings.CommandHistory);
 | 
						|
  }
 | 
						|
 | 
						|
  ASSERT (ShellInfoObject.ConsoleInfo != NULL);
 | 
						|
  if (ShellInfoObject.ConsoleInfo != NULL) {
 | 
						|
    ConsoleLoggerUninstall (ShellInfoObject.ConsoleInfo);
 | 
						|
    FreePool (ShellInfoObject.ConsoleInfo);
 | 
						|
    DEBUG_CODE (
 | 
						|
      ShellInfoObject.ConsoleInfo = NULL;
 | 
						|
      );
 | 
						|
  }
 | 
						|
 | 
						|
  ShellFreeEnvVarList ();
 | 
						|
 | 
						|
  if (ShellCommandGetExit ()) {
 | 
						|
    return ((EFI_STATUS)ShellCommandGetExitCode ());
 | 
						|
  }
 | 
						|
 | 
						|
  return (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Sets all the alias' that were registered with the ShellCommandLib library.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           all init commands were run successfully.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
SetBuiltInAlias (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS        Status;
 | 
						|
  CONST ALIAS_LIST  *List;
 | 
						|
  ALIAS_LIST        *Node;
 | 
						|
 | 
						|
  //
 | 
						|
  // Get all the commands we want to run
 | 
						|
  //
 | 
						|
  List = ShellCommandGetInitAliasList ();
 | 
						|
 | 
						|
  //
 | 
						|
  // for each command in the List
 | 
						|
  //
 | 
						|
  for ( Node = (ALIAS_LIST *)GetFirstNode (&List->Link)
 | 
						|
        ; !IsNull (&List->Link, &Node->Link)
 | 
						|
        ; Node = (ALIAS_LIST *)GetNextNode (&List->Link, &Node->Link)
 | 
						|
        )
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // install the alias'
 | 
						|
    //
 | 
						|
    Status = InternalSetAlias (Node->CommandString, Node->Alias, TRUE);
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  return (EFI_SUCCESS);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Internal function to determine if 2 command names are really the same.
 | 
						|
 | 
						|
  @param[in] Command1       The pointer to the first command name.
 | 
						|
  @param[in] Command2       The pointer to the second command name.
 | 
						|
 | 
						|
  @retval TRUE              The 2 command names are the same.
 | 
						|
  @retval FALSE             The 2 command names are not the same.
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
IsCommand (
 | 
						|
  IN CONST CHAR16  *Command1,
 | 
						|
  IN CONST CHAR16  *Command2
 | 
						|
  )
 | 
						|
{
 | 
						|
  if (StringNoCaseCompare (&Command1, &Command2) == 0) {
 | 
						|
    return (TRUE);
 | 
						|
  }
 | 
						|
 | 
						|
  return (FALSE);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Internal function to determine if a command is a script only command.
 | 
						|
 | 
						|
  @param[in] CommandName    The pointer to the command name.
 | 
						|
 | 
						|
  @retval TRUE              The command is a script only command.
 | 
						|
  @retval FALSE             The command is not a script only command.
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
IsScriptOnlyCommand (
 | 
						|
  IN CONST CHAR16  *CommandName
 | 
						|
  )
 | 
						|
{
 | 
						|
  if (  IsCommand (CommandName, L"for")
 | 
						|
     || IsCommand (CommandName, L"endfor")
 | 
						|
     || IsCommand (CommandName, L"if")
 | 
						|
     || IsCommand (CommandName, L"else")
 | 
						|
     || IsCommand (CommandName, L"endif")
 | 
						|
     || IsCommand (CommandName, L"goto"))
 | 
						|
  {
 | 
						|
    return (TRUE);
 | 
						|
  }
 | 
						|
 | 
						|
  return (FALSE);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function will populate the 2 device path protocol parameters based on the
 | 
						|
  global gImageHandle.  The DevPath will point to the device path for the handle that has
 | 
						|
  loaded image protocol installed on it.  The FilePath will point to the device path
 | 
						|
  for the file that was loaded.
 | 
						|
 | 
						|
  @param[in, out] DevPath       On a successful return the device path to the loaded image.
 | 
						|
  @param[in, out] FilePath      On a successful return the device path to the file.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The 2 device paths were successfully returned.
 | 
						|
  @retval other                 A error from gBS->HandleProtocol.
 | 
						|
 | 
						|
  @sa HandleProtocol
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
GetDevicePathsForImageAndFile (
 | 
						|
  IN OUT EFI_DEVICE_PATH_PROTOCOL  **DevPath,
 | 
						|
  IN OUT EFI_DEVICE_PATH_PROTOCOL  **FilePath
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                 Status;
 | 
						|
  EFI_LOADED_IMAGE_PROTOCOL  *LoadedImage;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL   *ImageDevicePath;
 | 
						|
 | 
						|
  ASSERT (DevPath  != NULL);
 | 
						|
  ASSERT (FilePath != NULL);
 | 
						|
 | 
						|
  Status = gBS->OpenProtocol (
 | 
						|
                  gImageHandle,
 | 
						|
                  &gEfiLoadedImageProtocolGuid,
 | 
						|
                  (VOID **)&LoadedImage,
 | 
						|
                  gImageHandle,
 | 
						|
                  NULL,
 | 
						|
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
 | 
						|
                  );
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    Status = gBS->OpenProtocol (
 | 
						|
                    LoadedImage->DeviceHandle,
 | 
						|
                    &gEfiDevicePathProtocolGuid,
 | 
						|
                    (VOID **)&ImageDevicePath,
 | 
						|
                    gImageHandle,
 | 
						|
                    NULL,
 | 
						|
                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
 | 
						|
                    );
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      *DevPath  = DuplicateDevicePath (ImageDevicePath);
 | 
						|
      *FilePath = DuplicateDevicePath (LoadedImage->FilePath);
 | 
						|
      gBS->CloseProtocol (
 | 
						|
             LoadedImage->DeviceHandle,
 | 
						|
             &gEfiDevicePathProtocolGuid,
 | 
						|
             gImageHandle,
 | 
						|
             NULL
 | 
						|
             );
 | 
						|
    }
 | 
						|
 | 
						|
    gBS->CloseProtocol (
 | 
						|
           gImageHandle,
 | 
						|
           &gEfiLoadedImageProtocolGuid,
 | 
						|
           gImageHandle,
 | 
						|
           NULL
 | 
						|
           );
 | 
						|
  }
 | 
						|
 | 
						|
  return (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Process all Uefi Shell 2.0 command line options.
 | 
						|
 | 
						|
  see Uefi Shell 2.0 section 3.2 for full details.
 | 
						|
 | 
						|
  the command line must resemble the following:
 | 
						|
 | 
						|
  shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
 | 
						|
 | 
						|
  ShellOpt-options  Options which control the initialization behavior of the shell.
 | 
						|
                    These options are read from the EFI global variable "ShellOpt"
 | 
						|
                    and are processed before options or file-name.
 | 
						|
 | 
						|
  options           Options which control the initialization behavior of the shell.
 | 
						|
 | 
						|
  file-name         The name of a UEFI shell application or script to be executed
 | 
						|
                    after initialization is complete. By default, if file-name is
 | 
						|
                    specified, then -nostartup is implied. Scripts are not supported
 | 
						|
                    by level 0.
 | 
						|
 | 
						|
  file-name-options The command-line options that are passed to file-name when it
 | 
						|
                    is invoked.
 | 
						|
 | 
						|
  This will initialize the ShellInfoObject.ShellInitSettings global variable.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The variable is initialized.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
ProcessCommandLine (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                           Size;
 | 
						|
  UINTN                           LoopVar;
 | 
						|
  CHAR16                          *CurrentArg;
 | 
						|
  CHAR16                          *DelayValueStr;
 | 
						|
  UINT64                          DelayValue;
 | 
						|
  EFI_STATUS                      Status;
 | 
						|
  EFI_UNICODE_COLLATION_PROTOCOL  *UnicodeCollation;
 | 
						|
 | 
						|
  // `file-name-options` will contain arguments to `file-name` that we don't
 | 
						|
  // know about. This would cause ShellCommandLineParse to error, so we parse
 | 
						|
  // arguments manually, ignoring those after the first thing that doesn't look
 | 
						|
  // like a shell option (which is assumed to be `file-name`).
 | 
						|
 | 
						|
  Status = gBS->LocateProtocol (
 | 
						|
                  &gEfiUnicodeCollation2ProtocolGuid,
 | 
						|
                  NULL,
 | 
						|
                  (VOID **)&UnicodeCollation
 | 
						|
                  );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    Status = gBS->LocateProtocol (
 | 
						|
                    &gEfiUnicodeCollationProtocolGuid,
 | 
						|
                    NULL,
 | 
						|
                    (VOID **)&UnicodeCollation
 | 
						|
                    );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Set default options
 | 
						|
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup      = FALSE;
 | 
						|
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup    = FALSE;
 | 
						|
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = FALSE;
 | 
						|
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn  = FALSE;
 | 
						|
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt  = FALSE;
 | 
						|
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap        = FALSE;
 | 
						|
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion    = FALSE;
 | 
						|
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay        = FALSE;
 | 
						|
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit         = FALSE;
 | 
						|
  ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest       = FALSE;
 | 
						|
  ShellInfoObject.ShellInitSettings.Delay                      = PcdGet32 (PcdShellDefaultDelay);
 | 
						|
 | 
						|
  //
 | 
						|
  // Start LoopVar at 0 to parse only optional arguments at Argv[0]
 | 
						|
  // and parse other parameters from Argv[1].  This is for use case that
 | 
						|
  // UEFI Shell boot option is created, and OptionalData is provided
 | 
						|
  // that starts with shell command-line options.
 | 
						|
  //
 | 
						|
  for (LoopVar = 0; LoopVar < gEfiShellParametersProtocol->Argc; LoopVar++) {
 | 
						|
    CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar];
 | 
						|
    if (UnicodeCollation->StriColl (
 | 
						|
                            UnicodeCollation,
 | 
						|
                            L"-startup",
 | 
						|
                            CurrentArg
 | 
						|
                            ) == 0)
 | 
						|
    {
 | 
						|
      ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = TRUE;
 | 
						|
    } else if (UnicodeCollation->StriColl (
 | 
						|
                                   UnicodeCollation,
 | 
						|
                                   L"-nostartup",
 | 
						|
                                   CurrentArg
 | 
						|
                                   ) == 0)
 | 
						|
    {
 | 
						|
      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = TRUE;
 | 
						|
    } else if (UnicodeCollation->StriColl (
 | 
						|
                                   UnicodeCollation,
 | 
						|
                                   L"-noconsoleout",
 | 
						|
                                   CurrentArg
 | 
						|
                                   ) == 0)
 | 
						|
    {
 | 
						|
      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = TRUE;
 | 
						|
    } else if (UnicodeCollation->StriColl (
 | 
						|
                                   UnicodeCollation,
 | 
						|
                                   L"-noconsolein",
 | 
						|
                                   CurrentArg
 | 
						|
                                   ) == 0)
 | 
						|
    {
 | 
						|
      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = TRUE;
 | 
						|
    } else if (UnicodeCollation->StriColl (
 | 
						|
                                   UnicodeCollation,
 | 
						|
                                   L"-nointerrupt",
 | 
						|
                                   CurrentArg
 | 
						|
                                   ) == 0)
 | 
						|
    {
 | 
						|
      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = TRUE;
 | 
						|
    } else if (UnicodeCollation->StriColl (
 | 
						|
                                   UnicodeCollation,
 | 
						|
                                   L"-nomap",
 | 
						|
                                   CurrentArg
 | 
						|
                                   ) == 0)
 | 
						|
    {
 | 
						|
      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = TRUE;
 | 
						|
    } else if (UnicodeCollation->StriColl (
 | 
						|
                                   UnicodeCollation,
 | 
						|
                                   L"-noversion",
 | 
						|
                                   CurrentArg
 | 
						|
                                   ) == 0)
 | 
						|
    {
 | 
						|
      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = TRUE;
 | 
						|
    } else if (UnicodeCollation->StriColl (
 | 
						|
                                   UnicodeCollation,
 | 
						|
                                   L"-nonest",
 | 
						|
                                   CurrentArg
 | 
						|
                                   ) == 0)
 | 
						|
    {
 | 
						|
      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = TRUE;
 | 
						|
    } else if (UnicodeCollation->StriColl (
 | 
						|
                                   UnicodeCollation,
 | 
						|
                                   L"-delay",
 | 
						|
                                   CurrentArg
 | 
						|
                                   ) == 0)
 | 
						|
    {
 | 
						|
      ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = TRUE;
 | 
						|
      // Check for optional delay value following "-delay"
 | 
						|
      if ((LoopVar + 1) >= gEfiShellParametersProtocol->Argc) {
 | 
						|
        DelayValueStr = NULL;
 | 
						|
      } else {
 | 
						|
        DelayValueStr = gEfiShellParametersProtocol->Argv[LoopVar + 1];
 | 
						|
      }
 | 
						|
 | 
						|
      if (DelayValueStr != NULL) {
 | 
						|
        if (*DelayValueStr == L':') {
 | 
						|
          DelayValueStr++;
 | 
						|
        }
 | 
						|
 | 
						|
        if (!EFI_ERROR (
 | 
						|
               ShellConvertStringToUint64 (
 | 
						|
                 DelayValueStr,
 | 
						|
                 &DelayValue,
 | 
						|
                 FALSE,
 | 
						|
                 FALSE
 | 
						|
                 )
 | 
						|
               ))
 | 
						|
        {
 | 
						|
          ShellInfoObject.ShellInitSettings.Delay = (UINTN)DelayValue;
 | 
						|
          LoopVar++;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    } else if (UnicodeCollation->StriColl (
 | 
						|
                                   UnicodeCollation,
 | 
						|
                                   L"-exit",
 | 
						|
                                   CurrentArg
 | 
						|
                                   ) == 0)
 | 
						|
    {
 | 
						|
      ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = TRUE;
 | 
						|
    } else if (StrnCmp (L"-", CurrentArg, 1) == 0) {
 | 
						|
      // Unrecognized option
 | 
						|
      ShellPrintHiiEx (
 | 
						|
        -1,
 | 
						|
        -1,
 | 
						|
        NULL,
 | 
						|
        STRING_TOKEN (STR_GEN_PROBLEM),
 | 
						|
        ShellInfoObject.HiiHandle,
 | 
						|
        CurrentArg
 | 
						|
        );
 | 
						|
      return EFI_INVALID_PARAMETER;
 | 
						|
    } else {
 | 
						|
      //
 | 
						|
      // First argument should be Shell.efi image name
 | 
						|
      //
 | 
						|
      if (LoopVar == 0) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      ShellInfoObject.ShellInitSettings.FileName = NULL;
 | 
						|
      Size                                       = 0;
 | 
						|
      //
 | 
						|
      // If first argument contains a space, then add double quotes before the argument
 | 
						|
      //
 | 
						|
      if (StrStr (CurrentArg, L" ") != NULL) {
 | 
						|
        StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0);
 | 
						|
        if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
 | 
						|
          return (EFI_OUT_OF_RESOURCES);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, CurrentArg, 0);
 | 
						|
      if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
 | 
						|
        return (EFI_OUT_OF_RESOURCES);
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      // If first argument contains a space, then add double quotes after the argument
 | 
						|
      //
 | 
						|
      if (StrStr (CurrentArg, L" ") != NULL) {
 | 
						|
        StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0);
 | 
						|
        if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
 | 
						|
          return (EFI_OUT_OF_RESOURCES);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      // We found `file-name`.
 | 
						|
      //
 | 
						|
      ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;
 | 
						|
      LoopVar++;
 | 
						|
 | 
						|
      // Add `file-name-options`
 | 
						|
      for (Size = 0; LoopVar < gEfiShellParametersProtocol->Argc; LoopVar++) {
 | 
						|
        ASSERT ((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));
 | 
						|
        //
 | 
						|
        // Add a space between arguments
 | 
						|
        //
 | 
						|
        if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
 | 
						|
          StrnCatGrow (&ShellInfoObject.ShellInitSettings.FileOptions, &Size, L" ", 0);
 | 
						|
          if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
 | 
						|
            SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName);
 | 
						|
            return (EFI_OUT_OF_RESOURCES);
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        //
 | 
						|
        // If an argument contains a space, then add double quotes before the argument
 | 
						|
        //
 | 
						|
        if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != NULL) {
 | 
						|
          StrnCatGrow (
 | 
						|
            &ShellInfoObject.ShellInitSettings.FileOptions,
 | 
						|
            &Size,
 | 
						|
            L"\"",
 | 
						|
            0
 | 
						|
            );
 | 
						|
          if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
 | 
						|
            SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName);
 | 
						|
            return (EFI_OUT_OF_RESOURCES);
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        StrnCatGrow (
 | 
						|
          &ShellInfoObject.ShellInitSettings.FileOptions,
 | 
						|
          &Size,
 | 
						|
          gEfiShellParametersProtocol->Argv[LoopVar],
 | 
						|
          0
 | 
						|
          );
 | 
						|
        if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
 | 
						|
          SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName);
 | 
						|
          return (EFI_OUT_OF_RESOURCES);
 | 
						|
        }
 | 
						|
 | 
						|
        //
 | 
						|
        // If an argument contains a space, then add double quotes after the argument
 | 
						|
        //
 | 
						|
        if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != NULL) {
 | 
						|
          StrnCatGrow (
 | 
						|
            &ShellInfoObject.ShellInitSettings.FileOptions,
 | 
						|
            &Size,
 | 
						|
            L"\"",
 | 
						|
            0
 | 
						|
            );
 | 
						|
          if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
 | 
						|
            SHELL_FREE_NON_NULL (ShellInfoObject.ShellInitSettings.FileName);
 | 
						|
            return (EFI_OUT_OF_RESOURCES);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // "-nointerrupt" overrides "-delay"
 | 
						|
  if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
 | 
						|
    ShellInfoObject.ShellInitSettings.Delay = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Function try to find location of the Startup.nsh file.
 | 
						|
 | 
						|
  The buffer is callee allocated and should be freed by the caller.
 | 
						|
 | 
						|
  @param    ImageDevicePath       The path to the image for shell.  first place to look for the startup script
 | 
						|
  @param    FileDevicePath        The path to the file for shell.  second place to look for the startup script.
 | 
						|
 | 
						|
  @retval   NULL                  No Startup.nsh file was found.
 | 
						|
  @return   !=NULL                Pointer to NULL-terminated path.
 | 
						|
**/
 | 
						|
CHAR16 *
 | 
						|
LocateStartupScript (
 | 
						|
  IN EFI_DEVICE_PATH_PROTOCOL  *ImageDevicePath,
 | 
						|
  IN EFI_DEVICE_PATH_PROTOCOL  *FileDevicePath
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR16        *StartupScriptPath;
 | 
						|
  CHAR16        *TempSpot;
 | 
						|
  CONST CHAR16  *MapName;
 | 
						|
  UINTN         Size;
 | 
						|
 | 
						|
  StartupScriptPath = NULL;
 | 
						|
  Size              = 0;
 | 
						|
 | 
						|
  //
 | 
						|
  // Try to find 'Startup.nsh' in the directory where the shell itself was launched.
 | 
						|
  //
 | 
						|
  MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath (&ImageDevicePath);
 | 
						|
  if (MapName != NULL) {
 | 
						|
    StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, MapName, 0);
 | 
						|
    if (StartupScriptPath == NULL) {
 | 
						|
      //
 | 
						|
      // Do not locate the startup script in sys path when out of resource.
 | 
						|
      //
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    TempSpot = StrStr (StartupScriptPath, L";");
 | 
						|
    if (TempSpot != NULL) {
 | 
						|
      *TempSpot = CHAR_NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    InternalEfiShellSetEnv (L"homefilesystem", StartupScriptPath, TRUE);
 | 
						|
 | 
						|
    StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, ((FILEPATH_DEVICE_PATH *)FileDevicePath)->PathName, 0);
 | 
						|
    PathRemoveLastItem (StartupScriptPath);
 | 
						|
    StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, mStartupScript, 0);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Try to find 'Startup.nsh' in the execution path defined by the environment variable PATH.
 | 
						|
  //
 | 
						|
  if ((StartupScriptPath == NULL) || EFI_ERROR (ShellIsFile (StartupScriptPath))) {
 | 
						|
    SHELL_FREE_NON_NULL (StartupScriptPath);
 | 
						|
    StartupScriptPath = ShellFindFilePath (mStartupScript);
 | 
						|
  }
 | 
						|
 | 
						|
  return StartupScriptPath;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Handles all interaction with the default startup script.
 | 
						|
 | 
						|
  this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
 | 
						|
 | 
						|
  @param ImagePath              the path to the image for shell.  first place to look for the startup script
 | 
						|
  @param FilePath               the path to the file for shell.  second place to look for the startup script.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           the variable is initialized.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
DoStartupScript (
 | 
						|
  IN EFI_DEVICE_PATH_PROTOCOL  *ImagePath,
 | 
						|
  IN EFI_DEVICE_PATH_PROTOCOL  *FilePath
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS     Status;
 | 
						|
  EFI_STATUS     CalleeStatus;
 | 
						|
  UINTN          Delay;
 | 
						|
  EFI_INPUT_KEY  Key;
 | 
						|
  CHAR16         *FileStringPath;
 | 
						|
  CHAR16         *FullFileStringPath;
 | 
						|
  UINTN          NewSize;
 | 
						|
 | 
						|
  CalleeStatus    = EFI_SUCCESS;
 | 
						|
  Key.UnicodeChar = CHAR_NULL;
 | 
						|
  Key.ScanCode    = 0;
 | 
						|
 | 
						|
  if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && (ShellInfoObject.ShellInitSettings.FileName != NULL)) {
 | 
						|
    //
 | 
						|
    // launch something else instead
 | 
						|
    //
 | 
						|
    NewSize = StrSize (ShellInfoObject.ShellInitSettings.FileName);
 | 
						|
    if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
 | 
						|
      NewSize += StrSize (ShellInfoObject.ShellInitSettings.FileOptions) + sizeof (CHAR16);
 | 
						|
    }
 | 
						|
 | 
						|
    FileStringPath = AllocateZeroPool (NewSize);
 | 
						|
    if (FileStringPath == NULL) {
 | 
						|
      return (EFI_OUT_OF_RESOURCES);
 | 
						|
    }
 | 
						|
 | 
						|
    StrCpyS (FileStringPath, NewSize/sizeof (CHAR16), ShellInfoObject.ShellInitSettings.FileName);
 | 
						|
    if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
 | 
						|
      StrnCatS (FileStringPath, NewSize/sizeof (CHAR16), L" ", NewSize/sizeof (CHAR16) - StrLen (FileStringPath) -1);
 | 
						|
      StrnCatS (FileStringPath, NewSize/sizeof (CHAR16), ShellInfoObject.ShellInitSettings.FileOptions, NewSize/sizeof (CHAR16) - StrLen (FileStringPath) -1);
 | 
						|
    }
 | 
						|
 | 
						|
    Status = RunShellCommand (FileStringPath, &CalleeStatus);
 | 
						|
    if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit == TRUE) {
 | 
						|
      ShellCommandRegisterExit (gEfiShellProtocol->BatchIsActive (), (UINT64)CalleeStatus);
 | 
						|
    }
 | 
						|
 | 
						|
    FreePool (FileStringPath);
 | 
						|
    return (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // for shell level 0 we do no scripts
 | 
						|
  // Without the Startup bit overriding we allow for nostartup to prevent scripts
 | 
						|
  //
 | 
						|
  if (  (PcdGet8 (PcdShellSupportLevel) < 1)
 | 
						|
     || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup)
 | 
						|
        )
 | 
						|
  {
 | 
						|
    return (EFI_SUCCESS);
 | 
						|
  }
 | 
						|
 | 
						|
  gST->ConOut->EnableCursor (gST->ConOut, FALSE);
 | 
						|
  //
 | 
						|
  // print out our warning and see if they press a key
 | 
						|
  //
 | 
						|
  for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay
 | 
						|
        ; Delay != 0 && EFI_ERROR (Status)
 | 
						|
        ; Delay--
 | 
						|
        )
 | 
						|
  {
 | 
						|
    ShellPrintHiiEx (0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay);
 | 
						|
    gBS->Stall (1000000);
 | 
						|
    if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
 | 
						|
      Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);
 | 
						|
  gST->ConOut->EnableCursor (gST->ConOut, TRUE);
 | 
						|
 | 
						|
  //
 | 
						|
  // ESC was pressed
 | 
						|
  //
 | 
						|
  if ((Status == EFI_SUCCESS) && (Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC)) {
 | 
						|
    return (EFI_SUCCESS);
 | 
						|
  }
 | 
						|
 | 
						|
  FileStringPath = LocateStartupScript (ImagePath, FilePath);
 | 
						|
  if (FileStringPath != NULL) {
 | 
						|
    FullFileStringPath = FullyQualifyPath (FileStringPath);
 | 
						|
    if (FullFileStringPath == NULL) {
 | 
						|
      Status = RunScriptFile (FileStringPath, NULL, FileStringPath, ShellInfoObject.NewShellParametersProtocol);
 | 
						|
    } else {
 | 
						|
      Status = RunScriptFile (FullFileStringPath, NULL, FullFileStringPath, ShellInfoObject.NewShellParametersProtocol);
 | 
						|
      FreePool (FullFileStringPath);
 | 
						|
    }
 | 
						|
 | 
						|
    FreePool (FileStringPath);
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // we return success since startup script is not mandatory.
 | 
						|
    //
 | 
						|
    Status = EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  return (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Function to perform the shell prompt looping.  It will do a single prompt,
 | 
						|
  dispatch the result, and then return.  It is expected that the caller will
 | 
						|
  call this function in a loop many times.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS
 | 
						|
  @retval RETURN_ABORTED
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
DoShellPrompt (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN         Column;
 | 
						|
  UINTN         Row;
 | 
						|
  CHAR16        *CmdLine;
 | 
						|
  CONST CHAR16  *CurDir;
 | 
						|
  UINTN         BufferSize;
 | 
						|
  EFI_STATUS    Status;
 | 
						|
  LIST_ENTRY    OldBufferList;
 | 
						|
 | 
						|
  CurDir = NULL;
 | 
						|
 | 
						|
  //
 | 
						|
  // Get screen setting to decide size of the command line buffer
 | 
						|
  //
 | 
						|
  gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);
 | 
						|
  BufferSize = Column * Row * sizeof (CHAR16);
 | 
						|
  CmdLine    = AllocateZeroPool (BufferSize);
 | 
						|
  if (CmdLine == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  SaveBufferList (&OldBufferList);
 | 
						|
  CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv (L"cwd");
 | 
						|
 | 
						|
  //
 | 
						|
  // Prompt for input
 | 
						|
  //
 | 
						|
  gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);
 | 
						|
 | 
						|
  if ((CurDir != NULL) && (StrLen (CurDir) > 1)) {
 | 
						|
    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
 | 
						|
  } else {
 | 
						|
    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Read a line from the console
 | 
						|
  //
 | 
						|
  Status = ShellInfoObject.NewEfiShellProtocol->ReadFile (ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);
 | 
						|
 | 
						|
  //
 | 
						|
  // Null terminate the string and parse it
 | 
						|
  //
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    //
 | 
						|
    // Reset the CTRL-C event just before running the command (yes we ignore the return values)
 | 
						|
    //
 | 
						|
    Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);
 | 
						|
 | 
						|
    CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
 | 
						|
    Status                                = RunCommand (CmdLine);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Done with this command
 | 
						|
  //
 | 
						|
  RestoreBufferList (&OldBufferList);
 | 
						|
  FreePool (CmdLine);
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Add a buffer to the Buffer To Free List for safely returning buffers to other
 | 
						|
  places without risking letting them modify internal shell information.
 | 
						|
 | 
						|
  @param Buffer   Something to pass to FreePool when the shell is exiting.
 | 
						|
**/
 | 
						|
VOID *
 | 
						|
AddBufferToFreeList (
 | 
						|
  VOID  *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  BUFFER_LIST  *BufferListEntry;
 | 
						|
 | 
						|
  if (Buffer == NULL) {
 | 
						|
    return (NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  BufferListEntry = AllocateZeroPool (sizeof (BUFFER_LIST));
 | 
						|
  if (BufferListEntry == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  BufferListEntry->Buffer = Buffer;
 | 
						|
  InsertTailList (&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);
 | 
						|
  return (Buffer);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Create a new buffer list and stores the old one to OldBufferList
 | 
						|
 | 
						|
  @param OldBufferList   The temporary list head used to store the nodes in BufferToFreeList.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
SaveBufferList (
 | 
						|
  OUT LIST_ENTRY  *OldBufferList
 | 
						|
  )
 | 
						|
{
 | 
						|
  CopyMem (OldBufferList, &ShellInfoObject.BufferToFreeList.Link, sizeof (LIST_ENTRY));
 | 
						|
  InitializeListHead (&ShellInfoObject.BufferToFreeList.Link);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Restore previous nodes into BufferToFreeList .
 | 
						|
 | 
						|
  @param OldBufferList   The temporary list head used to store the nodes in BufferToFreeList.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
RestoreBufferList (
 | 
						|
  IN OUT LIST_ENTRY  *OldBufferList
 | 
						|
  )
 | 
						|
{
 | 
						|
  FreeBufferList (&ShellInfoObject.BufferToFreeList);
 | 
						|
  CopyMem (&ShellInfoObject.BufferToFreeList.Link, OldBufferList, sizeof (LIST_ENTRY));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Add a buffer to the Line History List
 | 
						|
 | 
						|
  @param Buffer     The line buffer to add.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
AddLineToCommandHistory (
 | 
						|
  IN CONST CHAR16  *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  BUFFER_LIST  *Node;
 | 
						|
  BUFFER_LIST  *Walker;
 | 
						|
  UINT16       MaxHistoryCmdCount;
 | 
						|
  UINT16       Count;
 | 
						|
 | 
						|
  Count              = 0;
 | 
						|
  MaxHistoryCmdCount = PcdGet16 (PcdShellMaxHistoryCommandCount);
 | 
						|
 | 
						|
  if (MaxHistoryCmdCount == 0) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  Node = AllocateZeroPool (sizeof (BUFFER_LIST));
 | 
						|
  if (Node == NULL) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  Node->Buffer = AllocateCopyPool (StrSize (Buffer), Buffer);
 | 
						|
  if (Node->Buffer == NULL) {
 | 
						|
    FreePool (Node);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  for ( Walker = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link)
 | 
						|
        ; !IsNull (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)
 | 
						|
        ; Walker = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)
 | 
						|
        )
 | 
						|
  {
 | 
						|
    Count++;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Count < MaxHistoryCmdCount) {
 | 
						|
    InsertTailList (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
 | 
						|
  } else {
 | 
						|
    Walker = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link);
 | 
						|
    RemoveEntryList (&Walker->Link);
 | 
						|
    if (Walker->Buffer != NULL) {
 | 
						|
      FreePool (Walker->Buffer);
 | 
						|
    }
 | 
						|
 | 
						|
    FreePool (Walker);
 | 
						|
    InsertTailList (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Checks if a string is an alias for another command.  If yes, then it replaces the alias name
 | 
						|
  with the correct command name.
 | 
						|
 | 
						|
  @param[in, out] CommandString    Upon entry the potential alias.  Upon return the
 | 
						|
                                   command name if it was an alias.  If it was not
 | 
						|
                                   an alias it will be unchanged.  This function may
 | 
						|
                                   change the buffer to fit the command name.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS             The name was changed.
 | 
						|
  @retval EFI_SUCCESS             The name was not an alias.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
ShellConvertAlias (
 | 
						|
  IN OUT CHAR16  **CommandString
 | 
						|
  )
 | 
						|
{
 | 
						|
  CONST CHAR16  *NewString;
 | 
						|
 | 
						|
  NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias (*CommandString, NULL);
 | 
						|
  if (NewString == NULL) {
 | 
						|
    return (EFI_SUCCESS);
 | 
						|
  }
 | 
						|
 | 
						|
  FreePool (*CommandString);
 | 
						|
  *CommandString = AllocateCopyPool (StrSize (NewString), NewString);
 | 
						|
  if (*CommandString == NULL) {
 | 
						|
    return (EFI_OUT_OF_RESOURCES);
 | 
						|
  }
 | 
						|
 | 
						|
  return (EFI_SUCCESS);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function will eliminate unreplaced (and therefore non-found) environment variables.
 | 
						|
 | 
						|
  @param[in,out] CmdLine   The command line to update.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
StripUnreplacedEnvironmentVariables (
 | 
						|
  IN OUT CHAR16  *CmdLine
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR16  *FirstPercent;
 | 
						|
  CHAR16  *FirstQuote;
 | 
						|
  CHAR16  *SecondPercent;
 | 
						|
  CHAR16  *SecondQuote;
 | 
						|
  CHAR16  *CurrentLocator;
 | 
						|
 | 
						|
  for (CurrentLocator = CmdLine; CurrentLocator != NULL; ) {
 | 
						|
    FirstQuote    = FindNextInstance (CurrentLocator, L"\"", TRUE);
 | 
						|
    FirstPercent  = FindNextInstance (CurrentLocator, L"%", TRUE);
 | 
						|
    SecondPercent = FirstPercent != NULL ? FindNextInstance (FirstPercent+1, L"%", TRUE) : NULL;
 | 
						|
    if ((FirstPercent == NULL) || (SecondPercent == NULL)) {
 | 
						|
      //
 | 
						|
      // If we ever don't have 2 % we are done.
 | 
						|
      //
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((FirstQuote != NULL) && (FirstQuote < FirstPercent)) {
 | 
						|
      SecondQuote = FindNextInstance (FirstQuote+1, L"\"", TRUE);
 | 
						|
      //
 | 
						|
      // Quote is first found
 | 
						|
      //
 | 
						|
 | 
						|
      if (SecondQuote < FirstPercent) {
 | 
						|
        //
 | 
						|
        // restart after the pair of "
 | 
						|
        //
 | 
						|
        CurrentLocator = SecondQuote + 1;
 | 
						|
      } else {
 | 
						|
        /* FirstPercent < SecondQuote */
 | 
						|
        //
 | 
						|
        // Restart on the first percent
 | 
						|
        //
 | 
						|
        CurrentLocator = FirstPercent;
 | 
						|
      }
 | 
						|
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((FirstQuote == NULL) || (SecondPercent < FirstQuote)) {
 | 
						|
      if (IsValidEnvironmentVariableName (FirstPercent, SecondPercent)) {
 | 
						|
        //
 | 
						|
        // We need to remove from FirstPercent to SecondPercent
 | 
						|
        //
 | 
						|
        CopyMem (FirstPercent, SecondPercent + 1, StrSize (SecondPercent + 1));
 | 
						|
        //
 | 
						|
        // don't need to update the locator.  both % characters are gone.
 | 
						|
        //
 | 
						|
      } else {
 | 
						|
        CurrentLocator = SecondPercent + 1;
 | 
						|
      }
 | 
						|
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    CurrentLocator = FirstQuote;
 | 
						|
  }
 | 
						|
 | 
						|
  return (EFI_SUCCESS);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Function allocates a new command line and replaces all instances of environment
 | 
						|
  variable names that are correctly preset to their values.
 | 
						|
 | 
						|
  If the return value is not NULL the memory must be caller freed.
 | 
						|
 | 
						|
  @param[in] OriginalCommandLine    The original command line
 | 
						|
 | 
						|
  @retval NULL                      An error occurred.
 | 
						|
  @return                           The new command line with no environment variables present.
 | 
						|
**/
 | 
						|
CHAR16 *
 | 
						|
ShellConvertVariables (
 | 
						|
  IN CONST CHAR16  *OriginalCommandLine
 | 
						|
  )
 | 
						|
{
 | 
						|
  CONST CHAR16  *MasterEnvList;
 | 
						|
  UINTN         NewSize;
 | 
						|
  CHAR16        *NewCommandLine1;
 | 
						|
  CHAR16        *NewCommandLine2;
 | 
						|
  CHAR16        *Temp;
 | 
						|
  UINTN         ItemSize;
 | 
						|
  CHAR16        *ItemTemp;
 | 
						|
  SCRIPT_FILE   *CurrentScriptFile;
 | 
						|
  ALIAS_LIST    *AliasListNode;
 | 
						|
 | 
						|
  ASSERT (OriginalCommandLine != NULL);
 | 
						|
 | 
						|
  ItemSize          = 0;
 | 
						|
  NewSize           = StrSize (OriginalCommandLine);
 | 
						|
  CurrentScriptFile = ShellCommandGetCurrentScriptFile ();
 | 
						|
  Temp              = NULL;
 | 
						|
 | 
						|
  /// @todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
 | 
						|
 | 
						|
  //
 | 
						|
  // calculate the size required for the post-conversion string...
 | 
						|
  //
 | 
						|
  if (CurrentScriptFile != NULL) {
 | 
						|
    for (AliasListNode = (ALIAS_LIST *)GetFirstNode (&CurrentScriptFile->SubstList)
 | 
						|
         ; !IsNull (&CurrentScriptFile->SubstList, &AliasListNode->Link)
 | 
						|
         ; AliasListNode = (ALIAS_LIST *)GetNextNode (&CurrentScriptFile->SubstList, &AliasListNode->Link)
 | 
						|
         )
 | 
						|
    {
 | 
						|
      for (Temp = StrStr (OriginalCommandLine, AliasListNode->Alias)
 | 
						|
           ; Temp != NULL
 | 
						|
           ; Temp = StrStr (Temp+1, AliasListNode->Alias)
 | 
						|
           )
 | 
						|
      {
 | 
						|
        //
 | 
						|
        // we need a preceding and if there is space no ^ preceding (if no space ignore)
 | 
						|
        //
 | 
						|
        if ((((Temp-OriginalCommandLine) > 2) && (*(Temp-2) != L'^')) || ((Temp-OriginalCommandLine) <= 2)) {
 | 
						|
          NewSize += StrSize (AliasListNode->CommandString);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  for (MasterEnvList = EfiShellGetEnv (NULL)
 | 
						|
       ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL // && *(MasterEnvList+1) != CHAR_NULL
 | 
						|
       ; MasterEnvList += StrLen (MasterEnvList) + 1
 | 
						|
       )
 | 
						|
  {
 | 
						|
    if (StrSize (MasterEnvList) > ItemSize) {
 | 
						|
      ItemSize = StrSize (MasterEnvList);
 | 
						|
    }
 | 
						|
 | 
						|
    for (Temp = StrStr (OriginalCommandLine, MasterEnvList)
 | 
						|
         ; Temp != NULL
 | 
						|
         ; Temp = StrStr (Temp+1, MasterEnvList)
 | 
						|
         )
 | 
						|
    {
 | 
						|
      //
 | 
						|
      // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)
 | 
						|
      //
 | 
						|
      if ((*(Temp-1) == L'%') && (*(Temp+StrLen (MasterEnvList)) == L'%') &&
 | 
						|
          ((((Temp-OriginalCommandLine) > 2) && (*(Temp-2) != L'^')) || ((Temp-OriginalCommandLine) <= 2)))
 | 
						|
      {
 | 
						|
        NewSize += StrSize (EfiShellGetEnv (MasterEnvList));
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // now do the replacements...
 | 
						|
  //
 | 
						|
  NewCommandLine1 = AllocateZeroPool (NewSize);
 | 
						|
  NewCommandLine2 = AllocateZeroPool (NewSize);
 | 
						|
  ItemTemp        = AllocateZeroPool (ItemSize+(2*sizeof (CHAR16)));
 | 
						|
  if ((NewCommandLine1 == NULL) || (NewCommandLine2 == NULL) || (ItemTemp == NULL)) {
 | 
						|
    SHELL_FREE_NON_NULL (NewCommandLine1);
 | 
						|
    SHELL_FREE_NON_NULL (NewCommandLine2);
 | 
						|
    SHELL_FREE_NON_NULL (ItemTemp);
 | 
						|
    return (NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  CopyMem (NewCommandLine1, OriginalCommandLine, StrSize (OriginalCommandLine));
 | 
						|
 | 
						|
  for (MasterEnvList = EfiShellGetEnv (NULL)
 | 
						|
       ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL
 | 
						|
       ; MasterEnvList += StrLen (MasterEnvList) + 1
 | 
						|
       )
 | 
						|
  {
 | 
						|
    StrCpyS (
 | 
						|
      ItemTemp,
 | 
						|
      ((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)),
 | 
						|
      L"%"
 | 
						|
      );
 | 
						|
    StrCatS (
 | 
						|
      ItemTemp,
 | 
						|
      ((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)),
 | 
						|
      MasterEnvList
 | 
						|
      );
 | 
						|
    StrCatS (
 | 
						|
      ItemTemp,
 | 
						|
      ((ItemSize+(2*sizeof (CHAR16)))/sizeof (CHAR16)),
 | 
						|
      L"%"
 | 
						|
      );
 | 
						|
    ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv (MasterEnvList), TRUE, FALSE);
 | 
						|
    StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), NewCommandLine2);
 | 
						|
  }
 | 
						|
 | 
						|
  if (CurrentScriptFile != NULL) {
 | 
						|
    for (AliasListNode = (ALIAS_LIST *)GetFirstNode (&CurrentScriptFile->SubstList)
 | 
						|
         ; !IsNull (&CurrentScriptFile->SubstList, &AliasListNode->Link)
 | 
						|
         ; AliasListNode = (ALIAS_LIST *)GetNextNode (&CurrentScriptFile->SubstList, &AliasListNode->Link)
 | 
						|
         )
 | 
						|
    {
 | 
						|
      ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);
 | 
						|
      StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), NewCommandLine2);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Remove non-existent environment variables
 | 
						|
  //
 | 
						|
  StripUnreplacedEnvironmentVariables (NewCommandLine1);
 | 
						|
 | 
						|
  //
 | 
						|
  // Now cleanup any straggler intentionally ignored "%" characters
 | 
						|
  //
 | 
						|
  ShellCopySearchAndReplace (NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);
 | 
						|
  StrCpyS (NewCommandLine1, NewSize/sizeof (CHAR16), NewCommandLine2);
 | 
						|
 | 
						|
  FreePool (NewCommandLine2);
 | 
						|
  FreePool (ItemTemp);
 | 
						|
 | 
						|
  return (NewCommandLine1);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Internal function to run a command line with pipe usage.
 | 
						|
 | 
						|
  @param[in] CmdLine        The pointer to the command line.
 | 
						|
  @param[in] StdIn          The pointer to the Standard input.
 | 
						|
  @param[in] StdOut         The pointer to the Standard output.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS       The split command is executed successfully.
 | 
						|
  @retval other             Some error occurs when executing the split command.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
RunSplitCommand (
 | 
						|
  IN CONST CHAR16             *CmdLine,
 | 
						|
  IN       SHELL_FILE_HANDLE  StdIn,
 | 
						|
  IN       SHELL_FILE_HANDLE  StdOut
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS         Status;
 | 
						|
  CHAR16             *NextCommandLine;
 | 
						|
  CHAR16             *OurCommandLine;
 | 
						|
  UINTN              Size1;
 | 
						|
  UINTN              Size2;
 | 
						|
  SPLIT_LIST         *Split;
 | 
						|
  SHELL_FILE_HANDLE  TempFileHandle;
 | 
						|
  BOOLEAN            Unicode;
 | 
						|
 | 
						|
  ASSERT (StdOut == NULL);
 | 
						|
 | 
						|
  ASSERT (StrStr (CmdLine, L"|") != NULL);
 | 
						|
 | 
						|
  Status          = EFI_SUCCESS;
 | 
						|
  NextCommandLine = NULL;
 | 
						|
  OurCommandLine  = NULL;
 | 
						|
  Size1           = 0;
 | 
						|
  Size2           = 0;
 | 
						|
 | 
						|
  NextCommandLine = StrnCatGrow (&NextCommandLine, &Size1, StrStr (CmdLine, L"|")+1, 0);
 | 
						|
  OurCommandLine  = StrnCatGrow (&OurCommandLine, &Size2, CmdLine, StrStr (CmdLine, L"|") - CmdLine);
 | 
						|
 | 
						|
  if ((NextCommandLine == NULL) || (OurCommandLine == NULL)) {
 | 
						|
    SHELL_FREE_NON_NULL (OurCommandLine);
 | 
						|
    SHELL_FREE_NON_NULL (NextCommandLine);
 | 
						|
    return (EFI_OUT_OF_RESOURCES);
 | 
						|
  } else if ((StrStr (OurCommandLine, L"|") != NULL) || (Size1 == 0) || (Size2 == 0)) {
 | 
						|
    SHELL_FREE_NON_NULL (OurCommandLine);
 | 
						|
    SHELL_FREE_NON_NULL (NextCommandLine);
 | 
						|
    return (EFI_INVALID_PARAMETER);
 | 
						|
  } else if ((NextCommandLine[0] == L'a') &&
 | 
						|
             ((NextCommandLine[1] == L' ') || (NextCommandLine[1] == CHAR_NULL))
 | 
						|
             )
 | 
						|
  {
 | 
						|
    CopyMem (NextCommandLine, NextCommandLine+1, StrSize (NextCommandLine) - sizeof (NextCommandLine[0]));
 | 
						|
    while (NextCommandLine[0] == L' ') {
 | 
						|
      CopyMem (NextCommandLine, NextCommandLine+1, StrSize (NextCommandLine) - sizeof (NextCommandLine[0]));
 | 
						|
    }
 | 
						|
 | 
						|
    if (NextCommandLine[0] == CHAR_NULL) {
 | 
						|
      SHELL_FREE_NON_NULL (OurCommandLine);
 | 
						|
      SHELL_FREE_NON_NULL (NextCommandLine);
 | 
						|
      return (EFI_INVALID_PARAMETER);
 | 
						|
    }
 | 
						|
 | 
						|
    Unicode = FALSE;
 | 
						|
  } else {
 | 
						|
    Unicode = TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // make a SPLIT_LIST item and add to list
 | 
						|
  //
 | 
						|
  Split = AllocateZeroPool (sizeof (SPLIT_LIST));
 | 
						|
  if (Split == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Split->SplitStdIn  = StdIn;
 | 
						|
  Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (Unicode), NULL);
 | 
						|
  ASSERT (Split->SplitStdOut != NULL);
 | 
						|
  InsertHeadList (&ShellInfoObject.SplitList.Link, &Split->Link);
 | 
						|
 | 
						|
  Status = RunCommand (OurCommandLine);
 | 
						|
 | 
						|
  //
 | 
						|
  // move the output from the first to the in to the second.
 | 
						|
  //
 | 
						|
  TempFileHandle = Split->SplitStdOut;
 | 
						|
  if (Split->SplitStdIn == StdIn) {
 | 
						|
    Split->SplitStdOut = NULL;
 | 
						|
  } else {
 | 
						|
    Split->SplitStdOut = Split->SplitStdIn;
 | 
						|
  }
 | 
						|
 | 
						|
  Split->SplitStdIn = TempFileHandle;
 | 
						|
  ShellInfoObject.NewEfiShellProtocol->SetFilePosition (Split->SplitStdIn, 0);
 | 
						|
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    Status = RunCommand (NextCommandLine);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // remove the top level from the ScriptList
 | 
						|
  //
 | 
						|
  ASSERT ((SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link) == Split);
 | 
						|
  RemoveEntryList (&Split->Link);
 | 
						|
 | 
						|
  //
 | 
						|
  // Note that the original StdIn is now the StdOut...
 | 
						|
  //
 | 
						|
  if (Split->SplitStdOut != NULL) {
 | 
						|
    ShellInfoObject.NewEfiShellProtocol->CloseFile (Split->SplitStdOut);
 | 
						|
  }
 | 
						|
 | 
						|
  if (Split->SplitStdIn != NULL) {
 | 
						|
    ShellInfoObject.NewEfiShellProtocol->CloseFile (Split->SplitStdIn);
 | 
						|
  }
 | 
						|
 | 
						|
  FreePool (Split);
 | 
						|
  FreePool (NextCommandLine);
 | 
						|
  FreePool (OurCommandLine);
 | 
						|
 | 
						|
  return (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Take the original command line, substitute any variables, free
 | 
						|
  the original string, return the modified copy.
 | 
						|
 | 
						|
  @param[in] CmdLine  pointer to the command line to update.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           the function was successful.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  a memory allocation failed.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
ShellSubstituteVariables (
 | 
						|
  IN CHAR16  **CmdLine
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR16  *NewCmdLine;
 | 
						|
 | 
						|
  NewCmdLine = ShellConvertVariables (*CmdLine);
 | 
						|
  SHELL_FREE_NON_NULL (*CmdLine);
 | 
						|
  if (NewCmdLine == NULL) {
 | 
						|
    return (EFI_OUT_OF_RESOURCES);
 | 
						|
  }
 | 
						|
 | 
						|
  *CmdLine = NewCmdLine;
 | 
						|
  return (EFI_SUCCESS);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Take the original command line, substitute any alias in the first group of space delimited characters, free
 | 
						|
  the original string, return the modified copy.
 | 
						|
 | 
						|
  @param[in] CmdLine  pointer to the command line to update.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           the function was successful.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  a memory allocation failed.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
ShellSubstituteAliases (
 | 
						|
  IN CHAR16  **CmdLine
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR16      *NewCmdLine;
 | 
						|
  CHAR16      *CommandName;
 | 
						|
  EFI_STATUS  Status;
 | 
						|
  UINTN       PostAliasSize;
 | 
						|
 | 
						|
  ASSERT (CmdLine != NULL);
 | 
						|
  ASSERT (*CmdLine != NULL);
 | 
						|
 | 
						|
  CommandName = NULL;
 | 
						|
  if (StrStr ((*CmdLine), L" ") == NULL) {
 | 
						|
    StrnCatGrow (&CommandName, NULL, (*CmdLine), 0);
 | 
						|
  } else {
 | 
						|
    StrnCatGrow (&CommandName, NULL, (*CmdLine), StrStr ((*CmdLine), L" ") - (*CmdLine));
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // This cannot happen 'inline' since the CmdLine can need extra space.
 | 
						|
  //
 | 
						|
  NewCmdLine = NULL;
 | 
						|
  if (!ShellCommandIsCommandOnList (CommandName)) {
 | 
						|
    //
 | 
						|
    // Convert via alias
 | 
						|
    //
 | 
						|
    Status = ShellConvertAlias (&CommandName);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return (Status);
 | 
						|
    }
 | 
						|
 | 
						|
    PostAliasSize = 0;
 | 
						|
    NewCmdLine    = StrnCatGrow (&NewCmdLine, &PostAliasSize, CommandName, 0);
 | 
						|
    if (NewCmdLine == NULL) {
 | 
						|
      SHELL_FREE_NON_NULL (CommandName);
 | 
						|
      SHELL_FREE_NON_NULL (*CmdLine);
 | 
						|
      return (EFI_OUT_OF_RESOURCES);
 | 
						|
    }
 | 
						|
 | 
						|
    NewCmdLine = StrnCatGrow (&NewCmdLine, &PostAliasSize, StrStr ((*CmdLine), L" "), 0);
 | 
						|
    if (NewCmdLine == NULL) {
 | 
						|
      SHELL_FREE_NON_NULL (CommandName);
 | 
						|
      SHELL_FREE_NON_NULL (*CmdLine);
 | 
						|
      return (EFI_OUT_OF_RESOURCES);
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    NewCmdLine = StrnCatGrow (&NewCmdLine, NULL, (*CmdLine), 0);
 | 
						|
  }
 | 
						|
 | 
						|
  SHELL_FREE_NON_NULL (*CmdLine);
 | 
						|
  SHELL_FREE_NON_NULL (CommandName);
 | 
						|
 | 
						|
  //
 | 
						|
  // re-assign the passed in double pointer to point to our newly allocated buffer
 | 
						|
  //
 | 
						|
  *CmdLine = NewCmdLine;
 | 
						|
 | 
						|
  return (EFI_SUCCESS);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Takes the Argv[0] part of the command line and determine the meaning of it.
 | 
						|
 | 
						|
  @param[in] CmdName  pointer to the command line to update.
 | 
						|
 | 
						|
  @retval Internal_Command    The name is an internal command.
 | 
						|
  @retval File_Sys_Change     the name is a file system change.
 | 
						|
  @retval Script_File_Name    the name is a NSH script file.
 | 
						|
  @retval Unknown_Invalid     the name is unknown.
 | 
						|
  @retval Efi_Application     the name is an application (.EFI).
 | 
						|
**/
 | 
						|
SHELL_OPERATION_TYPES
 | 
						|
GetOperationType (
 | 
						|
  IN CONST CHAR16  *CmdName
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR16        *FileWithPath;
 | 
						|
  CONST CHAR16  *TempLocation;
 | 
						|
  CONST CHAR16  *TempLocation2;
 | 
						|
 | 
						|
  FileWithPath = NULL;
 | 
						|
  //
 | 
						|
  // test for an internal command.
 | 
						|
  //
 | 
						|
  if (ShellCommandIsCommandOnList (CmdName)) {
 | 
						|
    return (Internal_Command);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Test for file system change request.  anything ending with first : and cant have spaces.
 | 
						|
  //
 | 
						|
  if (CmdName[(StrLen (CmdName)-1)] == L':') {
 | 
						|
    if (  (StrStr (CmdName, L" ") != NULL)
 | 
						|
       || (StrLen (StrStr (CmdName, L":")) > 1)
 | 
						|
          )
 | 
						|
    {
 | 
						|
      return (Unknown_Invalid);
 | 
						|
    }
 | 
						|
 | 
						|
    return (File_Sys_Change);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Test for a file
 | 
						|
  //
 | 
						|
  if ((FileWithPath = ShellFindFilePathEx (CmdName, mExecutableExtensions)) != NULL) {
 | 
						|
    //
 | 
						|
    // See if that file has a script file extension
 | 
						|
    //
 | 
						|
    if (StrLen (FileWithPath) > 4) {
 | 
						|
      TempLocation  = FileWithPath+StrLen (FileWithPath)-4;
 | 
						|
      TempLocation2 = mScriptExtension;
 | 
						|
      if (StringNoCaseCompare ((VOID *)(&TempLocation), (VOID *)(&TempLocation2)) == 0) {
 | 
						|
        SHELL_FREE_NON_NULL (FileWithPath);
 | 
						|
        return (Script_File_Name);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Was a file, but not a script.  we treat this as an application.
 | 
						|
    //
 | 
						|
    SHELL_FREE_NON_NULL (FileWithPath);
 | 
						|
    return (Efi_Application);
 | 
						|
  }
 | 
						|
 | 
						|
  SHELL_FREE_NON_NULL (FileWithPath);
 | 
						|
  //
 | 
						|
  // No clue what this is... return invalid flag...
 | 
						|
  //
 | 
						|
  return (Unknown_Invalid);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Determine if the first item in a command line is valid.
 | 
						|
 | 
						|
  @param[in] CmdLine            The command line to parse.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The item is valid.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
 | 
						|
  @retval EFI_NOT_FOUND         The operation type is unknown or invalid.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
IsValidSplit (
 | 
						|
  IN CONST CHAR16  *CmdLine
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR16      *Temp;
 | 
						|
  CHAR16      *FirstParameter;
 | 
						|
  CHAR16      *TempWalker;
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  Temp = NULL;
 | 
						|
 | 
						|
  Temp = StrnCatGrow (&Temp, NULL, CmdLine, 0);
 | 
						|
  if (Temp == NULL) {
 | 
						|
    return (EFI_OUT_OF_RESOURCES);
 | 
						|
  }
 | 
						|
 | 
						|
  FirstParameter = StrStr (Temp, L"|");
 | 
						|
  if (FirstParameter != NULL) {
 | 
						|
    *FirstParameter = CHAR_NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  FirstParameter = NULL;
 | 
						|
 | 
						|
  //
 | 
						|
  // Process the command line
 | 
						|
  //
 | 
						|
  Status = ProcessCommandLineToFinal (&Temp);
 | 
						|
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    FirstParameter = AllocateZeroPool (StrSize (CmdLine));
 | 
						|
    if (FirstParameter == NULL) {
 | 
						|
      SHELL_FREE_NON_NULL (Temp);
 | 
						|
      return (EFI_OUT_OF_RESOURCES);
 | 
						|
    }
 | 
						|
 | 
						|
    TempWalker = (CHAR16 *)Temp;
 | 
						|
    if (!EFI_ERROR (GetNextParameter (&TempWalker, &FirstParameter, StrSize (CmdLine), TRUE))) {
 | 
						|
      if (GetOperationType (FirstParameter) == Unknown_Invalid) {
 | 
						|
        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
 | 
						|
        SetLastError (SHELL_NOT_FOUND);
 | 
						|
        Status = EFI_NOT_FOUND;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  SHELL_FREE_NON_NULL (Temp);
 | 
						|
  SHELL_FREE_NON_NULL (FirstParameter);
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Determine if a command line contains with a split contains only valid commands.
 | 
						|
 | 
						|
  @param[in] CmdLine      The command line to parse.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS     CmdLine has only valid commands, application, or has no split.
 | 
						|
  @retval EFI_ABORTED     CmdLine has at least one invalid command or application.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
VerifySplit (
 | 
						|
  IN CONST CHAR16  *CmdLine
 | 
						|
  )
 | 
						|
{
 | 
						|
  CONST CHAR16  *TempSpot;
 | 
						|
  EFI_STATUS    Status;
 | 
						|
 | 
						|
  //
 | 
						|
  // If this was the only item, then get out
 | 
						|
  //
 | 
						|
  if (!ContainsSplit (CmdLine)) {
 | 
						|
    return (EFI_SUCCESS);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Verify up to the pipe or end character
 | 
						|
  //
 | 
						|
  Status = IsValidSplit (CmdLine);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // recurse to verify the next item
 | 
						|
  //
 | 
						|
  TempSpot = FindFirstCharacter (CmdLine, L"|", L'^') + 1;
 | 
						|
  if ((*TempSpot == L'a') &&
 | 
						|
      ((*(TempSpot + 1) == L' ') || (*(TempSpot + 1) == CHAR_NULL))
 | 
						|
      )
 | 
						|
  {
 | 
						|
    // If it's an ASCII pipe '|a'
 | 
						|
    TempSpot += 1;
 | 
						|
  }
 | 
						|
 | 
						|
  return (VerifySplit (TempSpot));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Process a split based operation.
 | 
						|
 | 
						|
  @param[in] CmdLine    pointer to the command line to process
 | 
						|
 | 
						|
  @retval EFI_SUCCESS   The operation was successful
 | 
						|
  @return               an error occurred.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
ProcessNewSplitCommandLine (
 | 
						|
  IN CONST CHAR16  *CmdLine
 | 
						|
  )
 | 
						|
{
 | 
						|
  SPLIT_LIST  *Split;
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  Status = VerifySplit (CmdLine);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  Split = NULL;
 | 
						|
 | 
						|
  //
 | 
						|
  // are we in an existing split???
 | 
						|
  //
 | 
						|
  if (!IsListEmpty (&ShellInfoObject.SplitList.Link)) {
 | 
						|
    Split = (SPLIT_LIST *)GetFirstNode (&ShellInfoObject.SplitList.Link);
 | 
						|
  }
 | 
						|
 | 
						|
  if (Split == NULL) {
 | 
						|
    Status = RunSplitCommand (CmdLine, NULL, NULL);
 | 
						|
  } else {
 | 
						|
    Status = RunSplitCommand (CmdLine, Split->SplitStdIn, Split->SplitStdOut);
 | 
						|
  }
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);
 | 
						|
  }
 | 
						|
 | 
						|
  return (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Handle a request to change the current file system.
 | 
						|
 | 
						|
  @param[in] CmdLine  The passed in command line.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS The operation was successful.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
ChangeMappedDrive (
 | 
						|
  IN CONST CHAR16  *CmdLine
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
 | 
						|
  //
 | 
						|
  // make sure we are the right operation
 | 
						|
  //
 | 
						|
  ASSERT (CmdLine[(StrLen (CmdLine)-1)] == L':' && StrStr (CmdLine, L" ") == NULL);
 | 
						|
 | 
						|
  //
 | 
						|
  // Call the protocol API to do the work
 | 
						|
  //
 | 
						|
  Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir (NULL, CmdLine);
 | 
						|
 | 
						|
  //
 | 
						|
  // Report any errors
 | 
						|
  //
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine);
 | 
						|
  }
 | 
						|
 | 
						|
  return (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Reprocess the command line to direct all -? to the help command.
 | 
						|
 | 
						|
  if found, will add "help" as argv[0], and move the rest later.
 | 
						|
 | 
						|
  @param[in,out] CmdLine        pointer to the command line to update
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
DoHelpUpdate (
 | 
						|
  IN OUT CHAR16  **CmdLine
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR16      *CurrentParameter;
 | 
						|
  CHAR16      *Walker;
 | 
						|
  CHAR16      *NewCommandLine;
 | 
						|
  EFI_STATUS  Status;
 | 
						|
  UINTN       NewCmdLineSize;
 | 
						|
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
 | 
						|
  CurrentParameter = AllocateZeroPool (StrSize (*CmdLine));
 | 
						|
  if (CurrentParameter == NULL) {
 | 
						|
    return (EFI_OUT_OF_RESOURCES);
 | 
						|
  }
 | 
						|
 | 
						|
  Walker = *CmdLine;
 | 
						|
  while (Walker != NULL && *Walker != CHAR_NULL) {
 | 
						|
    if (!EFI_ERROR (GetNextParameter (&Walker, &CurrentParameter, StrSize (*CmdLine), TRUE))) {
 | 
						|
      if (StrStr (CurrentParameter, L"-?") == CurrentParameter) {
 | 
						|
        CurrentParameter[0] = L' ';
 | 
						|
        CurrentParameter[1] = L' ';
 | 
						|
        NewCmdLineSize      = StrSize (L"help ") + StrSize (*CmdLine);
 | 
						|
        NewCommandLine      = AllocateZeroPool (NewCmdLineSize);
 | 
						|
        if (NewCommandLine == NULL) {
 | 
						|
          Status = EFI_OUT_OF_RESOURCES;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        //
 | 
						|
        // We know the space is sufficient since we just calculated it.
 | 
						|
        //
 | 
						|
        StrnCpyS (NewCommandLine, NewCmdLineSize/sizeof (CHAR16), L"help ", 5);
 | 
						|
        StrnCatS (NewCommandLine, NewCmdLineSize/sizeof (CHAR16), *CmdLine, StrLen (*CmdLine));
 | 
						|
        SHELL_FREE_NON_NULL (*CmdLine);
 | 
						|
        *CmdLine = NewCommandLine;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  SHELL_FREE_NON_NULL (CurrentParameter);
 | 
						|
 | 
						|
  return (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Function to update the shell variable "lasterror".
 | 
						|
 | 
						|
  @param[in] ErrorCode      the error code to put into lasterror.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
SetLastError (
 | 
						|
  IN CONST SHELL_STATUS  ErrorCode
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR16  LeString[19];
 | 
						|
 | 
						|
  if (sizeof (EFI_STATUS) == sizeof (UINT64)) {
 | 
						|
    UnicodeSPrint (LeString, sizeof (LeString), L"0x%Lx", ErrorCode);
 | 
						|
  } else {
 | 
						|
    UnicodeSPrint (LeString, sizeof (LeString), L"0x%x", ErrorCode);
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG_CODE (
 | 
						|
    InternalEfiShellSetEnv (L"debuglasterror", LeString, TRUE);
 | 
						|
    );
 | 
						|
  InternalEfiShellSetEnv (L"lasterror", LeString, TRUE);
 | 
						|
 | 
						|
  return (EFI_SUCCESS);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Converts the command line to its post-processed form.  this replaces variables and alias' per UEFI Shell spec.
 | 
						|
 | 
						|
  @param[in,out] CmdLine        pointer to the command line to update
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The operation was successful
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
 | 
						|
  @return                       some other error occurred
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
ProcessCommandLineToFinal (
 | 
						|
  IN OUT CHAR16  **CmdLine
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  TrimSpaces (CmdLine);
 | 
						|
 | 
						|
  Status = ShellSubstituteAliases (CmdLine);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  TrimSpaces (CmdLine);
 | 
						|
 | 
						|
  Status = ShellSubstituteVariables (CmdLine);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  ASSERT (*CmdLine != NULL);
 | 
						|
 | 
						|
  TrimSpaces (CmdLine);
 | 
						|
 | 
						|
  //
 | 
						|
  // update for help parsing
 | 
						|
  //
 | 
						|
  if (StrStr (*CmdLine, L"?") != NULL) {
 | 
						|
    //
 | 
						|
    // This may do nothing if the ? does not indicate help.
 | 
						|
    // Save all the details for in the API below.
 | 
						|
    //
 | 
						|
    Status = DoHelpUpdate (CmdLine);
 | 
						|
  }
 | 
						|
 | 
						|
  TrimSpaces (CmdLine);
 | 
						|
 | 
						|
  return (EFI_SUCCESS);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Run an internal shell command.
 | 
						|
 | 
						|
  This API will update the shell's environment since these commands are libraries.
 | 
						|
 | 
						|
  @param[in] CmdLine          the command line to run.
 | 
						|
  @param[in] FirstParameter   the first parameter on the command line
 | 
						|
  @param[in] ParamProtocol    the shell parameters protocol pointer
 | 
						|
  @param[out] CommandStatus   the status from the command line.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS     The command was completed.
 | 
						|
  @retval EFI_ABORTED     The command's operation was aborted.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
RunInternalCommand (
 | 
						|
  IN CONST CHAR16                   *CmdLine,
 | 
						|
  IN       CHAR16                   *FirstParameter,
 | 
						|
  IN EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,
 | 
						|
  OUT EFI_STATUS                    *CommandStatus
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS    Status;
 | 
						|
  UINTN         Argc;
 | 
						|
  CHAR16        **Argv;
 | 
						|
  SHELL_STATUS  CommandReturnedStatus;
 | 
						|
  BOOLEAN       LastError;
 | 
						|
  CHAR16        *Walker;
 | 
						|
  CHAR16        *NewCmdLine;
 | 
						|
 | 
						|
  NewCmdLine = AllocateCopyPool (StrSize (CmdLine), CmdLine);
 | 
						|
  if (NewCmdLine == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL; Walker++) {
 | 
						|
    if ((*Walker == L'^') && (*(Walker+1) == L'#')) {
 | 
						|
      CopyMem (Walker, Walker+1, StrSize (Walker) - sizeof (Walker[0]));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // get the argc and argv updated for internal commands
 | 
						|
  //
 | 
						|
  Status = UpdateArgcArgv (ParamProtocol, NewCmdLine, Internal_Command, &Argv, &Argc);
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    //
 | 
						|
    // Run the internal command.
 | 
						|
    //
 | 
						|
    Status = ShellCommandRunCommandHandler (FirstParameter, &CommandReturnedStatus, &LastError);
 | 
						|
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      if (CommandStatus != NULL) {
 | 
						|
        if (CommandReturnedStatus != SHELL_SUCCESS) {
 | 
						|
          *CommandStatus = (EFI_STATUS)(CommandReturnedStatus | MAX_BIT);
 | 
						|
        } else {
 | 
						|
          *CommandStatus = EFI_SUCCESS;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      // Update last error status.
 | 
						|
      // some commands do not update last error.
 | 
						|
      //
 | 
						|
      if (LastError) {
 | 
						|
        SetLastError (CommandReturnedStatus);
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      // Pass thru the exitcode from the app.
 | 
						|
      //
 | 
						|
      if (ShellCommandGetExit ()) {
 | 
						|
        //
 | 
						|
        // An Exit was requested ("exit" command), pass its value up.
 | 
						|
        //
 | 
						|
        Status = CommandReturnedStatus;
 | 
						|
      } else if ((CommandReturnedStatus != SHELL_SUCCESS) && IsScriptOnlyCommand (FirstParameter)) {
 | 
						|
        //
 | 
						|
        // Always abort when a script only command fails for any reason
 | 
						|
        //
 | 
						|
        Status = EFI_ABORTED;
 | 
						|
      } else if ((ShellCommandGetCurrentScriptFile () != NULL) && (CommandReturnedStatus == SHELL_ABORTED)) {
 | 
						|
        //
 | 
						|
        // Abort when in a script and a command aborted
 | 
						|
        //
 | 
						|
        Status = EFI_ABORTED;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
 | 
						|
  // This is safe even if the update API failed.  In this case, it may be a no-op.
 | 
						|
  //
 | 
						|
  RestoreArgcArgv (ParamProtocol, &Argv, &Argc);
 | 
						|
 | 
						|
  //
 | 
						|
  // If a script is running and the command is not a script only command, then
 | 
						|
  // change return value to success so the script won't halt (unless aborted).
 | 
						|
  //
 | 
						|
  // Script only commands have to be able halt the script since the script will
 | 
						|
  // not operate if they are failing.
 | 
						|
  //
 | 
						|
  if (  (ShellCommandGetCurrentScriptFile () != NULL)
 | 
						|
     && !IsScriptOnlyCommand (FirstParameter)
 | 
						|
     && (Status != EFI_ABORTED)
 | 
						|
        )
 | 
						|
  {
 | 
						|
    Status = EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  FreePool (NewCmdLine);
 | 
						|
  return (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Function to run the command or file.
 | 
						|
 | 
						|
  @param[in] Type             the type of operation being run.
 | 
						|
  @param[in] CmdLine          the command line to run.
 | 
						|
  @param[in] FirstParameter   the first parameter on the command line
 | 
						|
  @param[in] ParamProtocol    the shell parameters protocol pointer
 | 
						|
  @param[out] CommandStatus   the status from the command line.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS     The command was completed.
 | 
						|
  @retval EFI_ABORTED     The command's operation was aborted.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
RunCommandOrFile (
 | 
						|
  IN       SHELL_OPERATION_TYPES    Type,
 | 
						|
  IN CONST CHAR16                   *CmdLine,
 | 
						|
  IN       CHAR16                   *FirstParameter,
 | 
						|
  IN EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,
 | 
						|
  OUT EFI_STATUS                    *CommandStatus
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                Status;
 | 
						|
  EFI_STATUS                StartStatus;
 | 
						|
  CHAR16                    *CommandWithPath;
 | 
						|
  CHAR16                    *FullCommandWithPath;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL  *DevPath;
 | 
						|
  SHELL_STATUS              CalleeExitStatus;
 | 
						|
 | 
						|
  Status           = EFI_SUCCESS;
 | 
						|
  CommandWithPath  = NULL;
 | 
						|
  DevPath          = NULL;
 | 
						|
  CalleeExitStatus = SHELL_INVALID_PARAMETER;
 | 
						|
 | 
						|
  switch (Type) {
 | 
						|
    case Internal_Command:
 | 
						|
      Status = RunInternalCommand (CmdLine, FirstParameter, ParamProtocol, CommandStatus);
 | 
						|
      break;
 | 
						|
    case Script_File_Name:
 | 
						|
    case Efi_Application:
 | 
						|
      //
 | 
						|
      // Process a fully qualified path
 | 
						|
      //
 | 
						|
      if (StrStr (FirstParameter, L":") != NULL) {
 | 
						|
        ASSERT (CommandWithPath == NULL);
 | 
						|
        if (ShellIsFile (FirstParameter) == EFI_SUCCESS) {
 | 
						|
          CommandWithPath = StrnCatGrow (&CommandWithPath, NULL, FirstParameter, 0);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      // Process a relative path and also check in the path environment variable
 | 
						|
      //
 | 
						|
      if (CommandWithPath == NULL) {
 | 
						|
        CommandWithPath = ShellFindFilePathEx (FirstParameter, mExecutableExtensions);
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      // This should be impossible now.
 | 
						|
      //
 | 
						|
      ASSERT (CommandWithPath != NULL);
 | 
						|
 | 
						|
      //
 | 
						|
      // Make sure that path is not just a directory (or not found)
 | 
						|
      //
 | 
						|
      if (!EFI_ERROR (ShellIsDirectory (CommandWithPath))) {
 | 
						|
        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
 | 
						|
        SetLastError (SHELL_NOT_FOUND);
 | 
						|
      }
 | 
						|
 | 
						|
      switch (Type) {
 | 
						|
        case Script_File_Name:
 | 
						|
          FullCommandWithPath = FullyQualifyPath (CommandWithPath);
 | 
						|
          if (FullCommandWithPath == NULL) {
 | 
						|
            Status = RunScriptFile (CommandWithPath, NULL, CmdLine, ParamProtocol);
 | 
						|
          } else {
 | 
						|
            Status = RunScriptFile (FullCommandWithPath, NULL, CmdLine, ParamProtocol);
 | 
						|
            FreePool (FullCommandWithPath);
 | 
						|
          }
 | 
						|
 | 
						|
          break;
 | 
						|
        case Efi_Application:
 | 
						|
          //
 | 
						|
          // Get the device path of the application image
 | 
						|
          //
 | 
						|
          DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath (CommandWithPath);
 | 
						|
          if (DevPath == NULL) {
 | 
						|
            Status = EFI_OUT_OF_RESOURCES;
 | 
						|
            break;
 | 
						|
          }
 | 
						|
 | 
						|
          //
 | 
						|
          // Execute the device path
 | 
						|
          //
 | 
						|
          Status = InternalShellExecuteDevicePath (
 | 
						|
                     &gImageHandle,
 | 
						|
                     DevPath,
 | 
						|
                     CmdLine,
 | 
						|
                     NULL,
 | 
						|
                     &StartStatus
 | 
						|
                     );
 | 
						|
 | 
						|
          SHELL_FREE_NON_NULL (DevPath);
 | 
						|
 | 
						|
          if (EFI_ERROR (Status)) {
 | 
						|
            CalleeExitStatus = (SHELL_STATUS)(Status & (~MAX_BIT));
 | 
						|
          } else {
 | 
						|
            CalleeExitStatus = (SHELL_STATUS)StartStatus;
 | 
						|
          }
 | 
						|
 | 
						|
          if (CommandStatus != NULL) {
 | 
						|
            *CommandStatus = CalleeExitStatus;
 | 
						|
          }
 | 
						|
 | 
						|
          //
 | 
						|
          // Update last error status.
 | 
						|
          //
 | 
						|
          // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
 | 
						|
          SetLastError (CalleeExitStatus);
 | 
						|
          break;
 | 
						|
        default:
 | 
						|
          //
 | 
						|
          // Do nothing.
 | 
						|
          //
 | 
						|
          break;
 | 
						|
      }
 | 
						|
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      //
 | 
						|
      // Do nothing.
 | 
						|
      //
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  SHELL_FREE_NON_NULL (CommandWithPath);
 | 
						|
 | 
						|
  return (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Function to setup StdIn, StdErr, StdOut, and then run the command or file.
 | 
						|
 | 
						|
  @param[in] Type             the type of operation being run.
 | 
						|
  @param[in] CmdLine          the command line to run.
 | 
						|
  @param[in] FirstParameter   the first parameter on the command line.
 | 
						|
  @param[in] ParamProtocol    the shell parameters protocol pointer
 | 
						|
  @param[out] CommandStatus   the status from the command line.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS     The command was completed.
 | 
						|
  @retval EFI_ABORTED     The command's operation was aborted.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
SetupAndRunCommandOrFile (
 | 
						|
  IN   SHELL_OPERATION_TYPES          Type,
 | 
						|
  IN   CHAR16                         *CmdLine,
 | 
						|
  IN   CHAR16                         *FirstParameter,
 | 
						|
  IN   EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,
 | 
						|
  OUT EFI_STATUS                      *CommandStatus
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS         Status;
 | 
						|
  SHELL_FILE_HANDLE  OriginalStdIn;
 | 
						|
  SHELL_FILE_HANDLE  OriginalStdOut;
 | 
						|
  SHELL_FILE_HANDLE  OriginalStdErr;
 | 
						|
  SYSTEM_TABLE_INFO  OriginalSystemTableInfo;
 | 
						|
  CONST SCRIPT_FILE  *ConstScriptFile;
 | 
						|
 | 
						|
  //
 | 
						|
  // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
 | 
						|
  //
 | 
						|
  Status = UpdateStdInStdOutStdErr (ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
 | 
						|
 | 
						|
  //
 | 
						|
  // The StdIn, StdOut, and StdErr are set up.
 | 
						|
  // Now run the command, script, or application
 | 
						|
  //
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    TrimSpaces (&CmdLine);
 | 
						|
    Status = RunCommandOrFile (Type, CmdLine, FirstParameter, ParamProtocol, CommandStatus);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Now print errors
 | 
						|
  //
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    ConstScriptFile = ShellCommandGetCurrentScriptFile ();
 | 
						|
    if ((ConstScriptFile == NULL) || (ConstScriptFile->CurrentCommand == NULL)) {
 | 
						|
      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID *)(Status));
 | 
						|
    } else {
 | 
						|
      ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR_SCRIPT), ShellInfoObject.HiiHandle, (VOID *)(Status), ConstScriptFile->CurrentCommand->Line);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // put back the original StdIn, StdOut, and StdErr
 | 
						|
  //
 | 
						|
  RestoreStdInStdOutStdErr (ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
 | 
						|
 | 
						|
  return (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Function will process and run a command line.
 | 
						|
 | 
						|
  This will determine if the command line represents an internal shell
 | 
						|
  command or dispatch an external application.
 | 
						|
 | 
						|
  @param[in] CmdLine      The command line to parse.
 | 
						|
  @param[out] CommandStatus   The status from the command line.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS     The command was completed.
 | 
						|
  @retval EFI_ABORTED     The command's operation was aborted.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
RunShellCommand (
 | 
						|
  IN CONST CHAR16  *CmdLine,
 | 
						|
  OUT EFI_STATUS   *CommandStatus
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS             Status;
 | 
						|
  CHAR16                 *CleanOriginal;
 | 
						|
  CHAR16                 *FirstParameter;
 | 
						|
  CHAR16                 *TempWalker;
 | 
						|
  SHELL_OPERATION_TYPES  Type;
 | 
						|
  CONST CHAR16           *CurDir;
 | 
						|
 | 
						|
  ASSERT (CmdLine != NULL);
 | 
						|
  if (StrLen (CmdLine) == 0) {
 | 
						|
    return (EFI_SUCCESS);
 | 
						|
  }
 | 
						|
 | 
						|
  Status        = EFI_SUCCESS;
 | 
						|
  CleanOriginal = NULL;
 | 
						|
 | 
						|
  CleanOriginal = StrnCatGrow (&CleanOriginal, NULL, CmdLine, 0);
 | 
						|
  if (CleanOriginal == NULL) {
 | 
						|
    return (EFI_OUT_OF_RESOURCES);
 | 
						|
  }
 | 
						|
 | 
						|
  TrimSpaces (&CleanOriginal);
 | 
						|
 | 
						|
  //
 | 
						|
  // NULL out comments (leveraged from RunScriptFileHandle() ).
 | 
						|
  // The # character on a line is used to denote that all characters on the same line
 | 
						|
  // and to the right of the # are to be ignored by the shell.
 | 
						|
  // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.
 | 
						|
  //
 | 
						|
  for (TempWalker = CleanOriginal; TempWalker != NULL && *TempWalker != CHAR_NULL; TempWalker++) {
 | 
						|
    if (*TempWalker == L'^') {
 | 
						|
      if (*(TempWalker + 1) == L'#') {
 | 
						|
        TempWalker++;
 | 
						|
      }
 | 
						|
    } else if (*TempWalker == L'#') {
 | 
						|
      *TempWalker = CHAR_NULL;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  TrimSpaces (&CleanOriginal);
 | 
						|
 | 
						|
  //
 | 
						|
  // Handle case that passed in command line is just 1 or more " " characters.
 | 
						|
  //
 | 
						|
  if (StrLen (CleanOriginal) == 0) {
 | 
						|
    SHELL_FREE_NON_NULL (CleanOriginal);
 | 
						|
    return (EFI_SUCCESS);
 | 
						|
  }
 | 
						|
 | 
						|
  Status = ProcessCommandLineToFinal (&CleanOriginal);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    SHELL_FREE_NON_NULL (CleanOriginal);
 | 
						|
    return (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // We don't do normal processing with a split command line (output from one command input to another)
 | 
						|
  //
 | 
						|
  if (ContainsSplit (CleanOriginal)) {
 | 
						|
    Status = ProcessNewSplitCommandLine (CleanOriginal);
 | 
						|
    SHELL_FREE_NON_NULL (CleanOriginal);
 | 
						|
    return (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // We need the first parameter information so we can determine the operation type
 | 
						|
  //
 | 
						|
  FirstParameter = AllocateZeroPool (StrSize (CleanOriginal));
 | 
						|
  if (FirstParameter == NULL) {
 | 
						|
    SHELL_FREE_NON_NULL (CleanOriginal);
 | 
						|
    return (EFI_OUT_OF_RESOURCES);
 | 
						|
  }
 | 
						|
 | 
						|
  TempWalker = CleanOriginal;
 | 
						|
  if (!EFI_ERROR (GetNextParameter (&TempWalker, &FirstParameter, StrSize (CleanOriginal), TRUE))) {
 | 
						|
    //
 | 
						|
    // Depending on the first parameter we change the behavior
 | 
						|
    //
 | 
						|
    switch (Type = GetOperationType (FirstParameter)) {
 | 
						|
      case File_Sys_Change:
 | 
						|
        Status = ChangeMappedDrive (FirstParameter);
 | 
						|
        break;
 | 
						|
      case Internal_Command:
 | 
						|
      case Script_File_Name:
 | 
						|
      case Efi_Application:
 | 
						|
        Status = SetupAndRunCommandOrFile (Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol, CommandStatus);
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        //
 | 
						|
        // Whatever was typed, it was invalid.
 | 
						|
        //
 | 
						|
        ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
 | 
						|
        SetLastError (SHELL_NOT_FOUND);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
 | 
						|
    SetLastError (SHELL_NOT_FOUND);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Check whether the current file system still exists. If not exist, we need update "cwd" and gShellCurMapping.
 | 
						|
  //
 | 
						|
  CurDir = EfiShellGetCurDir (NULL);
 | 
						|
  if (CurDir != NULL) {
 | 
						|
    if (EFI_ERROR (ShellFileExists (CurDir))) {
 | 
						|
      //
 | 
						|
      // EfiShellSetCurDir() cannot set current directory to NULL.
 | 
						|
      // EfiShellSetEnv() is not allowed to set the "cwd" variable.
 | 
						|
      // Only InternalEfiShellSetEnv () is allowed setting the "cwd" variable.
 | 
						|
      //
 | 
						|
      InternalEfiShellSetEnv (L"cwd", NULL, TRUE);
 | 
						|
      gShellCurMapping = NULL;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  SHELL_FREE_NON_NULL (CleanOriginal);
 | 
						|
  SHELL_FREE_NON_NULL (FirstParameter);
 | 
						|
 | 
						|
  return (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Function will process and run a command line.
 | 
						|
 | 
						|
  This will determine if the command line represents an internal shell
 | 
						|
  command or dispatch an external application.
 | 
						|
 | 
						|
  @param[in] CmdLine      The command line to parse.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS     The command was completed.
 | 
						|
  @retval EFI_ABORTED     The command's operation was aborted.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
RunCommand (
 | 
						|
  IN CONST CHAR16  *CmdLine
 | 
						|
  )
 | 
						|
{
 | 
						|
  return (RunShellCommand (CmdLine, NULL));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Function to process a NSH script file via SHELL_FILE_HANDLE.
 | 
						|
 | 
						|
  @param[in] Handle             The handle to the already opened file.
 | 
						|
  @param[in] Name               The name of the script file.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           the script completed successfully
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
RunScriptFileHandle (
 | 
						|
  IN SHELL_FILE_HANDLE  Handle,
 | 
						|
  IN CONST CHAR16       *Name
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS           Status;
 | 
						|
  SCRIPT_FILE          *NewScriptFile;
 | 
						|
  UINTN                LoopVar;
 | 
						|
  UINTN                PrintBuffSize;
 | 
						|
  CHAR16               *CommandLine;
 | 
						|
  CHAR16               *CommandLine2;
 | 
						|
  CHAR16               *CommandLine3;
 | 
						|
  SCRIPT_COMMAND_LIST  *LastCommand;
 | 
						|
  BOOLEAN              Ascii;
 | 
						|
  BOOLEAN              PreScriptEchoState;
 | 
						|
  BOOLEAN              PreCommandEchoState;
 | 
						|
  CONST CHAR16         *CurDir;
 | 
						|
  UINTN                LineCount;
 | 
						|
  CHAR16               LeString[50];
 | 
						|
  LIST_ENTRY           OldBufferList;
 | 
						|
 | 
						|
  ASSERT (!ShellCommandGetScriptExit ());
 | 
						|
 | 
						|
  PreScriptEchoState = ShellCommandGetEchoState ();
 | 
						|
  PrintBuffSize      = PcdGet32 (PcdShellPrintBufferSize);
 | 
						|
 | 
						|
  NewScriptFile = (SCRIPT_FILE *)AllocateZeroPool (sizeof (SCRIPT_FILE));
 | 
						|
  if (NewScriptFile == NULL) {
 | 
						|
    return (EFI_OUT_OF_RESOURCES);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Set up the name
 | 
						|
  //
 | 
						|
  ASSERT (NewScriptFile->ScriptName == NULL);
 | 
						|
  NewScriptFile->ScriptName = StrnCatGrow (&NewScriptFile->ScriptName, NULL, Name, 0);
 | 
						|
  if (NewScriptFile->ScriptName == NULL) {
 | 
						|
    DeleteScriptFileStruct (NewScriptFile);
 | 
						|
    return (EFI_OUT_OF_RESOURCES);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Save the parameters (used to replace %0 to %9 later on)
 | 
						|
  //
 | 
						|
  NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;
 | 
						|
  if (NewScriptFile->Argc != 0) {
 | 
						|
    NewScriptFile->Argv = (CHAR16 **)AllocateZeroPool (NewScriptFile->Argc * sizeof (CHAR16 *));
 | 
						|
    if (NewScriptFile->Argv == NULL) {
 | 
						|
      DeleteScriptFileStruct (NewScriptFile);
 | 
						|
      return (EFI_OUT_OF_RESOURCES);
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Put the full path of the script file into Argv[0] as required by section
 | 
						|
    // 3.6.2 of version 2.2 of the shell specification.
 | 
						|
    //
 | 
						|
    NewScriptFile->Argv[0] = StrnCatGrow (&NewScriptFile->Argv[0], NULL, NewScriptFile->ScriptName, 0);
 | 
						|
    for (LoopVar = 1; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {
 | 
						|
      ASSERT (NewScriptFile->Argv[LoopVar] == NULL);
 | 
						|
      NewScriptFile->Argv[LoopVar] = StrnCatGrow (&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);
 | 
						|
      if (NewScriptFile->Argv[LoopVar] == NULL) {
 | 
						|
        DeleteScriptFileStruct (NewScriptFile);
 | 
						|
        return (EFI_OUT_OF_RESOURCES);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    NewScriptFile->Argv = NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  InitializeListHead (&NewScriptFile->CommandList);
 | 
						|
  InitializeListHead (&NewScriptFile->SubstList);
 | 
						|
 | 
						|
  //
 | 
						|
  // Now build the list of all script commands.
 | 
						|
  //
 | 
						|
  LineCount = 0;
 | 
						|
  while (!ShellFileHandleEof (Handle)) {
 | 
						|
    CommandLine = ShellFileHandleReturnLine (Handle, &Ascii);
 | 
						|
    LineCount++;
 | 
						|
    if ((CommandLine == NULL) || (StrLen (CommandLine) == 0) || (CommandLine[0] == '#')) {
 | 
						|
      SHELL_FREE_NON_NULL (CommandLine);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    NewScriptFile->CurrentCommand = AllocateZeroPool (sizeof (SCRIPT_COMMAND_LIST));
 | 
						|
    if (NewScriptFile->CurrentCommand == NULL) {
 | 
						|
      SHELL_FREE_NON_NULL (CommandLine);
 | 
						|
      DeleteScriptFileStruct (NewScriptFile);
 | 
						|
      return (EFI_OUT_OF_RESOURCES);
 | 
						|
    }
 | 
						|
 | 
						|
    NewScriptFile->CurrentCommand->Cl   = CommandLine;
 | 
						|
    NewScriptFile->CurrentCommand->Data = NULL;
 | 
						|
    NewScriptFile->CurrentCommand->Line = LineCount;
 | 
						|
 | 
						|
    InsertTailList (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Add this as the topmost script file
 | 
						|
  //
 | 
						|
  ShellCommandSetNewScript (NewScriptFile);
 | 
						|
 | 
						|
  //
 | 
						|
  // Now enumerate through the commands and run each one.
 | 
						|
  //
 | 
						|
  CommandLine = AllocateZeroPool (PrintBuffSize);
 | 
						|
  if (CommandLine == NULL) {
 | 
						|
    DeleteScriptFileStruct (NewScriptFile);
 | 
						|
    return (EFI_OUT_OF_RESOURCES);
 | 
						|
  }
 | 
						|
 | 
						|
  CommandLine2 = AllocateZeroPool (PrintBuffSize);
 | 
						|
  if (CommandLine2 == NULL) {
 | 
						|
    FreePool (CommandLine);
 | 
						|
    DeleteScriptFileStruct (NewScriptFile);
 | 
						|
    return (EFI_OUT_OF_RESOURCES);
 | 
						|
  }
 | 
						|
 | 
						|
  for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode (&NewScriptFile->CommandList)
 | 
						|
        ; !IsNull (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)
 | 
						|
        ; // conditional increment in the body of the loop
 | 
						|
        )
 | 
						|
  {
 | 
						|
    ASSERT (CommandLine2 != NULL);
 | 
						|
    StrnCpyS (
 | 
						|
      CommandLine2,
 | 
						|
      PrintBuffSize/sizeof (CHAR16),
 | 
						|
      NewScriptFile->CurrentCommand->Cl,
 | 
						|
      PrintBuffSize/sizeof (CHAR16) - 1
 | 
						|
      );
 | 
						|
 | 
						|
    SaveBufferList (&OldBufferList);
 | 
						|
 | 
						|
    //
 | 
						|
    // NULL out comments
 | 
						|
    //
 | 
						|
    for (CommandLine3 = CommandLine2; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL; CommandLine3++) {
 | 
						|
      if (*CommandLine3 == L'^') {
 | 
						|
        if ( *(CommandLine3+1) == L':') {
 | 
						|
          CopyMem (CommandLine3, CommandLine3+1, StrSize (CommandLine3) - sizeof (CommandLine3[0]));
 | 
						|
        } else if (*(CommandLine3+1) == L'#') {
 | 
						|
          CommandLine3++;
 | 
						|
        }
 | 
						|
      } else if (*CommandLine3 == L'#') {
 | 
						|
        *CommandLine3 = CHAR_NULL;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if ((CommandLine2 != NULL) && (StrLen (CommandLine2) >= 1)) {
 | 
						|
      //
 | 
						|
      // Due to variability in starting the find and replace action we need to have both buffers the same.
 | 
						|
      //
 | 
						|
      StrnCpyS (
 | 
						|
        CommandLine,
 | 
						|
        PrintBuffSize/sizeof (CHAR16),
 | 
						|
        CommandLine2,
 | 
						|
        PrintBuffSize/sizeof (CHAR16) - 1
 | 
						|
        );
 | 
						|
 | 
						|
      //
 | 
						|
      // Remove the %0 to %9 from the command line (if we have some arguments)
 | 
						|
      //
 | 
						|
      if (NewScriptFile->Argv != NULL) {
 | 
						|
        switch (NewScriptFile->Argc) {
 | 
						|
          default:
 | 
						|
            Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%9", NewScriptFile->Argv[9], FALSE, FALSE);
 | 
						|
            ASSERT_EFI_ERROR (Status);
 | 
						|
          case 9:
 | 
						|
            Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%8", NewScriptFile->Argv[8], FALSE, FALSE);
 | 
						|
            ASSERT_EFI_ERROR (Status);
 | 
						|
          case 8:
 | 
						|
            Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%7", NewScriptFile->Argv[7], FALSE, FALSE);
 | 
						|
            ASSERT_EFI_ERROR (Status);
 | 
						|
          case 7:
 | 
						|
            Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%6", NewScriptFile->Argv[6], FALSE, FALSE);
 | 
						|
            ASSERT_EFI_ERROR (Status);
 | 
						|
          case 6:
 | 
						|
            Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%5", NewScriptFile->Argv[5], FALSE, FALSE);
 | 
						|
            ASSERT_EFI_ERROR (Status);
 | 
						|
          case 5:
 | 
						|
            Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%4", NewScriptFile->Argv[4], FALSE, FALSE);
 | 
						|
            ASSERT_EFI_ERROR (Status);
 | 
						|
          case 4:
 | 
						|
            Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%3", NewScriptFile->Argv[3], FALSE, FALSE);
 | 
						|
            ASSERT_EFI_ERROR (Status);
 | 
						|
          case 3:
 | 
						|
            Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%2", NewScriptFile->Argv[2], FALSE, FALSE);
 | 
						|
            ASSERT_EFI_ERROR (Status);
 | 
						|
          case 2:
 | 
						|
            Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%1", NewScriptFile->Argv[1], FALSE, FALSE);
 | 
						|
            ASSERT_EFI_ERROR (Status);
 | 
						|
          case 1:
 | 
						|
            Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%0", NewScriptFile->Argv[0], FALSE, FALSE);
 | 
						|
            ASSERT_EFI_ERROR (Status);
 | 
						|
            break;
 | 
						|
          case 0:
 | 
						|
            break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%1", L"\"\"", FALSE, FALSE);
 | 
						|
      Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%2", L"\"\"", FALSE, FALSE);
 | 
						|
      Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%3", L"\"\"", FALSE, FALSE);
 | 
						|
      Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%4", L"\"\"", FALSE, FALSE);
 | 
						|
      Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%5", L"\"\"", FALSE, FALSE);
 | 
						|
      Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%6", L"\"\"", FALSE, FALSE);
 | 
						|
      Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%7", L"\"\"", FALSE, FALSE);
 | 
						|
      Status = ShellCopySearchAndReplace (CommandLine, CommandLine2, PrintBuffSize, L"%8", L"\"\"", FALSE, FALSE);
 | 
						|
      Status = ShellCopySearchAndReplace (CommandLine2, CommandLine, PrintBuffSize, L"%9", L"\"\"", FALSE, FALSE);
 | 
						|
 | 
						|
      StrnCpyS (
 | 
						|
        CommandLine2,
 | 
						|
        PrintBuffSize/sizeof (CHAR16),
 | 
						|
        CommandLine,
 | 
						|
        PrintBuffSize/sizeof (CHAR16) - 1
 | 
						|
        );
 | 
						|
 | 
						|
      LastCommand = NewScriptFile->CurrentCommand;
 | 
						|
 | 
						|
      for (CommandLine3 = CommandLine2; CommandLine3[0] == L' '; CommandLine3++) {
 | 
						|
      }
 | 
						|
 | 
						|
      if ((CommandLine3 != NULL) && (CommandLine3[0] == L':')) {
 | 
						|
        //
 | 
						|
        // This line is a goto target / label
 | 
						|
        //
 | 
						|
      } else {
 | 
						|
        if ((CommandLine3 != NULL) && (StrLen (CommandLine3) > 0)) {
 | 
						|
          if (CommandLine3[0] == L'@') {
 | 
						|
            //
 | 
						|
            // We need to save the current echo state
 | 
						|
            // and disable echo for just this command.
 | 
						|
            //
 | 
						|
            PreCommandEchoState = ShellCommandGetEchoState ();
 | 
						|
            ShellCommandSetEchoState (FALSE);
 | 
						|
            Status = RunCommand (CommandLine3+1);
 | 
						|
 | 
						|
            //
 | 
						|
            // If command was "@echo -off" or "@echo -on" then don't restore echo state
 | 
						|
            //
 | 
						|
            if ((StrCmp (L"@echo -off", CommandLine3) != 0) &&
 | 
						|
                (StrCmp (L"@echo -on", CommandLine3) != 0))
 | 
						|
            {
 | 
						|
              //
 | 
						|
              // Now restore the pre-'@' echo state.
 | 
						|
              //
 | 
						|
              ShellCommandSetEchoState (PreCommandEchoState);
 | 
						|
            }
 | 
						|
          } else {
 | 
						|
            if (ShellCommandGetEchoState ()) {
 | 
						|
              CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv (L"cwd");
 | 
						|
              if ((CurDir != NULL) && (StrLen (CurDir) > 1)) {
 | 
						|
                ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
 | 
						|
              } else {
 | 
						|
                ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
 | 
						|
              }
 | 
						|
 | 
						|
              ShellPrintEx (-1, -1, L"%s\r\n", CommandLine2);
 | 
						|
            }
 | 
						|
 | 
						|
            Status = RunCommand (CommandLine3);
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        if (ShellCommandGetScriptExit ()) {
 | 
						|
          //
 | 
						|
          // ShellCommandGetExitCode() always returns a UINT64
 | 
						|
          //
 | 
						|
          UnicodeSPrint (LeString, sizeof (LeString), L"0x%Lx", ShellCommandGetExitCode ());
 | 
						|
          DEBUG_CODE (
 | 
						|
            InternalEfiShellSetEnv (L"debuglasterror", LeString, TRUE);
 | 
						|
            );
 | 
						|
          InternalEfiShellSetEnv (L"lasterror", LeString, TRUE);
 | 
						|
 | 
						|
          ShellCommandRegisterExit (FALSE, 0);
 | 
						|
          Status = EFI_SUCCESS;
 | 
						|
          RestoreBufferList (&OldBufferList);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        if (ShellGetExecutionBreakFlag ()) {
 | 
						|
          RestoreBufferList (&OldBufferList);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        if (EFI_ERROR (Status)) {
 | 
						|
          RestoreBufferList (&OldBufferList);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        if (ShellCommandGetExit ()) {
 | 
						|
          RestoreBufferList (&OldBufferList);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      // If that commend did not update the CurrentCommand then we need to advance it...
 | 
						|
      //
 | 
						|
      if (LastCommand == NewScriptFile->CurrentCommand) {
 | 
						|
        NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
 | 
						|
        if (!IsNull (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
 | 
						|
          NewScriptFile->CurrentCommand->Reset = TRUE;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
 | 
						|
      if (!IsNull (&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
 | 
						|
        NewScriptFile->CurrentCommand->Reset = TRUE;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    RestoreBufferList (&OldBufferList);
 | 
						|
  }
 | 
						|
 | 
						|
  FreePool (CommandLine);
 | 
						|
  FreePool (CommandLine2);
 | 
						|
  ShellCommandSetNewScript (NULL);
 | 
						|
 | 
						|
  //
 | 
						|
  // Only if this was the last script reset the state.
 | 
						|
  //
 | 
						|
  if (ShellCommandGetCurrentScriptFile () == NULL) {
 | 
						|
    ShellCommandSetEchoState (PreScriptEchoState);
 | 
						|
  }
 | 
						|
 | 
						|
  return (EFI_SUCCESS);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Function to process a NSH script file.
 | 
						|
 | 
						|
  @param[in] ScriptPath         Pointer to the script file name (including file system path).
 | 
						|
  @param[in] Handle             the handle of the script file already opened.
 | 
						|
  @param[in] CmdLine            the command line to run.
 | 
						|
  @param[in] ParamProtocol      the shell parameters protocol pointer
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           the script completed successfully
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
RunScriptFile (
 | 
						|
  IN CONST CHAR16                   *ScriptPath,
 | 
						|
  IN SHELL_FILE_HANDLE              Handle OPTIONAL,
 | 
						|
  IN CONST CHAR16                   *CmdLine,
 | 
						|
  IN EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS         Status;
 | 
						|
  SHELL_FILE_HANDLE  FileHandle;
 | 
						|
  UINTN              Argc;
 | 
						|
  CHAR16             **Argv;
 | 
						|
 | 
						|
  if (ShellIsFile (ScriptPath) != EFI_SUCCESS) {
 | 
						|
    return (EFI_INVALID_PARAMETER);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // get the argc and argv updated for scripts
 | 
						|
  //
 | 
						|
  Status = UpdateArgcArgv (ParamProtocol, CmdLine, Script_File_Name, &Argv, &Argc);
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    if (Handle == NULL) {
 | 
						|
      //
 | 
						|
      // open the file
 | 
						|
      //
 | 
						|
      Status = ShellOpenFileByName (ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);
 | 
						|
      if (!EFI_ERROR (Status)) {
 | 
						|
        //
 | 
						|
        // run it
 | 
						|
        //
 | 
						|
        Status = RunScriptFileHandle (FileHandle, ScriptPath);
 | 
						|
 | 
						|
        //
 | 
						|
        // now close the file
 | 
						|
        //
 | 
						|
        ShellCloseFile (&FileHandle);
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      Status = RunScriptFileHandle (Handle, ScriptPath);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
 | 
						|
  // This is safe even if the update API failed.  In this case, it may be a no-op.
 | 
						|
  //
 | 
						|
  RestoreArgcArgv (ParamProtocol, &Argv, &Argc);
 | 
						|
 | 
						|
  return (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Return the pointer to the first occurrence of any character from a list of characters.
 | 
						|
 | 
						|
  @param[in] String           the string to parse
 | 
						|
  @param[in] CharacterList    the list of character to look for
 | 
						|
  @param[in] EscapeCharacter  An escape character to skip
 | 
						|
 | 
						|
  @return the location of the first character in the string
 | 
						|
  @retval CHAR_NULL no instance of any character in CharacterList was found in String
 | 
						|
**/
 | 
						|
CONST CHAR16 *
 | 
						|
FindFirstCharacter (
 | 
						|
  IN CONST CHAR16  *String,
 | 
						|
  IN CONST CHAR16  *CharacterList,
 | 
						|
  IN CONST CHAR16  EscapeCharacter
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT32  WalkChar;
 | 
						|
  UINT32  WalkStr;
 | 
						|
 | 
						|
  for (WalkStr = 0; WalkStr < StrLen (String); WalkStr++) {
 | 
						|
    if (String[WalkStr] == EscapeCharacter) {
 | 
						|
      WalkStr++;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    for (WalkChar = 0; WalkChar < StrLen (CharacterList); WalkChar++) {
 | 
						|
      if (String[WalkStr] == CharacterList[WalkChar]) {
 | 
						|
        return (&String[WalkStr]);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return (String + StrLen (String));
 | 
						|
}
 |