/** @file
  EFI_FILE_PROTOCOL wrappers for other items (Like Environment Variables,
  StdIn, StdOut, StdErr, etc...).

  Copyright 2016 Dell Inc.
  Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
  (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "Shell.h"
#include "FileHandleInternal.h"

#define MEM_WRITE_REALLOC_OVERHEAD  1024

/**
  File style interface for console (Open).

  @param[in] This       Ignored.
  @param[out] NewHandle Ignored.
  @param[in] FileName   Ignored.
  @param[in] OpenMode   Ignored.
  @param[in] Attributes Ignored.

  @retval EFI_NOT_FOUND
**/
EFI_STATUS
EFIAPI
FileInterfaceOpenNotFound (
  IN EFI_FILE_PROTOCOL   *This,
  OUT EFI_FILE_PROTOCOL  **NewHandle,
  IN CHAR16              *FileName,
  IN UINT64              OpenMode,
  IN UINT64              Attributes
  )
{
  return (EFI_NOT_FOUND);
}

/**
  File style interface for console (Close, Delete, & Flush)

  @param[in] This       Ignored.

  @retval EFI_SUCCESS
**/
EFI_STATUS
EFIAPI
FileInterfaceNopGeneric (
  IN EFI_FILE_PROTOCOL  *This
  )
{
  return (EFI_SUCCESS);
}

/**
  File style interface for console (GetPosition).

  @param[in] This       Ignored.
  @param[out] Position  Ignored.

  @retval EFI_UNSUPPORTED
**/
EFI_STATUS
EFIAPI
FileInterfaceNopGetPosition (
  IN EFI_FILE_PROTOCOL  *This,
  OUT UINT64            *Position
  )
{
  return (EFI_UNSUPPORTED);
}

/**
  File style interface for console (SetPosition).

  @param[in] This       Ignored.
  @param[in] Position   Ignored.

  @retval EFI_UNSUPPORTED
**/
EFI_STATUS
EFIAPI
FileInterfaceNopSetPosition (
  IN EFI_FILE_PROTOCOL  *This,
  IN UINT64             Position
  )
{
  return (EFI_UNSUPPORTED);
}

/**
  File style interface for console (GetInfo).

  @param[in] This              Ignored.
  @param[in] InformationType   Ignored.
  @param[in, out] BufferSize   Ignored.
  @param[out] Buffer           Ignored.

  @retval EFI_UNSUPPORTED
**/
EFI_STATUS
EFIAPI
FileInterfaceNopGetInfo (
  IN EFI_FILE_PROTOCOL  *This,
  IN EFI_GUID           *InformationType,
  IN OUT UINTN          *BufferSize,
  OUT VOID              *Buffer
  )
{
  return (EFI_UNSUPPORTED);
}

/**
  File style interface for console (SetInfo).

  @param[in] This       Ignored.
  @param[in] InformationType   Ignored.
  @param[in] BufferSize Ignored.
  @param[in] Buffer     Ignored.

  @retval EFI_UNSUPPORTED
**/
EFI_STATUS
EFIAPI
FileInterfaceNopSetInfo (
  IN EFI_FILE_PROTOCOL  *This,
  IN EFI_GUID           *InformationType,
  IN UINTN              BufferSize,
  IN VOID               *Buffer
  )
{
  return (EFI_UNSUPPORTED);
}

/**
  File style interface for StdOut (Write).

  Writes data to the screen.

  @param[in] This              The pointer to the EFI_FILE_PROTOCOL object.
  @param[in, out] BufferSize   Size in bytes of Buffer.
  @param[in] Buffer            The pointer to the buffer to write.

  @retval EFI_UNSUPPORTED No output console is supported.
  @return A return value from gST->ConOut->OutputString.
**/
EFI_STATUS
EFIAPI
FileInterfaceStdOutWrite (
  IN EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN          *BufferSize,
  IN VOID               *Buffer
  )
{
  if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
    return (EFI_UNSUPPORTED);
  }

  if (*((CHAR16 *)Buffer) == gUnicodeFileTag) {
    return (gST->ConOut->OutputString (gST->ConOut, (CHAR16 *)Buffer + 1));
  }

  return (gST->ConOut->OutputString (gST->ConOut, Buffer));
}

/**
  File style interface for StdIn (Write).

  @param[in] This            Ignored.
  @param[in, out] BufferSize Ignored.
  @param[in] Buffer          Ignored.

  @retval EFI_UNSUPPORTED
**/
EFI_STATUS
EFIAPI
FileInterfaceStdInWrite (
  IN      EFI_FILE_PROTOCOL  *This,
  IN OUT  UINTN              *BufferSize,
  IN      VOID               *Buffer
  )
{
  return (EFI_UNSUPPORTED);
}

/**
  File style interface for console StdErr (Write).

  Writes error to the error output.

  @param[in] This              The pointer to the EFI_FILE_PROTOCOL object.
  @param[in, out] BufferSize   Size in bytes of Buffer.
  @param[in] Buffer            The pointer to the buffer to write.

  @return A return value from gST->StdErr->OutputString.
**/
EFI_STATUS
EFIAPI
FileInterfaceStdErrWrite (
  IN EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN          *BufferSize,
  IN VOID               *Buffer
  )
{
  return (gST->StdErr->OutputString (gST->StdErr, Buffer));
}

/**
  File style interface for console StdOut (Read).

  @param[in] This              Ignored.
  @param[in, out] BufferSize   Ignored.
  @param[out] Buffer           Ignored.

  @retval EFI_UNSUPPORTED
**/
EFI_STATUS
EFIAPI
FileInterfaceStdOutRead (
  IN EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN          *BufferSize,
  OUT VOID              *Buffer
  )
{
  return (EFI_UNSUPPORTED);
}

/**
  File style interface for console StdErr (Read).

  @param[in] This              Ignored.
  @param[in, out] BufferSize   Ignored.
  @param[out] Buffer           Ignored.

  @retval EFI_UNSUPPORTED Always.
**/
EFI_STATUS
EFIAPI
FileInterfaceStdErrRead (
  IN EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN          *BufferSize,
  OUT VOID              *Buffer
  )
{
  return (EFI_UNSUPPORTED);
}

/**
  File style interface for NUL file (Read).

  @param[in] This              Ignored.
  @param[in, out] BufferSize   Poiner to 0 upon return.
  @param[out] Buffer           Ignored.

  @retval EFI_SUCCESS Always.
**/
EFI_STATUS
EFIAPI
FileInterfaceNulRead (
  IN      EFI_FILE_PROTOCOL  *This,
  IN OUT  UINTN              *BufferSize,
  OUT     VOID               *Buffer
  )
{
  *BufferSize = 0;
  return (EFI_SUCCESS);
}

/**
  File style interface for NUL file (Write).

  @param[in] This              Ignored.
  @param[in, out] BufferSize   Ignored.
  @param[in] Buffer            Ignored.

  @retval EFI_SUCCESS
**/
EFI_STATUS
EFIAPI
FileInterfaceNulWrite (
  IN EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN          *BufferSize,
  IN VOID               *Buffer
  )
{
  return (EFI_SUCCESS);
}

/**
  Create the TAB completion list.

  @param[in]  InputString       The command line to expand.
  @param[in]  StringLen         Length of the command line.
  @param[in]  BufferSize        Buffer size.
  @param[in, out] TabCompletionList Return the TAB completion list.
  @param[in, out] TabUpdatePos      Return the TAB update position.
**/
EFI_STATUS
CreateTabCompletionList (
  IN CONST CHAR16             *InputString,
  IN CONST UINTN              StringLen,
  IN CONST UINTN              BufferSize,
  IN OUT EFI_SHELL_FILE_INFO  **TabCompletionList,
  IN OUT   UINTN              *TabUpdatePos
  )
{
  BOOLEAN              InQuotation;
  UINTN                TabPos;
  UINTN                Index;
  CONST CHAR16         *Cwd;
  EFI_STATUS           Status;
  CHAR16               *TabStr;
  EFI_SHELL_FILE_INFO  *FileList;
  EFI_SHELL_FILE_INFO  *FileInfo;
  EFI_SHELL_FILE_INFO  *TempFileInfo;

  //
  // Allocate buffers
  //
  TabStr = AllocateZeroPool (BufferSize);
  if (TabStr == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // handle auto complete of file and directory names...
  // E.g.: cd fs0:\EFI\Bo<TAB>
  //          ^        ^
  //          TabPos   TabUpdatePos
  //
  TabPos        = 0;
  *TabUpdatePos = 0;
  FileList      = NULL;
  InQuotation   = FALSE;
  for (Index = 0; Index < StringLen; Index++) {
    switch (InputString[Index]) {
      case L'\"':
        InQuotation = (BOOLEAN)(!InQuotation);
        break;

      case L' ':
        if (!InQuotation) {
          TabPos        = Index + 1;
          *TabUpdatePos = TabPos;
        }

        break;

      case L':':
      //
      // handle the case "fs0:<TAB>"
      // Update the TabUpdatePos as well.
      //
      case L'\\':
        *TabUpdatePos = Index + 1;
        break;

      default:
        break;
    }
  }

  if (StrStr (InputString + TabPos, L":") == NULL) {
    //
    // If file path doesn't contain ":", ...
    //
    Cwd = ShellInfoObject.NewEfiShellProtocol->GetCurDir (NULL);
    if (Cwd != NULL) {
      if (InputString[TabPos] != L'\\') {
        //
        // and it doesn't begin with "\\", it's a path relative to current directory.
        // TabStr = "<cwd>\\"
        //
        StrnCpyS (TabStr, BufferSize / sizeof (CHAR16), Cwd, (BufferSize) / sizeof (CHAR16) - 1);
        StrCatS (TabStr, (BufferSize) / sizeof (CHAR16), L"\\");
      } else {
        //
        // and it begins with "\\", it's a path pointing to root directory of current map.
        // TabStr = "fsx:"
        //
        Index = StrStr (Cwd, L":") - Cwd + 1;
        StrnCpyS (TabStr, BufferSize / sizeof (CHAR16), Cwd, Index);
      }
    }
  }

  StrnCatS (TabStr, (BufferSize) / sizeof (CHAR16), InputString + TabPos, StringLen - TabPos);
  StrnCatS (TabStr, (BufferSize) / sizeof (CHAR16), L"*", (BufferSize) / sizeof (CHAR16) - 1 - StrLen (TabStr));
  Status = ShellInfoObject.NewEfiShellProtocol->FindFiles (TabStr, &FileList);

  //
  // Filter out the non-directory for "CD" command
  // Filter "." and ".." for all
  //
  if (!EFI_ERROR (Status) && (FileList != NULL)) {
    //
    // Skip the spaces in the beginning
    //
    while (*InputString == L' ') {
      InputString++;
    }

    for (FileInfo = (EFI_SHELL_FILE_INFO *)GetFirstNode (&FileList->Link); !IsNull (&FileList->Link, &FileInfo->Link); ) {
      if (IsDotOrDotDot (FileInfo->FileName) ||
          ((((InputString[0] == L'c') || (InputString[0] == L'C')) && ((InputString[1] == L'd') || (InputString[1] == L'D'))) &&
           (ShellIsDirectory (FileInfo->FullName) != EFI_SUCCESS)))
      {
        TempFileInfo = FileInfo;
        FileInfo     = (EFI_SHELL_FILE_INFO *)RemoveEntryList (&FileInfo->Link);
        InternalFreeShellFileInfoNode (TempFileInfo);
      } else {
        FileInfo = (EFI_SHELL_FILE_INFO *)GetNextNode (&FileList->Link, &FileInfo->Link);
      }
    }
  }

  if ((FileList != NULL) && !IsListEmpty (&FileList->Link)) {
    Status = EFI_SUCCESS;
  } else {
    ShellInfoObject.NewEfiShellProtocol->FreeFileList (&FileList);
    Status = EFI_NOT_FOUND;
  }

  FreePool (TabStr);

  *TabCompletionList = FileList;
  return Status;
}

/**
  File style interface for console (Read).

  This will return a single line of input from the console.

  @param This           A pointer to the EFI_FILE_PROTOCOL instance that is the
                        file handle to read data from. Not used.
  @param BufferSize     On input, the size of the Buffer. On output, the amount
                        of data returned in Buffer. In both cases, the size is
                        measured in bytes.
  @param Buffer         The buffer into which the data is read.


  @retval EFI_SUCCESS           The data was read.
  @retval EFI_NO_MEDIA          The device has no medium.
  @retval EFI_DEVICE_ERROR      The device reported an error.
  @retval EFI_DEVICE_ERROR      An attempt was made to read from a deleted file.
  @retval EFI_DEVICE_ERROR      On entry, the current file position is beyond the end of the file.
  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
  @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small to read the current directory
                                entry. BufferSize has been updated with the size
                                needed to complete the request.
  @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
**/
EFI_STATUS
EFIAPI
FileInterfaceStdInRead (
  IN EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN          *BufferSize,
  OUT VOID              *Buffer
  )
{
  CHAR16         *CurrentString;
  BOOLEAN        Done;
  UINTN          TabUpdatePos;        // Start index of the string updated by TAB stroke
  UINTN          Column;              // Column of current cursor
  UINTN          Row;                 // Row of current cursor
  UINTN          StartColumn;         // Column at the beginning of the line
  UINTN          Update;              // Line index for update
  UINTN          Delete;              // Num of chars to delete from console after update
  UINTN          StringLen;           // Total length of the line
  UINTN          StringCurPos;        // Line index corresponding to the cursor
  UINTN          MaxStr;              // Maximum possible line length
  UINTN          TotalColumn;         // Num of columns in the console
  UINTN          TotalRow;            // Num of rows in the console
  UINTN          SkipLength;
  UINTN          OutputLength;        // Length of the update string
  UINTN          TailRow;             // Row of end of line
  UINTN          TailColumn;          // Column of end of line
  EFI_INPUT_KEY  Key;

  BUFFER_LIST          *LinePos;
  BUFFER_LIST          *NewPos;
  BOOLEAN              InScrolling;
  EFI_STATUS           Status;
  BOOLEAN              InTabScrolling; // Whether in TAB-completion state
  EFI_SHELL_FILE_INFO  *TabCompleteList;
  EFI_SHELL_FILE_INFO  *TabCurrent;
  UINTN                EventIndex;
  CHAR16               *TabOutputStr;

  //
  // If buffer is not large enough to hold a CHAR16, return minimum buffer size
  //
  if (*BufferSize < sizeof (CHAR16) * 2) {
    *BufferSize = sizeof (CHAR16) * 2;
    return (EFI_BUFFER_TOO_SMALL);
  }

  Done            = FALSE;
  CurrentString   = Buffer;
  StringLen       = 0;
  StringCurPos    = 0;
  OutputLength    = 0;
  Update          = 0;
  Delete          = 0;
  LinePos         = NewPos = (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory);
  InScrolling     = FALSE;
  InTabScrolling  = FALSE;
  Status          = EFI_SUCCESS;
  TabOutputStr    = NULL;
  TabUpdatePos    = 0;
  TabCompleteList = NULL;
  TabCurrent      = NULL;

  //
  // Get the screen setting and the current cursor location
  //
  Column = StartColumn = gST->ConOut->Mode->CursorColumn;
  Row    = gST->ConOut->Mode->CursorRow;
  gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &TotalColumn, &TotalRow);

  //
  // Limit the line length to the buffer size or the minimum size of the
  // screen. (The smaller takes effect)
  //
  MaxStr = TotalColumn * (TotalRow - 1) - StartColumn;
  if (MaxStr > *BufferSize / sizeof (CHAR16)) {
    MaxStr = *BufferSize / sizeof (CHAR16);
  }

  ZeroMem (CurrentString, MaxStr * sizeof (CHAR16));
  do {
    //
    // Read a key
    //
    gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
    Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
    if (EFI_ERROR (Status)) {
      if (Status == EFI_NOT_READY) {
        continue;
      }

      ZeroMem (CurrentString, MaxStr * sizeof (CHAR16));
      StringLen = 0;
      break;
    }

    //
    // Press PageUp or PageDown to scroll the history screen up or down.
    // Press any other key to quit scrolling.
    //
    if ((Key.UnicodeChar == 0) && ((Key.ScanCode == SCAN_PAGE_UP) || (Key.ScanCode == SCAN_PAGE_DOWN))) {
      if (Key.ScanCode == SCAN_PAGE_UP) {
        ConsoleLoggerDisplayHistory (FALSE, 0, ShellInfoObject.ConsoleInfo);
      } else if (Key.ScanCode == SCAN_PAGE_DOWN) {
        ConsoleLoggerDisplayHistory (TRUE, 0, ShellInfoObject.ConsoleInfo);
      }

      InScrolling = TRUE;
    } else {
      if (InScrolling) {
        ConsoleLoggerStopHistory (ShellInfoObject.ConsoleInfo);
        InScrolling = FALSE;
      }
    }

    //
    // If we are quitting TAB scrolling...
    //
    if (InTabScrolling && (Key.UnicodeChar != CHAR_TAB)) {
      if (TabCompleteList != NULL) {
        ShellInfoObject.NewEfiShellProtocol->FreeFileList (&TabCompleteList);
        DEBUG_CODE (
          TabCompleteList = NULL;
          );
      }

      InTabScrolling = FALSE;
    }

    switch (Key.UnicodeChar) {
      case CHAR_CARRIAGE_RETURN:
        //
        // All done, print a newline at the end of the string
        //
        TailRow    = Row + (StringLen - StringCurPos + Column) / TotalColumn;
        TailColumn = (StringLen - StringCurPos + Column) % TotalColumn;
        ShellPrintEx ((INT32)TailColumn, (INT32)TailRow, L"%N\n");
        Done = TRUE;
        break;

      case CHAR_BACKSPACE:
        if (StringCurPos != 0) {
          //
          // If not move back beyond string beginning, move all characters behind
          // the current position one character forward
          //
          StringCurPos--;
          Update = StringCurPos;
          Delete = 1;
          CopyMem (CurrentString + StringCurPos, CurrentString + StringCurPos + 1, sizeof (CHAR16) * (StringLen - StringCurPos));

          //
          // Adjust the current column and row
          //
          MoveCursorBackward (TotalColumn, &Column, &Row);
        }

        break;

      case CHAR_TAB:
        if (!InTabScrolling) {
          TabCurrent = NULL;
          //
          // Initialize a tab complete operation.
          //
          Status = CreateTabCompletionList (CurrentString, StringLen, *BufferSize, &TabCompleteList, &TabUpdatePos);
          if (!EFI_ERROR (Status)) {
            InTabScrolling = TRUE;
          }

          //
          // We do not set up the replacement.
          // The next section will do that.
          //
        }

        if (InTabScrolling) {
          //
          // We are in a tab complete operation.
          // set up the next replacement.
          //
          ASSERT (TabCompleteList != NULL);
          if (TabCurrent == NULL) {
            TabCurrent = (EFI_SHELL_FILE_INFO *)GetFirstNode (&TabCompleteList->Link);
          } else {
            TabCurrent = (EFI_SHELL_FILE_INFO *)GetNextNode (&TabCompleteList->Link, &TabCurrent->Link);
          }

          //
          // Skip over the empty list beginning node
          //
          if (IsNull (&TabCompleteList->Link, &TabCurrent->Link)) {
            TabCurrent = (EFI_SHELL_FILE_INFO *)GetNextNode (&TabCompleteList->Link, &TabCurrent->Link);
          }
        }

        break;

      default:
        if (Key.UnicodeChar >= ' ') {
          //
          // If we are at the buffer's end, drop the key
          //
          if ((StringLen == MaxStr - 1) && (ShellInfoObject.ViewingSettings.InsertMode || (StringCurPos == StringLen))) {
            break;
          }

          //
          // If in insert mode, make space by moving each other character 1
          // space higher in the array
          //
          if (ShellInfoObject.ViewingSettings.InsertMode) {
            CopyMem (CurrentString + StringCurPos + 1, CurrentString + StringCurPos, (StringLen - StringCurPos)*sizeof (CurrentString[0]));
          }

          CurrentString[StringCurPos] = Key.UnicodeChar;
          Update                      = StringCurPos;

          StringCurPos += 1;
          OutputLength  = 1;
        }

        break;

      case 0:
        switch (Key.ScanCode) {
          case SCAN_DELETE:
            //
            // Move characters behind current position one character forward
            //
            if (StringLen != 0) {
              Update = StringCurPos;
              Delete = 1;
              CopyMem (CurrentString + StringCurPos, CurrentString + StringCurPos + 1, sizeof (CHAR16) * (StringLen - StringCurPos));
            }

            break;

          case SCAN_UP:
            //
            // Prepare to print the previous command
            //
            NewPos = (BUFFER_LIST *)GetPreviousNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link);
            if (IsNull (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link)) {
              NewPos = (BUFFER_LIST *)GetPreviousNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link);
            }

            break;

          case SCAN_DOWN:
            //
            // Prepare to print the next command
            //
            NewPos = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link);
            if (NewPos == (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory)) {
              NewPos = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link);
            }

            break;

          case SCAN_LEFT:
            //
            // Adjust current cursor position
            //
            if (StringCurPos != 0) {
              --StringCurPos;
              MoveCursorBackward (TotalColumn, &Column, &Row);
            }

            break;

          case SCAN_RIGHT:
            //
            // Adjust current cursor position
            //
            if (StringCurPos < StringLen) {
              ++StringCurPos;
              MoveCursorForward (TotalColumn, TotalRow, &Column, &Row);
            }

            break;

          case SCAN_HOME:
            //
            // Move current cursor position to the beginning of the command line
            //
            Row         -= (StringCurPos + StartColumn) / TotalColumn;
            Column       = StartColumn;
            StringCurPos = 0;
            break;

          case SCAN_END:
            //
            // Move current cursor position to the end of the command line
            //
            TailRow      = Row + (StringLen - StringCurPos + Column) / TotalColumn;
            TailColumn   = (StringLen - StringCurPos + Column) % TotalColumn;
            Row          = TailRow;
            Column       = TailColumn;
            StringCurPos = StringLen;
            break;

          case SCAN_ESC:
            //
            // Prepare to clear the current command line
            //
            CurrentString[0] = 0;
            Update           = 0;
            Delete           = StringLen;
            Row             -= (StringCurPos + StartColumn) / TotalColumn;
            Column           = StartColumn;
            OutputLength     = 0;
            break;

          case SCAN_INSERT:
            //
            // Toggle the SEnvInsertMode flag
            //
            ShellInfoObject.ViewingSettings.InsertMode = (BOOLEAN) !ShellInfoObject.ViewingSettings.InsertMode;
            break;

          case SCAN_F7:
            //
            // Print command history
            //
            PrintCommandHistory (TotalColumn, TotalRow, 4);
            *CurrentString = CHAR_NULL;
            Done           = TRUE;
            break;
        }
    }

    if (Done) {
      break;
    }

    //
    // If we are in auto-complete mode, we are preparing to print
    // the next file or directory name
    //
    if (InTabScrolling) {
      TabOutputStr = AllocateZeroPool (*BufferSize);
      if (TabOutputStr == NULL) {
        Status = EFI_OUT_OF_RESOURCES;
      }
    }

    if (InTabScrolling && (TabOutputStr != NULL)) {
      //
      // Adjust the column and row to the start of TAB-completion string.
      //
      Column       = (StartColumn + TabUpdatePos) % TotalColumn;
      Row         -= (StartColumn + StringCurPos) / TotalColumn - (StartColumn + TabUpdatePos) / TotalColumn;
      OutputLength = StrLen (TabCurrent->FileName);
      //
      // if the output string contains  blank space, quotation marks L'\"'
      // should be added to the output.
      //
      if (StrStr (TabCurrent->FileName, L" ") != NULL) {
        TabOutputStr[0] = L'\"';
        CopyMem (TabOutputStr + 1, TabCurrent->FileName, OutputLength * sizeof (CHAR16));
        TabOutputStr[OutputLength + 1] = L'\"';
        TabOutputStr[OutputLength + 2] = CHAR_NULL;
      } else {
        CopyMem (TabOutputStr, TabCurrent->FileName, OutputLength * sizeof (CHAR16));
        TabOutputStr[OutputLength] = CHAR_NULL;
      }

      OutputLength = StrLen (TabOutputStr) < MaxStr - 1 ? StrLen (TabOutputStr) : MaxStr - 1;
      CopyMem (CurrentString + TabUpdatePos, TabOutputStr, OutputLength * sizeof (CHAR16));
      CurrentString[TabUpdatePos + OutputLength] = CHAR_NULL;
      StringCurPos                               = TabUpdatePos + OutputLength;
      Update                                     = TabUpdatePos;
      if (StringLen > TabUpdatePos + OutputLength) {
        Delete = StringLen - TabUpdatePos - OutputLength;
      }

      FreePool (TabOutputStr);
    }

    //
    // If we have a new position, we are preparing to print a previous or
    // next command.
    //
    if (NewPos != (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory)) {
      Column = StartColumn;
      Row   -= (StringCurPos + StartColumn) / TotalColumn;

      LinePos = NewPos;
      NewPos  = (BUFFER_LIST *)(&ShellInfoObject.ViewingSettings.CommandHistory);

      OutputLength = StrLen (LinePos->Buffer) < MaxStr - 1 ? StrLen (LinePos->Buffer) : MaxStr - 1;
      CopyMem (CurrentString, LinePos->Buffer, OutputLength * sizeof (CHAR16));
      CurrentString[OutputLength] = CHAR_NULL;

      StringCurPos = OutputLength;

      //
      // Draw new input string
      //
      Update = 0;
      if (StringLen > OutputLength) {
        //
        // If old string was longer, blank its tail
        //
        Delete = StringLen - OutputLength;
      }
    }

    //
    // If we need to update the output do so now
    //
    if (Update != (UINTN)-1) {
      ShellPrintEx ((INT32)Column, (INT32)Row, L"%s%.*s", CurrentString + Update, Delete, L"");
      StringLen = StrLen (CurrentString);

      if (Delete != 0) {
        SetMem (CurrentString + StringLen, Delete * sizeof (CHAR16), CHAR_NULL);
      }

      if (StringCurPos > StringLen) {
        StringCurPos = StringLen;
      }

      Update = (UINTN)-1;

      //
      // After using print to reflect newly updates, if we're not using
      // BACKSPACE and DELETE, we need to move the cursor position forward,
      // so adjust row and column here.
      //
      if ((Key.UnicodeChar != CHAR_BACKSPACE) && !((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_DELETE))) {
        //
        // Calculate row and column of the tail of current string
        //
        TailRow    = Row + (StringLen - StringCurPos + Column + OutputLength) / TotalColumn;
        TailColumn = (StringLen - StringCurPos + Column + OutputLength) % TotalColumn;

        //
        // If the tail of string reaches screen end, screen rolls up, so if
        // Row does not equal TailRow, Row should be decremented
        //
        // (if we are recalling commands using UPPER and DOWN key, and if the
        // old command is too long to fit the screen, TailColumn must be 79.
        //
        if ((TailColumn == 0) && (TailRow >= TotalRow) && (Row != TailRow)) {
          Row--;
        }

        //
        // Calculate the cursor position after current operation. If cursor
        // reaches line end, update both row and column, otherwise, only
        // column will be changed.
        //
        if (Column + OutputLength >= TotalColumn) {
          SkipLength = OutputLength - (TotalColumn - Column);

          Row += SkipLength / TotalColumn + 1;
          if (Row > TotalRow - 1) {
            Row = TotalRow - 1;
          }

          Column = SkipLength % TotalColumn;
        } else {
          Column += OutputLength;
        }
      }

      Delete = 0;
    }

    //
    // Set the cursor position for this key
    //
    gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row);
  } while (!Done);

  if ((CurrentString != NULL) && (StrLen (CurrentString) > 0)) {
    //
    // add the line to the history buffer
    //
    AddLineToCommandHistory (CurrentString);
  }

  //
  // Return the data to the caller
  //
  *BufferSize = StringLen * sizeof (CHAR16);

  //
  // if this was used it should be deallocated by now...
  // prevent memory leaks...
  //
  if (TabCompleteList != NULL) {
    ShellInfoObject.NewEfiShellProtocol->FreeFileList (&TabCompleteList);
  }

  ASSERT (TabCompleteList == NULL);

  return Status;
}

//
// FILE style interfaces for StdIn/StdOut/StdErr
//
EFI_FILE_PROTOCOL  FileInterfaceStdIn = {
  EFI_FILE_REVISION,
  FileInterfaceOpenNotFound,
  FileInterfaceNopGeneric,
  FileInterfaceNopGeneric,
  FileInterfaceStdInRead,
  FileInterfaceStdInWrite,
  FileInterfaceNopGetPosition,
  FileInterfaceNopSetPosition,
  FileInterfaceNopGetInfo,
  FileInterfaceNopSetInfo,
  FileInterfaceNopGeneric
};

EFI_FILE_PROTOCOL  FileInterfaceStdOut = {
  EFI_FILE_REVISION,
  FileInterfaceOpenNotFound,
  FileInterfaceNopGeneric,
  FileInterfaceNopGeneric,
  FileInterfaceStdOutRead,
  FileInterfaceStdOutWrite,
  FileInterfaceNopGetPosition,
  FileInterfaceNopSetPosition,
  FileInterfaceNopGetInfo,
  FileInterfaceNopSetInfo,
  FileInterfaceNopGeneric
};

EFI_FILE_PROTOCOL  FileInterfaceStdErr = {
  EFI_FILE_REVISION,
  FileInterfaceOpenNotFound,
  FileInterfaceNopGeneric,
  FileInterfaceNopGeneric,
  FileInterfaceStdErrRead,
  FileInterfaceStdErrWrite,
  FileInterfaceNopGetPosition,
  FileInterfaceNopSetPosition,
  FileInterfaceNopGetInfo,
  FileInterfaceNopSetInfo,
  FileInterfaceNopGeneric
};

EFI_FILE_PROTOCOL  FileInterfaceNulFile = {
  EFI_FILE_REVISION,
  FileInterfaceOpenNotFound,
  FileInterfaceNopGeneric,
  FileInterfaceNopGeneric,
  FileInterfaceNulRead,
  FileInterfaceNulWrite,
  FileInterfaceNopGetPosition,
  FileInterfaceNopSetPosition,
  FileInterfaceNopGetInfo,
  FileInterfaceNopSetInfo,
  FileInterfaceNopGeneric
};

//
// This is identical to EFI_FILE_PROTOCOL except for the additional member
// for the name.
//

typedef struct {
  UINT64                   Revision;
  EFI_FILE_OPEN            Open;
  EFI_FILE_CLOSE           Close;
  EFI_FILE_DELETE          Delete;
  EFI_FILE_READ            Read;
  EFI_FILE_WRITE           Write;
  EFI_FILE_GET_POSITION    GetPosition;
  EFI_FILE_SET_POSITION    SetPosition;
  EFI_FILE_GET_INFO        GetInfo;
  EFI_FILE_SET_INFO        SetInfo;
  EFI_FILE_FLUSH           Flush;
  CHAR16                   Name[1];
} EFI_FILE_PROTOCOL_ENVIRONMENT;
// ANSI compliance helper to get size of the struct.
#define SIZE_OF_EFI_FILE_PROTOCOL_ENVIRONMENT  EFI_FIELD_OFFSET (EFI_FILE_PROTOCOL_ENVIRONMENT, Name)

/**
  File style interface for Environment Variable (Close).

  Frees the memory for this object.

  @param[in] This       The pointer to the EFI_FILE_PROTOCOL object.

  @retval EFI_SUCCESS
**/
EFI_STATUS
EFIAPI
FileInterfaceEnvClose (
  IN EFI_FILE_PROTOCOL  *This
  )
{
  VOID        *NewBuffer;
  UINTN       NewSize;
  EFI_STATUS  Status;
  BOOLEAN     Volatile;
  UINTN       TotalSize;

  //
  // Most if not all UEFI commands will have an '\r\n' at the end of any output.
  // Since the output was redirected to a variable, it does not make sense to
  // keep this.  So, before closing, strip the trailing '\r\n' from the variable
  // if it exists.
  //
  NewBuffer = NULL;
  NewSize   = 0;
  TotalSize = 0;

  Status = IsVolatileEnv (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &Volatile);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer);
  if (Status == EFI_BUFFER_TOO_SMALL) {
    TotalSize = NewSize + sizeof (CHAR16);
    NewBuffer = AllocateZeroPool (TotalSize);
    if (NewBuffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer);
  }

  if (!EFI_ERROR (Status) && (NewBuffer != NULL)) {
    if (TotalSize / sizeof (CHAR16) >= 3) {
      if ((((CHAR16 *)NewBuffer)[TotalSize / sizeof (CHAR16) - 2] == CHAR_LINEFEED) &&
          (((CHAR16 *)NewBuffer)[TotalSize / sizeof (CHAR16) - 3] == CHAR_CARRIAGE_RETURN)
          )
      {
        ((CHAR16 *)NewBuffer)[TotalSize / sizeof (CHAR16) - 3] = CHAR_NULL;
        //
        // If the NewBuffer end with \r\n\0, We will replace '\r' by '\0' and then update TotalSize.
        //
        TotalSize -= sizeof (CHAR16) * 2;
      }

      if (Volatile) {
        Status = SHELL_SET_ENVIRONMENT_VARIABLE_V (
                   ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
                   TotalSize - sizeof (CHAR16),
                   NewBuffer
                   );

        if (!EFI_ERROR (Status)) {
          Status = ShellAddEnvVarToList (
                     ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
                     NewBuffer,
                     TotalSize,
                     EFI_VARIABLE_BOOTSERVICE_ACCESS
                     );
        }
      } else {
        Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV (
                   ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
                   TotalSize - sizeof (CHAR16),
                   NewBuffer
                   );

        if (!EFI_ERROR (Status)) {
          Status = ShellAddEnvVarToList (
                     ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
                     NewBuffer,
                     TotalSize,
                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS
                     );
        }
      }
    }
  }

  SHELL_FREE_NON_NULL (NewBuffer);
  FreePool ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This);
  return (Status);
}

/**
  File style interface for Environment Variable (Delete).

  @param[in] This       The pointer to the EFI_FILE_PROTOCOL object.

  @retval The return value from FileInterfaceEnvClose().
**/
EFI_STATUS
EFIAPI
FileInterfaceEnvDelete (
  IN EFI_FILE_PROTOCOL  *This
  )
{
  SHELL_DELETE_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name);
  return (FileInterfaceEnvClose (This));
}

/**
  File style interface for Environment Variable (Read).

  @param[in] This              The pointer to the EFI_FILE_PROTOCOL object.
  @param[in, out] BufferSize   Size in bytes of Buffer.
  @param[out] Buffer           The pointer to the buffer to fill.

  @retval EFI_SUCCESS   The data was read.
**/
EFI_STATUS
EFIAPI
FileInterfaceEnvRead (
  IN     EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN              *BufferSize,
  OUT    VOID               *Buffer
  )
{
  EFI_STATUS  Status;

  *BufferSize = *BufferSize / sizeof (CHAR16) * sizeof (CHAR16);
  if (*BufferSize != 0) {
    //
    // Make sure the first unicode character is \xFEFF
    //
    *(CHAR16 *)Buffer = gUnicodeFileTag;
    Buffer            = (CHAR16 *)Buffer + 1;
    *BufferSize      -= sizeof (gUnicodeFileTag);
  }

  Status = SHELL_GET_ENVIRONMENT_VARIABLE (
             ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
             BufferSize,
             Buffer
             );
  if (!EFI_ERROR (Status) || (Status == EFI_BUFFER_TOO_SMALL)) {
    //
    // BufferSize is valid and needs update when Status is Success or BufferTooSmall.
    //
    *BufferSize += sizeof (gUnicodeFileTag);
  }

  return Status;
}

/**
  File style interface for Volatile Environment Variable (Write).
  This function also caches the environment variable into gShellEnvVarList.

  @param[in] This              The pointer to the EFI_FILE_PROTOCOL object.
  @param[in, out] BufferSize   Size in bytes of Buffer.
  @param[in] Buffer            The pointer to the buffer to write.

  @retval EFI_SUCCESS             The data was successfully write to variable.
  @retval SHELL_OUT_OF_RESOURCES  A memory allocation failed.
**/
EFI_STATUS
EFIAPI
FileInterfaceEnvVolWrite (
  IN EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN          *BufferSize,
  IN VOID               *Buffer
  )
{
  VOID        *NewBuffer;
  UINTN       NewSize;
  EFI_STATUS  Status;
  UINTN       TotalSize;

  NewBuffer = NULL;
  NewSize   = 0;
  TotalSize = 0;

  Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer);
  if (Status == EFI_BUFFER_TOO_SMALL) {
    TotalSize = NewSize + *BufferSize + sizeof (CHAR16);
  } else if (Status == EFI_NOT_FOUND) {
    TotalSize = *BufferSize + sizeof (CHAR16);
  } else {
    return Status;
  }

  NewBuffer = AllocateZeroPool (TotalSize);
  if (NewBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  if (Status == EFI_BUFFER_TOO_SMALL) {
    Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer);
  }

  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    FreePool (NewBuffer);
    return Status;
  }

  CopyMem ((UINT8 *)NewBuffer + NewSize, Buffer, *BufferSize);
  Status = ShellAddEnvVarToList (
             ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
             NewBuffer,
             TotalSize,
             EFI_VARIABLE_BOOTSERVICE_ACCESS
             );
  if (EFI_ERROR (Status)) {
    FreePool (NewBuffer);
    return Status;
  }

  Status = SHELL_SET_ENVIRONMENT_VARIABLE_V (
             ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
             TotalSize - sizeof (CHAR16),
             NewBuffer
             );
  if (EFI_ERROR (Status)) {
    ShellRemvoeEnvVarFromList (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name);
  }

  FreePool (NewBuffer);
  return Status;
}

/**
  File style interface for Non Volatile Environment Variable (Write).
  This function also caches the environment variable into gShellEnvVarList.

  @param[in] This              The pointer to the EFI_FILE_PROTOCOL object.
  @param[in, out] BufferSize   Size in bytes of Buffer.
  @param[in] Buffer            The pointer to the buffer to write.

  @retval EFI_SUCCESS             The data was successfully write to variable.
  @retval SHELL_OUT_OF_RESOURCES  A memory allocation failed.
**/
EFI_STATUS
EFIAPI
FileInterfaceEnvNonVolWrite (
  IN EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN          *BufferSize,
  IN VOID               *Buffer
  )
{
  VOID        *NewBuffer;
  UINTN       NewSize;
  EFI_STATUS  Status;
  UINTN       TotalSize;

  NewBuffer = NULL;
  NewSize   = 0;
  TotalSize = 0;

  Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer);
  if (Status == EFI_BUFFER_TOO_SMALL) {
    TotalSize = NewSize + *BufferSize + sizeof (CHAR16);
  } else if (Status == EFI_NOT_FOUND) {
    TotalSize = *BufferSize + sizeof (CHAR16);
  } else {
    return Status;
  }

  NewBuffer = AllocateZeroPool (TotalSize);
  if (NewBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  if (Status == EFI_BUFFER_TOO_SMALL) {
    Status = SHELL_GET_ENVIRONMENT_VARIABLE (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name, &NewSize, NewBuffer);
  }

  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
    FreePool (NewBuffer);
    return Status;
  }

  CopyMem ((UINT8 *)NewBuffer + NewSize, Buffer, *BufferSize);
  Status = ShellAddEnvVarToList (
             ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
             NewBuffer,
             TotalSize,
             EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS
             );
  if (EFI_ERROR (Status)) {
    FreePool (NewBuffer);
    return Status;
  }

  Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV (
             ((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name,
             TotalSize - sizeof (CHAR16),
             NewBuffer
             );
  if (EFI_ERROR (Status)) {
    ShellRemvoeEnvVarFromList (((EFI_FILE_PROTOCOL_ENVIRONMENT *)This)->Name);
  }

  FreePool (NewBuffer);
  return Status;
}

/**
  Creates a EFI_FILE_PROTOCOL (almost) object for using to access
  environment variables through file operations.

  @param EnvName    The name of the Environment Variable to be operated on.

  @retval NULL      Memory could not be allocated.
  @return other     a pointer to an EFI_FILE_PROTOCOL structure
**/
EFI_FILE_PROTOCOL *
CreateFileInterfaceEnv (
  IN CONST CHAR16  *EnvName
  )
{
  EFI_STATUS                     Status;
  EFI_FILE_PROTOCOL_ENVIRONMENT  *EnvFileInterface;
  UINTN                          EnvNameSize;
  BOOLEAN                        Volatile;

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

  Status = IsVolatileEnv (EnvName, &Volatile);
  if (EFI_ERROR (Status)) {
    return NULL;
  }

  //
  // Get some memory
  //
  EnvNameSize      = StrSize (EnvName);
  EnvFileInterface = AllocateZeroPool (sizeof (EFI_FILE_PROTOCOL_ENVIRONMENT)+EnvNameSize);
  if (EnvFileInterface == NULL) {
    return (NULL);
  }

  //
  // Assign the generic members
  //
  EnvFileInterface->Revision    = EFI_FILE_REVISION;
  EnvFileInterface->Open        = FileInterfaceOpenNotFound;
  EnvFileInterface->Close       = FileInterfaceEnvClose;
  EnvFileInterface->GetPosition = FileInterfaceNopGetPosition;
  EnvFileInterface->SetPosition = FileInterfaceNopSetPosition;
  EnvFileInterface->GetInfo     = FileInterfaceNopGetInfo;
  EnvFileInterface->SetInfo     = FileInterfaceNopSetInfo;
  EnvFileInterface->Flush       = FileInterfaceNopGeneric;
  EnvFileInterface->Delete      = FileInterfaceEnvDelete;
  EnvFileInterface->Read        = FileInterfaceEnvRead;

  CopyMem (EnvFileInterface->Name, EnvName, EnvNameSize);

  //
  // Assign the different members for Volatile and Non-Volatile variables
  //
  if (Volatile) {
    EnvFileInterface->Write = FileInterfaceEnvVolWrite;
  } else {
    EnvFileInterface->Write = FileInterfaceEnvNonVolWrite;
  }

  return ((EFI_FILE_PROTOCOL *)EnvFileInterface);
}

/**
  Move the cursor position one character backward.

  @param[in] LineLength       Length of a line. Get it by calling QueryMode
  @param[in, out] Column      Current column of the cursor position
  @param[in, out] Row         Current row of the cursor position
**/
VOID
MoveCursorBackward (
  IN     UINTN  LineLength,
  IN OUT UINTN  *Column,
  IN OUT UINTN  *Row
  )
{
  //
  // If current column is 0, move to the last column of the previous line,
  // otherwise, just decrement column.
  //
  if (*Column == 0) {
    *Column = LineLength - 1;
    if (*Row > 0) {
      (*Row)--;
    }

    return;
  }

  (*Column)--;
}

/**
  Move the cursor position one character forward.

  @param[in] LineLength       Length of a line.
  @param[in] TotalRow         Total row of a screen
  @param[in, out] Column      Current column of the cursor position
  @param[in, out] Row         Current row of the cursor position
**/
VOID
MoveCursorForward (
  IN     UINTN  LineLength,
  IN     UINTN  TotalRow,
  IN OUT UINTN  *Column,
  IN OUT UINTN  *Row
  )
{
  //
  // Increment Column.
  // If this puts column past the end of the line, move to first column
  // of the next row.
  //
  (*Column)++;
  if (*Column >= LineLength) {
    (*Column) = 0;
    if ((*Row) < TotalRow - 1) {
      (*Row)++;
    }
  }
}

/**
  Prints out each previously typed command in the command list history log.

  When each screen is full it will pause for a key before continuing.

  @param[in] TotalCols    How many columns are on the screen
  @param[in] TotalRows    How many rows are on the screen
  @param[in] StartColumn  which column to start at
**/
VOID
PrintCommandHistory (
  IN CONST UINTN  TotalCols,
  IN CONST UINTN  TotalRows,
  IN CONST UINTN  StartColumn
  )
{
  BUFFER_LIST  *Node;
  UINTN        Index;
  UINTN        LineNumber;
  UINTN        LineCount;

  ShellPrintDefaultEx (L"\n");
  Index      = 0;
  LineNumber = 0;
  //
  // go through history list...
  //
  for ( Node = (BUFFER_LIST *)GetFirstNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link)
        ; !IsNull (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link)
        ; Node = (BUFFER_LIST *)GetNextNode (&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link)
        )
  {
    Index++;
    LineCount = ((StrLen (Node->Buffer) + StartColumn + 1) / TotalCols) + 1;

    if (LineNumber + LineCount >= TotalRows) {
      ShellPromptForResponseHii (
        ShellPromptResponseTypeEnterContinue,
        STRING_TOKEN (STR_SHELL_ENTER_TO_CONT),
        ShellInfoObject.HiiHandle,
        NULL
        );
      LineNumber = 0;
    }

    ShellPrintDefaultEx (L"%2d. %s\n", Index, Node->Buffer);
    LineNumber += LineCount;
  }
}

//
// This is identical to EFI_FILE_PROTOCOL except for the additional members
// for the buffer, size, and position.
//

typedef struct {
  UINT64                   Revision;
  EFI_FILE_OPEN            Open;
  EFI_FILE_CLOSE           Close;
  EFI_FILE_DELETE          Delete;
  EFI_FILE_READ            Read;
  EFI_FILE_WRITE           Write;
  EFI_FILE_GET_POSITION    GetPosition;
  EFI_FILE_SET_POSITION    SetPosition;
  EFI_FILE_GET_INFO        GetInfo;
  EFI_FILE_SET_INFO        SetInfo;
  EFI_FILE_FLUSH           Flush;
  VOID                     *Buffer;
  UINT64                   Position;
  UINT64                   BufferSize;
  BOOLEAN                  Unicode;
  UINT64                   FileSize;
} EFI_FILE_PROTOCOL_MEM;

/**
  File style interface for Mem (SetPosition).

  @param[in] This       The pointer to the EFI_FILE_PROTOCOL object.
  @param[out] Position  The position to set.

  @retval EFI_SUCCESS             The position was successfully changed.
  @retval EFI_INVALID_PARAMETER   The Position was invalid.
**/
EFI_STATUS
EFIAPI
FileInterfaceMemSetPosition (
  IN EFI_FILE_PROTOCOL  *This,
  OUT UINT64            Position
  )
{
  if (Position <= ((EFI_FILE_PROTOCOL_MEM *)This)->FileSize) {
    ((EFI_FILE_PROTOCOL_MEM *)This)->Position = Position;
    return (EFI_SUCCESS);
  } else {
    return (EFI_INVALID_PARAMETER);
  }
}

/**
  File style interface for Mem (GetPosition).

  @param[in] This       The pointer to the EFI_FILE_PROTOCOL object.
  @param[out] Position  The pointer to the position.

  @retval EFI_SUCCESS   The position was retrieved.
**/
EFI_STATUS
EFIAPI
FileInterfaceMemGetPosition (
  IN EFI_FILE_PROTOCOL  *This,
  OUT UINT64            *Position
  )
{
  *Position = ((EFI_FILE_PROTOCOL_MEM *)This)->Position;
  return (EFI_SUCCESS);
}

/**
  File style interface for Mem (GetInfo).

  @param  This            Protocol instance pointer.
  @param  InformationType Type of information to return in Buffer.
  @param  BufferSize      On input size of buffer, on output amount of data in buffer.
  @param  Buffer          The buffer to return data.

  @retval EFI_SUCCESS          Data was returned.
  @retval EFI_UNSUPPORT        InformationType is not supported.
  @retval EFI_NO_MEDIA         The device has no media.
  @retval EFI_DEVICE_ERROR     The device reported an error.
  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
  @retval EFI_WRITE_PROTECTED  The device is write protected.
  @retval EFI_ACCESS_DENIED    The file was open for read only.
  @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in BufferSize.

**/
EFI_STATUS
EFIAPI
FileInterfaceMemGetInfo (
  IN EFI_FILE_PROTOCOL  *This,
  IN EFI_GUID           *InformationType,
  IN OUT UINTN          *BufferSize,
  OUT VOID              *Buffer
  )
{
  EFI_FILE_INFO  *FileInfo;

  if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
    if (*BufferSize < sizeof (EFI_FILE_INFO)) {
      *BufferSize = sizeof (EFI_FILE_INFO);
      return EFI_BUFFER_TOO_SMALL;
    }

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

    FileInfo       = (EFI_FILE_INFO *)Buffer;
    FileInfo->Size = sizeof (*FileInfo);
    ZeroMem (FileInfo, sizeof (*FileInfo));
    FileInfo->FileSize     = ((EFI_FILE_PROTOCOL_MEM *)This)->FileSize;
    FileInfo->PhysicalSize = FileInfo->FileSize;
    return EFI_SUCCESS;
  }

  return EFI_UNSUPPORTED;
}

/**
  File style interface for Mem (Write).

  @param[in] This              The pointer to the EFI_FILE_PROTOCOL object.
  @param[in, out] BufferSize   Size in bytes of Buffer.
  @param[in] Buffer            The pointer to the buffer to write.

  @retval EFI_OUT_OF_RESOURCES The operation failed due to lack of resources.
  @retval EFI_SUCCESS          The data was written.
**/
EFI_STATUS
EFIAPI
FileInterfaceMemWrite (
  IN EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN          *BufferSize,
  IN VOID               *Buffer
  )
{
  CHAR8                  *AsciiBuffer;
  EFI_FILE_PROTOCOL_MEM  *MemFile;

  MemFile = (EFI_FILE_PROTOCOL_MEM *)This;
  if (MemFile->Unicode) {
    //
    // Unicode
    //
    if ((UINTN)(MemFile->Position + (*BufferSize)) > (UINTN)(MemFile->BufferSize)) {
      MemFile->Buffer = ReallocatePool ((UINTN)(MemFile->BufferSize), (UINTN)(MemFile->BufferSize) + (*BufferSize) + MEM_WRITE_REALLOC_OVERHEAD, MemFile->Buffer);
      if (MemFile->Buffer == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }

      MemFile->BufferSize += (*BufferSize) + MEM_WRITE_REALLOC_OVERHEAD;
    }

    CopyMem (((UINT8 *)MemFile->Buffer) + MemFile->Position, Buffer, *BufferSize);
    MemFile->Position += (*BufferSize);
    MemFile->FileSize  = MemFile->Position;
    return (EFI_SUCCESS);
  } else {
    //
    // Ascii
    //
    AsciiBuffer = AllocateZeroPool (*BufferSize);
    if (AsciiBuffer == NULL) {
      return (EFI_OUT_OF_RESOURCES);
    }

    AsciiSPrint (AsciiBuffer, *BufferSize, "%S", Buffer);
    if ((UINTN)(MemFile->Position + AsciiStrSize (AsciiBuffer)) > (UINTN)(MemFile->BufferSize)) {
      MemFile->Buffer = ReallocatePool ((UINTN)(MemFile->BufferSize), (UINTN)(MemFile->BufferSize) + AsciiStrSize (AsciiBuffer) + MEM_WRITE_REALLOC_OVERHEAD, MemFile->Buffer);
      if (MemFile->Buffer == NULL) {
        FreePool (AsciiBuffer);
        return EFI_OUT_OF_RESOURCES;
      }

      MemFile->BufferSize += AsciiStrSize (AsciiBuffer) + MEM_WRITE_REALLOC_OVERHEAD;
    }

    CopyMem (((UINT8 *)MemFile->Buffer) + MemFile->Position, AsciiBuffer, AsciiStrSize (AsciiBuffer));
    MemFile->Position += (*BufferSize / sizeof (CHAR16));
    MemFile->FileSize  = MemFile->Position;
    FreePool (AsciiBuffer);
    return (EFI_SUCCESS);
  }
}

/**
  File style interface for Mem (Read).

  @param[in] This              The pointer to the EFI_FILE_PROTOCOL object.
  @param[in, out] BufferSize   Size in bytes of Buffer.
  @param[in] Buffer            The pointer to the buffer to fill.

  @retval EFI_SUCCESS   The data was read.
**/
EFI_STATUS
EFIAPI
FileInterfaceMemRead (
  IN EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN          *BufferSize,
  IN VOID               *Buffer
  )
{
  EFI_FILE_PROTOCOL_MEM  *MemFile;

  MemFile = (EFI_FILE_PROTOCOL_MEM *)This;
  if (*BufferSize > (UINTN)((MemFile->FileSize) - (UINTN)(MemFile->Position))) {
    (*BufferSize) = (UINTN)((MemFile->FileSize) - (UINTN)(MemFile->Position));
  }

  CopyMem (Buffer, ((UINT8 *)MemFile->Buffer) + MemFile->Position, (*BufferSize));
  MemFile->Position = MemFile->Position + (*BufferSize);
  return (EFI_SUCCESS);
}

/**
  File style interface for Mem (Close).

  Frees all memory associated with this object.

  @param[in] This       The pointer to the EFI_FILE_PROTOCOL object.

  @retval EFI_SUCCESS   The 'file' was closed.
**/
EFI_STATUS
EFIAPI
FileInterfaceMemClose (
  IN EFI_FILE_PROTOCOL  *This
  )
{
  SHELL_FREE_NON_NULL (((EFI_FILE_PROTOCOL_MEM *)This)->Buffer);
  SHELL_FREE_NON_NULL (This);
  return (EFI_SUCCESS);
}

/**
  Creates a EFI_FILE_PROTOCOL (almost) object for using to access
  a file entirely in memory through file operations.

  @param[in] Unicode Boolean value with TRUE for Unicode and FALSE for Ascii.

  @retval NULL      Memory could not be allocated.
  @return other     A pointer to an EFI_FILE_PROTOCOL structure.
**/
EFI_FILE_PROTOCOL *
CreateFileInterfaceMem (
  IN CONST BOOLEAN  Unicode
  )
{
  EFI_FILE_PROTOCOL_MEM  *FileInterface;

  //
  // Get some memory
  //
  FileInterface = AllocateZeroPool (sizeof (EFI_FILE_PROTOCOL_MEM));
  if (FileInterface == NULL) {
    return (NULL);
  }

  //
  // Assign the generic members
  //
  FileInterface->Revision    = EFI_FILE_REVISION;
  FileInterface->Open        = FileInterfaceOpenNotFound;
  FileInterface->Close       = FileInterfaceMemClose;
  FileInterface->GetPosition = FileInterfaceMemGetPosition;
  FileInterface->SetPosition = FileInterfaceMemSetPosition;
  FileInterface->GetInfo     = FileInterfaceMemGetInfo;
  FileInterface->SetInfo     = FileInterfaceNopSetInfo;
  FileInterface->Flush       = FileInterfaceNopGeneric;
  FileInterface->Delete      = FileInterfaceNopGeneric;
  FileInterface->Read        = FileInterfaceMemRead;
  FileInterface->Write       = FileInterfaceMemWrite;
  FileInterface->Unicode     = Unicode;

  ASSERT (FileInterface->Buffer      == NULL);
  ASSERT (FileInterface->BufferSize  == 0);
  ASSERT (FileInterface->Position    == 0);

  if (Unicode) {
    FileInterface->Buffer = AllocateZeroPool (sizeof (gUnicodeFileTag));
    if (FileInterface->Buffer == NULL) {
      FreePool (FileInterface);
      return NULL;
    }

    *((CHAR16 *)(FileInterface->Buffer)) = EFI_UNICODE_BYTE_ORDER_MARK;
    FileInterface->BufferSize            = 2;
    FileInterface->Position              = 2;
  }

  return ((EFI_FILE_PROTOCOL *)FileInterface);
}

typedef struct {
  UINT64                   Revision;
  EFI_FILE_OPEN            Open;
  EFI_FILE_CLOSE           Close;
  EFI_FILE_DELETE          Delete;
  EFI_FILE_READ            Read;
  EFI_FILE_WRITE           Write;
  EFI_FILE_GET_POSITION    GetPosition;
  EFI_FILE_SET_POSITION    SetPosition;
  EFI_FILE_GET_INFO        GetInfo;
  EFI_FILE_SET_INFO        SetInfo;
  EFI_FILE_FLUSH           Flush;
  BOOLEAN                  Unicode;
  EFI_FILE_PROTOCOL        *Orig;
} EFI_FILE_PROTOCOL_FILE;

/**
  Set a files current position

  @param  This            Protocol instance pointer.
  @param  Position        Byte position from the start of the file.

  @retval EFI_SUCCESS     Data was written.
  @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open.

**/
EFI_STATUS
EFIAPI
FileInterfaceFileSetPosition (
  IN EFI_FILE_PROTOCOL  *This,
  IN UINT64             Position
  )
{
  return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->SetPosition (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, Position);
}

/**
  Get a file's current position

  @param  This            Protocol instance pointer.
  @param  Position        Byte position from the start of the file.

  @retval EFI_SUCCESS     Data was written.
  @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open..

**/
EFI_STATUS
EFIAPI
FileInterfaceFileGetPosition (
  IN EFI_FILE_PROTOCOL  *This,
  OUT UINT64            *Position
  )
{
  return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->GetPosition (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, Position);
}

/**
  Get information about a file.

  @param  This            Protocol instance pointer.
  @param  InformationType Type of information to return in Buffer.
  @param  BufferSize      On input size of buffer, on output amount of data in buffer.
  @param  Buffer          The buffer to return data.

  @retval EFI_SUCCESS          Data was returned.
  @retval EFI_UNSUPPORT        InformationType is not supported.
  @retval EFI_NO_MEDIA         The device has no media.
  @retval EFI_DEVICE_ERROR     The device reported an error.
  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
  @retval EFI_WRITE_PROTECTED  The device is write protected.
  @retval EFI_ACCESS_DENIED    The file was open for read only.
  @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in BufferSize.

**/
EFI_STATUS
EFIAPI
FileInterfaceFileGetInfo (
  IN EFI_FILE_PROTOCOL  *This,
  IN EFI_GUID           *InformationType,
  IN OUT UINTN          *BufferSize,
  OUT VOID              *Buffer
  )
{
  return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->GetInfo (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, InformationType, BufferSize, Buffer);
}

/**
  Set information about a file

  @param  This            Protocol instance pointer.
  @param  InformationType Type of information in Buffer.
  @param  BufferSize      Size of buffer.
  @param  Buffer          The data to write.

  @retval EFI_SUCCESS          Data was returned.
  @retval EFI_UNSUPPORT        InformationType is not supported.
  @retval EFI_NO_MEDIA         The device has no media.
  @retval EFI_DEVICE_ERROR     The device reported an error.
  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
  @retval EFI_WRITE_PROTECTED  The device is write protected.
  @retval EFI_ACCESS_DENIED    The file was open for read only.

**/
EFI_STATUS
EFIAPI
FileInterfaceFileSetInfo (
  IN EFI_FILE_PROTOCOL  *This,
  IN EFI_GUID           *InformationType,
  IN UINTN              BufferSize,
  IN VOID               *Buffer
  )
{
  return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->SetInfo (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, InformationType, BufferSize, Buffer);
}

/**
  Flush data back for the file handle.

  @param  This Protocol instance pointer.

  @retval EFI_SUCCESS          Data was written.
  @retval EFI_UNSUPPORT        Writes to Open directory are not supported.
  @retval EFI_NO_MEDIA         The device has no media.
  @retval EFI_DEVICE_ERROR     The device reported an error.
  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
  @retval EFI_WRITE_PROTECTED  The device is write protected.
  @retval EFI_ACCESS_DENIED    The file was open for read only.
  @retval EFI_VOLUME_FULL      The volume is full.

**/
EFI_STATUS
EFIAPI
FileInterfaceFileFlush (
  IN EFI_FILE_PROTOCOL  *This
  )
{
  return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Flush (((EFI_FILE_PROTOCOL_FILE *)This)->Orig);
}

/**
  Read data from the file.

  @param  This       Protocol instance pointer.
  @param  BufferSize On input size of buffer, on output amount of data in buffer.
  @param  Buffer     The buffer in which data is read.

  @retval EFI_SUCCESS          Data was read.
  @retval EFI_NO_MEDIA         The device has no media.
  @retval EFI_DEVICE_ERROR     The device reported an error.
  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
  @retval EFI_BUFFER_TO_SMALL  BufferSize is too small. BufferSize contains required size.

**/
EFI_STATUS
EFIAPI
FileInterfaceFileRead (
  IN EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN          *BufferSize,
  OUT VOID              *Buffer
  )
{
  EFI_STATUS  Status;
  UINT64      Position;
  CHAR8       *AsciiStrBuffer;
  CHAR16      *UscStrBuffer;
  UINTN       Size;

  if (((EFI_FILE_PROTOCOL_FILE *)This)->Unicode) {
    //
    // Unicode
    // There might be different file tag for the Unicode file. We cannot unconditionally insert the \xFEFF.
    // So we choose to leave the file content as is.
    //
    return (((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Read (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, BufferSize, Buffer));
  } else {
    //
    // Ascii
    //
    *BufferSize = *BufferSize / sizeof (CHAR16) * sizeof (CHAR16);
    if (*BufferSize == 0) {
      return EFI_SUCCESS;
    }

    Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->GetPosition (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, &Position);
    if (EFI_ERROR (Status)) {
      return Status;
    }

    if (Position == 0) {
      //
      // First two bytes in Buffer is for the Unicode file tag.
      //
      *(CHAR16 *)Buffer = gUnicodeFileTag;
      Buffer            = (CHAR16 *)Buffer + 1;
      Size              = *BufferSize / sizeof (CHAR16) - 1;
    } else {
      Size = *BufferSize / sizeof (CHAR16);
    }

    AsciiStrBuffer = AllocateZeroPool (Size + 1);
    if (AsciiStrBuffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    UscStrBuffer = AllocateZeroPool ((Size + 1) * sizeof (CHAR16));
    if (UscStrBuffer == NULL) {
      SHELL_FREE_NON_NULL (AsciiStrBuffer);
      return EFI_OUT_OF_RESOURCES;
    }

    Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Read (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, &Size, AsciiStrBuffer);
    if (!EFI_ERROR (Status)) {
      AsciiStrToUnicodeStrS (AsciiStrBuffer, UscStrBuffer, Size + 1);
      *BufferSize = Size * sizeof (CHAR16);
      CopyMem (Buffer, UscStrBuffer, *BufferSize);
    }

    SHELL_FREE_NON_NULL (AsciiStrBuffer);
    SHELL_FREE_NON_NULL (UscStrBuffer);
    return Status;
  }
}

/**
  Opens a new file relative to the source file's location.

  @param[in]  This       The protocol instance pointer.
  @param[out]  NewHandle Returns File Handle for FileName.
  @param[in]  FileName   Null terminated string. "\", ".", and ".." are supported.
  @param[in]  OpenMode   Open mode for file.
  @param[in]  Attributes Only used for EFI_FILE_MODE_CREATE.

  @retval EFI_SUCCESS          The device was opened.
  @retval EFI_NOT_FOUND        The specified file could not be found on the device.
  @retval EFI_NO_MEDIA         The device has no media.
  @retval EFI_MEDIA_CHANGED    The media has changed.
  @retval EFI_DEVICE_ERROR     The device reported an error.
  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
  @retval EFI_ACCESS_DENIED    The service denied access to the file.
  @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources.
  @retval EFI_VOLUME_FULL      The volume is full.
**/
EFI_STATUS
EFIAPI
FileInterfaceFileOpen (
  IN EFI_FILE_PROTOCOL   *This,
  OUT EFI_FILE_PROTOCOL  **NewHandle,
  IN CHAR16              *FileName,
  IN UINT64              OpenMode,
  IN UINT64              Attributes
  )
{
  return ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Open (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, NewHandle, FileName, OpenMode, Attributes);
}

/**
  Close and delete the file handle.

  @param  This                     Protocol instance pointer.

  @retval EFI_SUCCESS              The device was opened.
  @retval EFI_WARN_DELETE_FAILURE  The handle was closed but the file was not deleted.

**/
EFI_STATUS
EFIAPI
FileInterfaceFileDelete (
  IN EFI_FILE_PROTOCOL  *This
  )
{
  EFI_STATUS  Status;

  Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Delete (((EFI_FILE_PROTOCOL_FILE *)This)->Orig);
  FreePool (This);
  return (Status);
}

/**
  File style interface for File (Close).

  @param[in] This       The pointer to the EFI_FILE_PROTOCOL object.

  @retval EFI_SUCCESS   The file was closed.
**/
EFI_STATUS
EFIAPI
FileInterfaceFileClose (
  IN EFI_FILE_PROTOCOL  *This
  )
{
  EFI_STATUS  Status;

  Status = ((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Close (((EFI_FILE_PROTOCOL_FILE *)This)->Orig);
  FreePool (This);
  return (Status);
}

/**
  File style interface for File (Write).

  If the file was opened with ASCII mode the data will be processed through
  AsciiSPrint before writing.

  @param[in] This              The pointer to the EFI_FILE_PROTOCOL object.
  @param[in, out] BufferSize   Size in bytes of Buffer.
  @param[in] Buffer            The pointer to the buffer to write.

  @retval EFI_SUCCESS   The data was written.
**/
EFI_STATUS
EFIAPI
FileInterfaceFileWrite (
  IN     EFI_FILE_PROTOCOL  *This,
  IN OUT UINTN              *BufferSize,
  IN     VOID               *Buffer
  )
{
  CHAR8       *AsciiBuffer;
  UINTN       Size;
  EFI_STATUS  Status;

  if (((EFI_FILE_PROTOCOL_FILE *)This)->Unicode) {
    //
    // Unicode
    //
    return (((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Write (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, BufferSize, Buffer));
  } else {
    //
    // Ascii
    //
    AsciiBuffer = AllocateZeroPool (*BufferSize);
    if (AsciiBuffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    AsciiSPrint (AsciiBuffer, *BufferSize, "%S", Buffer);
    Size   = AsciiStrSize (AsciiBuffer) - 1; // (we dont need the null terminator)
    Status = (((EFI_FILE_PROTOCOL_FILE *)This)->Orig->Write (((EFI_FILE_PROTOCOL_FILE *)This)->Orig, &Size, AsciiBuffer));
    FreePool (AsciiBuffer);
    return (Status);
  }
}

/**
  Create a file interface with unicode information.

  This will create a new EFI_FILE_PROTOCOL identical to the Templace
  except that the new one has Unicode and Ascii knowledge.

  @param[in] Template   A pointer to the EFI_FILE_PROTOCOL object.
  @param[in] Unicode    TRUE for UCS-2, FALSE for ASCII.

  @return a new EFI_FILE_PROTOCOL object to be used instead of the template.
**/
EFI_FILE_PROTOCOL *
CreateFileInterfaceFile (
  IN CONST EFI_FILE_PROTOCOL  *Template,
  IN CONST BOOLEAN            Unicode
  )
{
  EFI_FILE_PROTOCOL_FILE  *NewOne;

  NewOne = AllocateZeroPool (sizeof (EFI_FILE_PROTOCOL_FILE));
  if (NewOne == NULL) {
    return (NULL);
  }

  CopyMem (NewOne, Template, sizeof (EFI_FILE_PROTOCOL_FILE));
  NewOne->Orig        = (EFI_FILE_PROTOCOL *)Template;
  NewOne->Unicode     = Unicode;
  NewOne->Open        = FileInterfaceFileOpen;
  NewOne->Close       = FileInterfaceFileClose;
  NewOne->Delete      = FileInterfaceFileDelete;
  NewOne->Read        = FileInterfaceFileRead;
  NewOne->Write       = FileInterfaceFileWrite;
  NewOne->GetPosition = FileInterfaceFileGetPosition;
  NewOne->SetPosition = FileInterfaceFileSetPosition;
  NewOne->GetInfo     = FileInterfaceFileGetInfo;
  NewOne->SetInfo     = FileInterfaceFileSetInfo;
  NewOne->Flush       = FileInterfaceFileFlush;

  return ((EFI_FILE_PROTOCOL *)NewOne);
}
