/** @file
EFI_FILE_PROTOCOL wrappers for other items (Like Environment Variables,
StdIn, StdOut, StdErr, etc...).
Copyright 2016 Dell Inc.
Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.
(C) Copyright 2013 Hewlett-Packard Development Company, L.P.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "Shell.h"
#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 minimun 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)) {
//
// Calulate 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 sytle 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 (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
)
{
return (SHELL_GET_ENVIRONMENT_VARIABLE(
((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name,
BufferSize,
Buffer));
}
/**
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 (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);
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);
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 = FileInterfaceNopGetInfo;
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
)
{
CHAR8 *AsciiStrBuffer;
CHAR16 *UscStrBuffer;
UINTN Size;
UINTN CharNum;
EFI_STATUS Status;
if (((EFI_FILE_PROTOCOL_FILE*)This)->Unicode) {
//
// Unicode
//
return (((EFI_FILE_PROTOCOL_FILE*)This)->Orig->Read(((EFI_FILE_PROTOCOL_FILE*)This)->Orig, BufferSize, Buffer));
} else {
//
// Ascii
//
Size = (*BufferSize) / sizeof(CHAR16);
AsciiStrBuffer = AllocateZeroPool(Size + sizeof(CHAR8));
if (AsciiStrBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
UscStrBuffer = AllocateZeroPool(*BufferSize + 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)) {
CharNum = UnicodeSPrint(UscStrBuffer, *BufferSize + sizeof(CHAR16), L"%a", AsciiStrBuffer);
if (CharNum == Size) {
CopyMem (Buffer, UscStrBuffer, *BufferSize);
} else {
Status = EFI_UNSUPPORTED;
}
}
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);
}