/** @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.
(C) Copyright 2013 Hewlett-Packard Development Company, L.P.
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
// ^ ^
// 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:"
// 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 = "\\"
//
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 (((StrCmp (FileInfo->FileName, L".") == 0) || (StrCmp (FileInfo->FileName, L"..") == 0)) ||
((((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;
ShellPrintEx (-1, -1, 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;
}
ShellPrintEx (-1, -1, 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);
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);
}