blob: 088030d29f672151a077317c05f78c73bc1de3a0 [file] [log] [blame]
/** @file
Implements filebuffer interface functions.
Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved. <BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "TextEditor.h"
#include <Guid/FileSystemInfo.h>
#include <Library/FileHandleLib.h>
EFI_EDITOR_FILE_BUFFER FileBuffer;
EFI_EDITOR_FILE_BUFFER FileBufferBackupVar;
//
// for basic initialization of FileBuffer
//
EFI_EDITOR_FILE_BUFFER FileBufferConst = {
NULL,
FileTypeUnicode,
NULL,
NULL,
0,
{
0,
0
},
{
0,
0
},
{
0,
0
},
{
0,
0
},
FALSE,
TRUE,
FALSE,
NULL
};
//
// the whole edit area needs to be refreshed
//
BOOLEAN FileBufferNeedRefresh;
//
// only the current line in edit area needs to be refresh
//
BOOLEAN FileBufferOnlyLineNeedRefresh;
BOOLEAN FileBufferMouseNeedRefresh;
extern BOOLEAN EditorMouseAction;
/**
Initialization function for FileBuffer.
@param EFI_SUCCESS The initialization was successful.
@param EFI_LOAD_ERROR A default name could not be created.
@param EFI_OUT_OF_RESOURCES A memory allocation failed.
**/
EFI_STATUS
FileBufferInit (
VOID
)
{
//
// basically initialize the FileBuffer
//
CopyMem (&FileBuffer, &FileBufferConst, sizeof (EFI_EDITOR_FILE_BUFFER));
CopyMem (&FileBufferBackupVar, &FileBufferConst, sizeof (EFI_EDITOR_FILE_BUFFER));
//
// set default FileName
//
FileBuffer.FileName = EditGetDefaultFileName (L"txt");
if (FileBuffer.FileName == NULL) {
return EFI_LOAD_ERROR;
}
FileBuffer.ListHead = AllocateZeroPool (sizeof (LIST_ENTRY));
if (FileBuffer.ListHead == NULL) {
return EFI_OUT_OF_RESOURCES;
}
InitializeListHead (FileBuffer.ListHead);
FileBuffer.DisplayPosition.Row = 2;
FileBuffer.DisplayPosition.Column = 1;
FileBuffer.LowVisibleRange.Row = 2;
FileBuffer.LowVisibleRange.Column = 1;
FileBufferNeedRefresh = FALSE;
FileBufferMouseNeedRefresh = FALSE;
FileBufferOnlyLineNeedRefresh = FALSE;
return EFI_SUCCESS;
}
/**
Backup function for FileBuffer. Only backup the following items:
Mouse/Cursor position
File Name, Type, ReadOnly, Modified
Insert Mode
This is for making the file buffer refresh as few as possible.
@retval EFI_SUCCESS The backup operation was successful.
**/
EFI_STATUS
FileBufferBackup (
VOID
)
{
FileBufferBackupVar.MousePosition = FileBuffer.MousePosition;
SHELL_FREE_NON_NULL (FileBufferBackupVar.FileName);
FileBufferBackupVar.FileName = NULL;
FileBufferBackupVar.FileName = StrnCatGrow (&FileBufferBackupVar.FileName, NULL, FileBuffer.FileName, 0);
FileBufferBackupVar.ModeInsert = FileBuffer.ModeInsert;
FileBufferBackupVar.FileType = FileBuffer.FileType;
FileBufferBackupVar.FilePosition = FileBuffer.FilePosition;
FileBufferBackupVar.LowVisibleRange = FileBuffer.LowVisibleRange;
FileBufferBackupVar.FileModified = FileBuffer.FileModified;
FileBufferBackupVar.ReadOnly = FileBuffer.ReadOnly;
return EFI_SUCCESS;
}
/**
Advance to the next Count lines
@param[in] Count The line number to advance by.
@param[in] CurrentLine The pointer to the current line structure.
@param[in] LineList The pointer to the linked list of lines.
@retval NULL There was an error.
@return The line structure after the advance.
**/
EFI_EDITOR_LINE *
InternalEditorMiscLineAdvance (
IN CONST UINTN Count,
IN CONST EFI_EDITOR_LINE *CurrentLine,
IN CONST LIST_ENTRY *LineList
)
{
UINTN Index;
CONST EFI_EDITOR_LINE *Line;
if ((CurrentLine == NULL) || (LineList == NULL)) {
return NULL;
}
for (Line = CurrentLine, Index = 0; Index < Count; Index++) {
//
// if already last line
//
if (Line->Link.ForwardLink == LineList) {
return NULL;
}
Line = CR (Line->Link.ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
}
return ((EFI_EDITOR_LINE *)Line);
}
/**
Retreat to the previous Count lines.
@param[in] Count The line number to retreat by.
@param[in] CurrentLine The pointer to the current line structure.
@param[in] LineList The pointer to the linked list of lines.
@retval NULL There was an error.
@return The line structure after the retreat.
**/
EFI_EDITOR_LINE *
InternalEditorMiscLineRetreat (
IN CONST UINTN Count,
IN CONST EFI_EDITOR_LINE *CurrentLine,
IN CONST LIST_ENTRY *LineList
)
{
UINTN Index;
CONST EFI_EDITOR_LINE *Line;
if ((CurrentLine == NULL) || (LineList == NULL)) {
return NULL;
}
for (Line = CurrentLine, Index = 0; Index < Count; Index++) {
//
// already the first line
//
if (Line->Link.BackLink == LineList) {
return NULL;
}
Line = CR (Line->Link.BackLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
}
return ((EFI_EDITOR_LINE *)Line);
}
/**
Advance/Retreat lines
@param[in] Count line number to advance/retreat
>0 : advance
<0 : retreat
@retval NULL An error occurred.
@return The line after advance/retreat.
**/
EFI_EDITOR_LINE *
MoveLine (
IN CONST INTN Count
)
{
EFI_EDITOR_LINE *Line;
UINTN AbsCount;
//
// if < 0, then retreat
// if > 0, the advance
//
if (Count <= 0) {
AbsCount = (UINTN)ABS (Count);
Line = InternalEditorMiscLineRetreat (AbsCount, MainEditor.FileBuffer->CurrentLine, MainEditor.FileBuffer->ListHead);
} else {
Line = InternalEditorMiscLineAdvance ((UINTN)Count, MainEditor.FileBuffer->CurrentLine, MainEditor.FileBuffer->ListHead);
}
return Line;
}
/**
Function to update the 'screen' to display the mouse position.
@retval EFI_SUCCESS The backup operation was successful.
**/
EFI_STATUS
FileBufferRestoreMousePosition (
VOID
)
{
EFI_EDITOR_COLOR_UNION Orig;
EFI_EDITOR_COLOR_UNION New;
UINTN FRow;
UINTN FColumn;
BOOLEAN HasCharacter;
EFI_EDITOR_LINE *CurrentLine;
EFI_EDITOR_LINE *Line;
CHAR16 Value;
//
// variable initialization
//
Line = NULL;
if (MainEditor.MouseSupported) {
if (FileBufferMouseNeedRefresh) {
FileBufferMouseNeedRefresh = FALSE;
//
// if mouse position not moved and only mouse action
// so do not need to refresh mouse position
//
if ( ((FileBuffer.MousePosition.Row == FileBufferBackupVar.MousePosition.Row) &&
(FileBuffer.MousePosition.Column == FileBufferBackupVar.MousePosition.Column))
&& EditorMouseAction)
{
return EFI_SUCCESS;
}
//
// backup the old screen attributes
//
Orig = MainEditor.ColorAttributes;
New.Data = 0;
New.Colors.Foreground = Orig.Colors.Background & 0xF;
New.Colors.Background = Orig.Colors.Foreground & 0x7;
//
// clear the old mouse position
//
FRow = FileBuffer.LowVisibleRange.Row + FileBufferBackupVar.MousePosition.Row - 2;
FColumn = FileBuffer.LowVisibleRange.Column + FileBufferBackupVar.MousePosition.Column - 1;
HasCharacter = TRUE;
if (FRow > FileBuffer.NumLines) {
HasCharacter = FALSE;
} else {
CurrentLine = FileBuffer.CurrentLine;
Line = MoveLine (FRow - FileBuffer.FilePosition.Row);
if ((Line == NULL) || (FColumn > Line->Size)) {
HasCharacter = FALSE;
}
FileBuffer.CurrentLine = CurrentLine;
}
ShellPrintEx (
(INT32)FileBufferBackupVar.MousePosition.Column - 1,
(INT32)FileBufferBackupVar.MousePosition.Row - 1,
L" "
);
if (HasCharacter) {
Value = (Line->Buffer[FColumn - 1]);
ShellPrintEx (
(INT32)FileBufferBackupVar.MousePosition.Column - 1,
(INT32)FileBufferBackupVar.MousePosition.Row - 1,
L"%c",
Value
);
}
//
// set the new mouse position
//
gST->ConOut->SetAttribute (gST->ConOut, New.Data & 0x7F);
//
// clear the old mouse position
//
FRow = FileBuffer.LowVisibleRange.Row + FileBuffer.MousePosition.Row - 2;
FColumn = FileBuffer.LowVisibleRange.Column + FileBuffer.MousePosition.Column - 1;
HasCharacter = TRUE;
if (FRow > FileBuffer.NumLines) {
HasCharacter = FALSE;
} else {
CurrentLine = FileBuffer.CurrentLine;
Line = MoveLine (FRow - FileBuffer.FilePosition.Row);
if ((Line == NULL) || (FColumn > Line->Size)) {
HasCharacter = FALSE;
}
FileBuffer.CurrentLine = CurrentLine;
}
ShellPrintEx (
(INT32)FileBuffer.MousePosition.Column - 1,
(INT32)FileBuffer.MousePosition.Row - 1,
L" "
);
if (HasCharacter) {
Value = Line->Buffer[FColumn - 1];
ShellPrintEx (
(INT32)FileBuffer.MousePosition.Column - 1,
(INT32)FileBuffer.MousePosition.Row - 1,
L"%c",
Value
);
}
//
// end of HasCharacter
//
gST->ConOut->SetAttribute (gST->ConOut, Orig.Data);
}
//
// end of MouseNeedRefresh
//
}
//
// end of MouseSupported
//
return EFI_SUCCESS;
}
/**
Free all the lines in FileBuffer
Fields affected:
Lines
CurrentLine
NumLines
ListHead
@retval EFI_SUCCESS The operation was successful.
**/
EFI_STATUS
FileBufferFreeLines (
VOID
)
{
LIST_ENTRY *Link;
EFI_EDITOR_LINE *Line;
//
// free all the lines
//
if (FileBuffer.Lines != NULL) {
Line = FileBuffer.Lines;
Link = &(Line->Link);
do {
Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
Link = Link->ForwardLink;
//
// free line's buffer and line itself
//
LineFree (Line);
} while (Link != FileBuffer.ListHead);
}
//
// clean the line list related structure
//
FileBuffer.Lines = NULL;
FileBuffer.CurrentLine = NULL;
FileBuffer.NumLines = 0;
FileBuffer.ListHead->ForwardLink = FileBuffer.ListHead;
FileBuffer.ListHead->BackLink = FileBuffer.ListHead;
return EFI_SUCCESS;
}
/**
Cleanup function for FileBuffer.
@retval EFI_SUCCESS The cleanup was successful.
**/
EFI_STATUS
FileBufferCleanup (
VOID
)
{
EFI_STATUS Status;
SHELL_FREE_NON_NULL (FileBuffer.FileName);
//
// free all the lines
//
Status = FileBufferFreeLines ();
SHELL_FREE_NON_NULL (FileBuffer.ListHead);
FileBuffer.ListHead = NULL;
SHELL_FREE_NON_NULL (FileBufferBackupVar.FileName);
return Status;
}
/**
Print a line specified by Line on a row specified by Row of the screen.
@param[in] Line The line to print.
@param[in] Row The row on the screen to print onto (begin from 1).
@retval EFI_SUCCESS The printing was successful.
**/
EFI_STATUS
FileBufferPrintLine (
IN CONST EFI_EDITOR_LINE *Line,
IN CONST UINTN Row
)
{
CHAR16 *Buffer;
UINTN Limit;
CHAR16 *PrintLine;
CHAR16 *PrintLine2;
UINTN BufLen;
//
// print start from correct character
//
Buffer = Line->Buffer + FileBuffer.LowVisibleRange.Column - 1;
Limit = Line->Size - FileBuffer.LowVisibleRange.Column + 1;
if (Limit > Line->Size) {
Limit = 0;
}
BufLen = (MainEditor.ScreenSize.Column + 1) * sizeof (CHAR16);
PrintLine = AllocatePool (BufLen);
if (PrintLine != NULL) {
StrnCpyS (PrintLine, BufLen/sizeof (CHAR16), Buffer, MIN (Limit, MainEditor.ScreenSize.Column));
for (Limit = StrLen (PrintLine); Limit < MainEditor.ScreenSize.Column; Limit++) {
PrintLine[Limit] = L' ';
}
PrintLine[MainEditor.ScreenSize.Column] = CHAR_NULL;
PrintLine2 = AllocatePool (BufLen * 2);
if (PrintLine2 != NULL) {
ShellCopySearchAndReplace (PrintLine, PrintLine2, BufLen * 2, L"%", L"^%", FALSE, FALSE);
ShellPrintEx (
0,
(INT32)Row - 1,
L"%s",
PrintLine2
);
FreePool (PrintLine2);
}
FreePool (PrintLine);
}
return EFI_SUCCESS;
}
/**
Set the cursor position according to FileBuffer.DisplayPosition.
@retval EFI_SUCCESS The operation was successful.
**/
EFI_STATUS
FileBufferRestorePosition (
VOID
)
{
//
// set cursor position
//
return (gST->ConOut->SetCursorPosition (
gST->ConOut,
FileBuffer.DisplayPosition.Column - 1,
FileBuffer.DisplayPosition.Row - 1
));
}
/**
Refresh the screen with whats in the buffer.
@retval EFI_SUCCESS The refresh was successful.
@retval EFI_LOAD_ERROR There was an error finding what to write.
**/
EFI_STATUS
FileBufferRefresh (
VOID
)
{
LIST_ENTRY *Link;
EFI_EDITOR_LINE *Line;
UINTN Row;
//
// if it's the first time after editor launch, so should refresh
//
if (!EditorFirst) {
//
// no definite required refresh
// and file position displayed on screen has not been changed
//
if (!FileBufferNeedRefresh &&
!FileBufferOnlyLineNeedRefresh &&
(FileBufferBackupVar.LowVisibleRange.Row == FileBuffer.LowVisibleRange.Row) &&
(FileBufferBackupVar.LowVisibleRange.Column == FileBuffer.LowVisibleRange.Column)
)
{
FileBufferRestoreMousePosition ();
FileBufferRestorePosition ();
return EFI_SUCCESS;
}
}
gST->ConOut->EnableCursor (gST->ConOut, FALSE);
//
// only need to refresh current line
//
if (FileBufferOnlyLineNeedRefresh &&
(FileBufferBackupVar.LowVisibleRange.Row == FileBuffer.LowVisibleRange.Row) &&
(FileBufferBackupVar.LowVisibleRange.Column == FileBuffer.LowVisibleRange.Column)
)
{
EditorClearLine (FileBuffer.DisplayPosition.Row, MainEditor.ScreenSize.Column, MainEditor.ScreenSize.Row);
FileBufferPrintLine (
FileBuffer.CurrentLine,
FileBuffer.DisplayPosition.Row
);
} else {
//
// the whole edit area need refresh
//
//
// no line
//
if (FileBuffer.Lines == NULL) {
FileBufferRestoreMousePosition ();
FileBufferRestorePosition ();
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
return EFI_SUCCESS;
}
//
// get the first line that will be displayed
//
Line = MoveLine (FileBuffer.LowVisibleRange.Row - FileBuffer.FilePosition.Row);
if (Line == NULL) {
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
return EFI_LOAD_ERROR;
}
Link = &(Line->Link);
Row = 2;
do {
Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
//
// print line at row
//
FileBufferPrintLine (Line, Row);
Link = Link->ForwardLink;
Row++;
} while (Link != FileBuffer.ListHead && Row <= (MainEditor.ScreenSize.Row - 1));
//
// while not file end and not screen full
//
while (Row <= (MainEditor.ScreenSize.Row - 1)) {
EditorClearLine (Row, MainEditor.ScreenSize.Column, MainEditor.ScreenSize.Row);
Row++;
}
}
FileBufferRestoreMousePosition ();
FileBufferRestorePosition ();
FileBufferNeedRefresh = FALSE;
FileBufferOnlyLineNeedRefresh = FALSE;
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
return EFI_SUCCESS;
}
/**
Create a new line and append it to the line list.
Fields affected:
NumLines
Lines
@retval NULL The create line failed.
@return The line created.
**/
EFI_EDITOR_LINE *
FileBufferCreateLine (
VOID
)
{
EFI_EDITOR_LINE *Line;
//
// allocate a line structure
//
Line = AllocateZeroPool (sizeof (EFI_EDITOR_LINE));
if (Line == NULL) {
return NULL;
}
//
// initialize the structure
//
Line->Signature = LINE_LIST_SIGNATURE;
Line->Size = 0;
Line->TotalSize = 0;
Line->Type = NewLineTypeDefault;
//
// initial buffer of the line is "\0"
//
ASSERT (CHAR_NULL == CHAR_NULL);
Line->Buffer = CatSPrint (NULL, L"\0");
if (Line->Buffer == NULL) {
return NULL;
}
FileBuffer.NumLines++;
//
// insert the line into line list
//
InsertTailList (FileBuffer.ListHead, &Line->Link);
if (FileBuffer.Lines == NULL) {
FileBuffer.Lines = CR (FileBuffer.ListHead->ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
}
return Line;
}
/**
Set FileName field in FileBuffer.
@param Str The file name to set.
@retval EFI_SUCCESS The filename was successfully set.
@retval EFI_OUT_OF_RESOURCES A memory allocation failed.
@retval EFI_INVALID_PARAMETER Str is not a valid filename.
**/
EFI_STATUS
FileBufferSetFileName (
IN CONST CHAR16 *Str
)
{
//
// Verify the parameters
//
if (!IsValidFileName (Str)) {
return (EFI_INVALID_PARAMETER);
}
//
// free the old file name
//
SHELL_FREE_NON_NULL (FileBuffer.FileName);
//
// Allocate and set the new name
//
FileBuffer.FileName = CatSPrint (NULL, L"%s", Str);
if (FileBuffer.FileName == NULL) {
return EFI_OUT_OF_RESOURCES;
}
return EFI_SUCCESS;
}
/**
Free the existing file lines and reset the modified flag.
@retval EFI_SUCCESS The operation was successful.
**/
EFI_STATUS
FileBufferFree (
VOID
)
{
//
// free all the lines
//
FileBufferFreeLines ();
FileBuffer.FileModified = FALSE;
return EFI_SUCCESS;
}
/**
Read a file from disk into the FileBuffer.
@param[in] FileName The filename to read.
@param[in] Recover TRUE if is for recover mode, no information printouts.
@retval EFI_SUCCESS The load was successful.
@retval EFI_LOAD_ERROR The load failed.
@retval EFI_OUT_OF_RESOURCES A memory allocation failed.
@retval EFI_INVALID_PARAMETER FileName is a directory.
**/
EFI_STATUS
FileBufferRead (
IN CONST CHAR16 *FileName,
IN CONST BOOLEAN Recover
)
{
EFI_EDITOR_LINE *Line;
EE_NEWLINE_TYPE Type;
UINTN LoopVar1;
UINTN LoopVar2;
UINTN LineSize;
VOID *Buffer;
CHAR16 *UnicodeBuffer;
UINT8 *AsciiBuffer;
UINTN FileSize;
SHELL_FILE_HANDLE FileHandle;
BOOLEAN CreateFile;
EFI_STATUS Status;
UINTN LineSizeBackup;
EFI_FILE_INFO *Info;
Line = NULL;
LoopVar1 = 0;
FileSize = 0;
UnicodeBuffer = NULL;
Type = NewLineTypeDefault;
FileHandle = NULL;
CreateFile = FALSE;
//
// in this function, when you return error ( except EFI_OUT_OF_RESOURCES )
// you should set status string via StatusBarSetStatusString(L"blah")
// since this function maybe called before the editorhandleinput loop
// so any error will cause editor return
// so if you want to print the error status
// you should set the status string
//
//
// try to open the file
//
Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ, 0);
if (!EFI_ERROR (Status)) {
CreateFile = FALSE;
if (FileHandle == NULL) {
StatusBarSetStatusString (L"Disk Error");
return EFI_LOAD_ERROR;
}
Info = ShellGetFileInfo (FileHandle);
if (Info->Attribute & EFI_FILE_DIRECTORY) {
StatusBarSetStatusString (L"Directory Can Not Be Edited");
FreePool (Info);
return EFI_INVALID_PARAMETER;
}
if (Info->Attribute & EFI_FILE_READ_ONLY) {
FileBuffer.ReadOnly = TRUE;
} else {
FileBuffer.ReadOnly = FALSE;
}
//
// get file size
//
FileSize = (UINTN)Info->FileSize;
FreePool (Info);
} else if (Status == EFI_NOT_FOUND) {
//
// file not exists. add create and try again
//
Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, 0);
if (EFI_ERROR (Status)) {
if ((Status == EFI_WRITE_PROTECTED) ||
(Status == EFI_ACCESS_DENIED) ||
(Status == EFI_NO_MEDIA) ||
(Status == EFI_MEDIA_CHANGED)
)
{
StatusBarSetStatusString (L"Access Denied");
} else if ((Status == EFI_DEVICE_ERROR) || (Status == EFI_VOLUME_CORRUPTED) || (Status == EFI_VOLUME_FULL)) {
StatusBarSetStatusString (L"Disk Error");
} else {
StatusBarSetStatusString (L"Invalid File Name or Current-working-directory");
}
return Status;
} else {
//
// it worked. now delete it and move on with the name (now validated)
//
Status = ShellDeleteFile (&FileHandle);
if (Status == EFI_WARN_DELETE_FAILURE) {
Status = EFI_ACCESS_DENIED;
}
FileHandle = NULL;
if (EFI_ERROR (Status)) {
StatusBarSetStatusString (L"Access Denied");
return Status;
}
}
//
// file doesn't exist, so set CreateFile to TRUE
//
CreateFile = TRUE;
FileBuffer.ReadOnly = FALSE;
//
// all the check ends
// so now begin to set file name, free lines
//
if (StrCmp (FileName, FileBuffer.FileName) != 0) {
FileBufferSetFileName (FileName);
}
//
// free the old lines
//
FileBufferFree ();
}
//
// the file exists
//
if (!CreateFile) {
//
// allocate buffer to read file
//
Buffer = AllocateZeroPool (FileSize);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// read file into Buffer
//
Status = ShellReadFile (FileHandle, &FileSize, Buffer);
ShellCloseFile (&FileHandle);
FileHandle = NULL;
if (EFI_ERROR (Status)) {
StatusBarSetStatusString (L"Read File Failed");
SHELL_FREE_NON_NULL (Buffer);
return EFI_LOAD_ERROR;
}
//
// nothing in this file
//
if (FileSize == 0) {
SHELL_FREE_NON_NULL (Buffer);
//
// since has no head, so only can be an ASCII file
//
FileBuffer.FileType = FileTypeAscii;
goto Done;
}
AsciiBuffer = Buffer;
if (FileSize < 2) {
//
// size < Unicode file header, so only can be ASCII file
//
FileBuffer.FileType = FileTypeAscii;
} else {
//
// Unicode file
//
if (*(UINT16 *)Buffer == EFI_UNICODE_BYTE_ORDER_MARK) {
//
// Unicode file's size should be even
//
if ((FileSize % 2) != 0) {
StatusBarSetStatusString (L"File Format Wrong");
SHELL_FREE_NON_NULL (Buffer);
return EFI_LOAD_ERROR;
}
FileSize /= 2;
FileBuffer.FileType = FileTypeUnicode;
UnicodeBuffer = Buffer;
//
// pass this 0xff and 0xfe
//
UnicodeBuffer++;
FileSize--;
} else {
FileBuffer.FileType = FileTypeAscii;
}
//
// end of AsciiBuffer ==
//
}
//
// end of FileSize < 2
// all the check ends
// so now begin to set file name, free lines
//
if (StrCmp (FileName, FileBuffer.FileName) != 0) {
FileBufferSetFileName (FileName);
}
//
// free the old lines
//
FileBufferFree ();
//
// parse file content line by line
//
for (LoopVar1 = 0; LoopVar1 < FileSize; LoopVar1++) {
Type = NewLineTypeUnknown;
for (LineSize = LoopVar1; LineSize < FileSize; LineSize++) {
if (FileBuffer.FileType == FileTypeAscii) {
if (AsciiBuffer[LineSize] == CHAR_CARRIAGE_RETURN) {
Type = NewLineTypeCarriageReturn;
//
// has LF following
//
if (LineSize < FileSize - 1) {
if (AsciiBuffer[LineSize + 1] == CHAR_LINEFEED) {
Type = NewLineTypeCarriageReturnLineFeed;
}
}
break;
} else if (AsciiBuffer[LineSize] == CHAR_LINEFEED) {
Type = NewLineTypeLineFeed;
//
// has CR following
//
if (LineSize < FileSize - 1) {
if (AsciiBuffer[LineSize + 1] == CHAR_CARRIAGE_RETURN) {
Type = NewLineTypeLineFeedCarriageReturn;
}
}
break;
}
} else {
if (UnicodeBuffer[LineSize] == CHAR_CARRIAGE_RETURN) {
Type = NewLineTypeCarriageReturn;
//
// has LF following
//
if (LineSize < FileSize - 1) {
if (UnicodeBuffer[LineSize + 1] == CHAR_LINEFEED) {
Type = NewLineTypeCarriageReturnLineFeed;
}
}
break;
} else if (UnicodeBuffer[LineSize] == CHAR_LINEFEED) {
Type = NewLineTypeLineFeed;
//
// has CR following
//
if (LineSize < FileSize - 1) {
if (UnicodeBuffer[LineSize + 1] == CHAR_CARRIAGE_RETURN) {
Type = NewLineTypeLineFeedCarriageReturn;
}
}
break;
}
}
//
// endif == ASCII
//
}
//
// end of for LineSize
//
// if the type is wrong, then exit
//
if (Type == NewLineTypeUnknown) {
//
// Now if Type is NewLineTypeUnknown, it should be file end
//
Type = NewLineTypeDefault;
}
LineSizeBackup = LineSize;
//
// create a new line
//
Line = FileBufferCreateLine ();
if (Line == NULL) {
SHELL_FREE_NON_NULL (Buffer);
return EFI_OUT_OF_RESOURCES;
}
//
// calculate file length
//
LineSize -= LoopVar1;
//
// Unicode and one CHAR_NULL
//
SHELL_FREE_NON_NULL (Line->Buffer);
Line->Buffer = AllocateZeroPool (LineSize * 2 + 2);
if (Line->Buffer == NULL) {
RemoveEntryList (&Line->Link);
return EFI_OUT_OF_RESOURCES;
}
//
// copy this line to Line->Buffer
//
for (LoopVar2 = 0; LoopVar2 < LineSize; LoopVar2++) {
if (FileBuffer.FileType == FileTypeAscii) {
Line->Buffer[LoopVar2] = (CHAR16)AsciiBuffer[LoopVar1];
} else {
Line->Buffer[LoopVar2] = UnicodeBuffer[LoopVar1];
}
LoopVar1++;
}
//
// LoopVar1 now points to where CHAR_CARRIAGE_RETURN or CHAR_LINEFEED;
//
Line->Buffer[LineSize] = 0;
Line->Size = LineSize;
Line->TotalSize = LineSize;
Line->Type = Type;
if ((Type == NewLineTypeCarriageReturnLineFeed) || (Type == NewLineTypeLineFeedCarriageReturn)) {
LoopVar1++;
}
//
// last character is a return, SO create a new line
//
if ((((Type == NewLineTypeCarriageReturnLineFeed) || (Type == NewLineTypeLineFeedCarriageReturn)) && (LineSizeBackup == FileSize - 2)) ||
(((Type == NewLineTypeLineFeed) || (Type == NewLineTypeCarriageReturn)) && (LineSizeBackup == FileSize - 1))
)
{
Line = FileBufferCreateLine ();
if (Line == NULL) {
SHELL_FREE_NON_NULL (Buffer);
return EFI_OUT_OF_RESOURCES;
}
}
//
// end of if
//
}
//
// end of LoopVar1
//
SHELL_FREE_NON_NULL (Buffer);
}
//
// end of if CreateFile
//
Done:
FileBuffer.DisplayPosition.Row = 2;
FileBuffer.DisplayPosition.Column = 1;
FileBuffer.LowVisibleRange.Row = 1;
FileBuffer.LowVisibleRange.Column = 1;
FileBuffer.FilePosition.Row = 1;
FileBuffer.FilePosition.Column = 1;
FileBuffer.MousePosition.Row = 2;
FileBuffer.MousePosition.Column = 1;
if (!Recover) {
UnicodeBuffer = CatSPrint (NULL, L"%d Lines Read", FileBuffer.NumLines);
if (UnicodeBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
StatusBarSetStatusString (UnicodeBuffer);
FreePool (UnicodeBuffer);
}
/*
//
// check whether we have fs?: in filename
//
LoopVar1 = 0;
FSMappingPtr = NULL;
while (FileName[LoopVar1] != 0) {
if (FileName[LoopVar1] == L':') {
FSMappingPtr = &FileName[LoopVar1];
break;
}
LoopVar1++;
}
if (FSMappingPtr == NULL) {
CurDir = ShellGetCurrentDir (NULL);
} else {
LoopVar1 = 0;
LoopVar2 = 0;
while (FileName[LoopVar1] != 0) {
if (FileName[LoopVar1] == L':') {
break;
}
FSMapping[LoopVar2++] = FileName[LoopVar1];
LoopVar1++;
}
FSMapping[LoopVar2] = 0;
CurDir = ShellGetCurrentDir (FSMapping);
}
if (CurDir != NULL) {
for (LoopVar1 = 0; LoopVar1 < StrLen (CurDir) && CurDir[LoopVar1] != ':'; LoopVar1++);
CurDir[LoopVar1] = 0;
DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) ShellGetMap (CurDir);
FreePool (CurDir);
} else {
return EFI_LOAD_ERROR;
}
Status = LibDevicePathToInterface (
&gEfiSimpleFileSystemProtocolGuid,
DevicePath,
(VOID **) &Vol
);
if (EFI_ERROR (Status)) {
return EFI_LOAD_ERROR;
}
Status = Vol->OpenVolume (Vol, &RootFs);
if (EFI_ERROR (Status)) {
return EFI_LOAD_ERROR;
}
//
// Get volume information of file system
//
Size = SIZE_OF_EFI_FILE_SYSTEM_INFO + 100;
VolumeInfo = (EFI_FILE_SYSTEM_INFO *) AllocateZeroPool (Size);
Status = RootFs->GetInfo (RootFs, &gEfiFileSystemInfoGuid, &Size, VolumeInfo);
if (EFI_ERROR (Status)) {
RootFs->Close (RootFs);
return EFI_LOAD_ERROR;
}
if (VolumeInfo->ReadOnly) {
StatusBarSetStatusString (L"WARNING: Volume Read Only");
}
FreePool (VolumeInfo);
RootFs->Close (RootFs);
}
//
*/
//
// has line
//
if (FileBuffer.Lines != 0) {
FileBuffer.CurrentLine = CR (FileBuffer.ListHead->ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
} else {
//
// create a dummy line
//
Line = FileBufferCreateLine ();
if (Line == NULL) {
return EFI_OUT_OF_RESOURCES;
}
FileBuffer.CurrentLine = Line;
}
FileBuffer.FileModified = FALSE;
FileBufferNeedRefresh = TRUE;
FileBufferOnlyLineNeedRefresh = FALSE;
FileBufferMouseNeedRefresh = TRUE;
return EFI_SUCCESS;
}
/**
According to FileBuffer.NewLineType & FileBuffer.FileType,
get the return buffer and size.
@param[in] Type The type of line.
@param[out] Buffer The buffer to fill.
@param[out] Size The amount of the buffer used on return.
**/
VOID
GetNewLine (
IN CONST EE_NEWLINE_TYPE Type,
OUT CHAR8 *Buffer,
OUT UINT8 *Size
)
{
UINT8 NewLineSize;
//
// give new line buffer,
// and will judge unicode or ascii
//
NewLineSize = 0;
//
// not legal new line type
//
if ((Type != NewLineTypeLineFeed) && (Type != NewLineTypeCarriageReturn) && (Type != NewLineTypeCarriageReturnLineFeed) && (Type != NewLineTypeLineFeedCarriageReturn)) {
*Size = 0;
return;
}
//
// use_cr: give 0x0d
//
if (Type == NewLineTypeCarriageReturn) {
if (MainEditor.FileBuffer->FileType == FileTypeUnicode) {
Buffer[0] = 0x0d;
Buffer[1] = 0;
NewLineSize = 2;
} else {
Buffer[0] = 0x0d;
NewLineSize = 1;
}
*Size = NewLineSize;
return;
}
//
// use_lf: give 0x0a
//
if (Type == NewLineTypeLineFeed) {
if (MainEditor.FileBuffer->FileType == FileTypeUnicode) {
Buffer[0] = 0x0a;
Buffer[1] = 0;
NewLineSize = 2;
} else {
Buffer[0] = 0x0a;
NewLineSize = 1;
}
*Size = NewLineSize;
return;
}
//
// use_crlf: give 0x0d 0x0a
//
if (Type == NewLineTypeCarriageReturnLineFeed) {
if (MainEditor.FileBuffer->FileType == FileTypeUnicode) {
Buffer[0] = 0x0d;
Buffer[1] = 0;
Buffer[2] = 0x0a;
Buffer[3] = 0;
NewLineSize = 4;
} else {
Buffer[0] = 0x0d;
Buffer[1] = 0x0a;
NewLineSize = 2;
}
*Size = NewLineSize;
return;
}
//
// use_lfcr: give 0x0a 0x0d
//
if (Type == NewLineTypeLineFeedCarriageReturn) {
if (MainEditor.FileBuffer->FileType == FileTypeUnicode) {
Buffer[0] = 0x0a;
Buffer[1] = 0;
Buffer[2] = 0x0d;
Buffer[3] = 0;
NewLineSize = 4;
} else {
Buffer[0] = 0x0a;
Buffer[1] = 0x0d;
NewLineSize = 2;
}
*Size = NewLineSize;
return;
}
}
/**
Change a Unicode string to an ASCII string.
@param[in] UStr The Unicode string.
@param[in] Length The maximum size of AStr.
@param[out] AStr ASCII string to pass out.
@return The actuall length.
**/
UINTN
UnicodeToAscii (
IN CONST CHAR16 *UStr,
IN CONST UINTN Length,
OUT CHAR8 *AStr
)
{
UINTN Index;
//
// just buffer copy, not character copy
//
for (Index = 0; Index < Length; Index++) {
*AStr++ = (CHAR8)*UStr++;
}
return Index;
}
/**
Save lines in FileBuffer to disk
@param[in] FileName The file name for writing.
@retval EFI_SUCCESS Data was written.
@retval EFI_LOAD_ERROR
@retval EFI_OUT_OF_RESOURCES There were not enough resources to write the file.
**/
EFI_STATUS
FileBufferSave (
IN CONST CHAR16 *FileName
)
{
SHELL_FILE_HANDLE FileHandle;
LIST_ENTRY *Link;
EFI_EDITOR_LINE *Line;
CHAR16 *Str;
EFI_STATUS Status;
UINTN Length;
UINTN NumLines;
CHAR8 NewLineBuffer[4];
UINT8 NewLineSize;
EFI_FILE_INFO *Info;
UINT64 Attribute;
EE_NEWLINE_TYPE Type;
UINTN TotalSize;
//
// 2M
//
CHAR8 *Cache;
UINTN LeftSize;
UINTN Size;
CHAR8 *Ptr;
Length = 0;
//
// 2M
//
TotalSize = 0x200000;
Attribute = 0;
//
// if is the old file
//
if ((FileBuffer.FileName != NULL) && (StrCmp (FileName, FileBuffer.FileName) == 0)) {
//
// file has not been modified
//
if (!FileBuffer.FileModified) {
return EFI_SUCCESS;
}
//
// if file is read-only, set error
//
if (FileBuffer.ReadOnly) {
StatusBarSetStatusString (L"Read Only File Can Not Be Saved");
return EFI_SUCCESS;
}
}
Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0);
if (!EFI_ERROR (Status)) {
Info = ShellGetFileInfo (FileHandle);
if ((Info != NULL) && Info->Attribute & EFI_FILE_DIRECTORY) {
StatusBarSetStatusString (L"Directory Can Not Be Saved");
ShellCloseFile (&FileHandle);
FreePool (Info);
return EFI_LOAD_ERROR;
}
if (Info != NULL) {
Attribute = Info->Attribute & ~EFI_FILE_READ_ONLY;
FreePool (Info);
}
//
// if file exits, so delete it
//
Status = ShellDeleteFile (&FileHandle);
if (EFI_ERROR (Status) || (Status == EFI_WARN_DELETE_FAILURE)) {
StatusBarSetStatusString (L"Write File Failed");
return EFI_LOAD_ERROR;
}
}
Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, Attribute);
if (EFI_ERROR (Status)) {
StatusBarSetStatusString (L"Create File Failed");
return EFI_LOAD_ERROR;
}
//
// if file is Unicode file, write Unicode header to it.
//
if (FileBuffer.FileType == FileTypeUnicode) {
Length = 2;
Status = ShellWriteFile (FileHandle, &Length, (VOID *)&gUnicodeFileTag);
if (EFI_ERROR (Status)) {
ShellDeleteFile (&FileHandle);
return EFI_LOAD_ERROR;
}
}
Cache = AllocateZeroPool (TotalSize);
if (Cache == NULL) {
ShellDeleteFile (&FileHandle);
return EFI_OUT_OF_RESOURCES;
}
//
// write all the lines back to disk
//
NumLines = 0;
Type = NewLineTypeCarriageReturnLineFeed;
Ptr = Cache;
LeftSize = TotalSize;
for (Link = FileBuffer.ListHead->ForwardLink; Link != FileBuffer.ListHead; Link = Link->ForwardLink) {
Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
if (Line->Type != NewLineTypeDefault) {
Type = Line->Type;
}
//
// newline character is at most 4 bytes ( two Unicode characters )
//
Length = 4;
if ((Line->Buffer != NULL) && (Line->Size != 0)) {
if (FileBuffer.FileType == FileTypeAscii) {
Length += Line->Size;
} else {
Length += (Line->Size * 2);
}
//
// end if FileTypeAscii
//
}
//
// no cache room left, so write cache to disk
//
if (LeftSize < Length) {
Size = TotalSize - LeftSize;
Status = ShellWriteFile (FileHandle, &Size, Cache);
if (EFI_ERROR (Status)) {
ShellDeleteFile (&FileHandle);
FreePool (Cache);
return EFI_LOAD_ERROR;
}
Ptr = Cache;
LeftSize = TotalSize;
}
if ((Line->Buffer != NULL) && (Line->Size != 0)) {
if (FileBuffer.FileType == FileTypeAscii) {
UnicodeToAscii (Line->Buffer, Line->Size, Ptr);
Length = Line->Size;
} else {
Length = (Line->Size * 2);
CopyMem (Ptr, (CHAR8 *)Line->Buffer, Length);
}
//
// end if FileTypeAscii
//
Ptr += Length;
LeftSize -= Length;
}
//
// end of if Line -> Buffer != NULL && Line -> Size != 0
//
// if not the last line , write return buffer to disk
//
if (Link->ForwardLink != FileBuffer.ListHead) {
GetNewLine (Type, NewLineBuffer, &NewLineSize);
CopyMem (Ptr, (CHAR8 *)NewLineBuffer, NewLineSize);
Ptr += NewLineSize;
LeftSize -= NewLineSize;
}
NumLines++;
}
if (TotalSize != LeftSize) {
Size = TotalSize - LeftSize;
Status = ShellWriteFile (FileHandle, &Size, Cache);
if (EFI_ERROR (Status)) {
ShellDeleteFile (&FileHandle);
FreePool (Cache);
return EFI_LOAD_ERROR;
}
}
FreePool (Cache);
ShellCloseFile (&FileHandle);
FileBuffer.FileModified = FALSE;
//
// set status string
//
Str = CatSPrint (NULL, L"%d Lines Written", NumLines);
if (Str == NULL) {
return EFI_OUT_OF_RESOURCES;
}
StatusBarSetStatusString (Str);
SHELL_FREE_NON_NULL (Str);
//
// now everything is ready , you can set the new file name to filebuffer
//
if ((FileName != NULL) && (FileBuffer.FileName != NULL) && (StrCmp (FileName, FileBuffer.FileName) != 0)) {
//
// not the same
//
FileBufferSetFileName (FileName);
if (FileBuffer.FileName == NULL) {
ShellDeleteFile (&FileHandle);
return EFI_OUT_OF_RESOURCES;
}
}
FileBuffer.ReadOnly = FALSE;
return EFI_SUCCESS;
}
/**
Scroll cursor to left 1 character position.
@retval EFI_SUCCESS The operation was successful.
**/
EFI_STATUS
FileBufferScrollLeft (
VOID
)
{
EFI_EDITOR_LINE *Line;
UINTN FRow;
UINTN FCol;
Line = FileBuffer.CurrentLine;
FRow = FileBuffer.FilePosition.Row;
FCol = FileBuffer.FilePosition.Column;
//
// if already at start of this line, so move to the end of previous line
//
if (FCol <= 1) {
//
// has previous line
//
if (Line->Link.BackLink != FileBuffer.ListHead) {
FRow--;
Line = CR (Line->Link.BackLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
FCol = Line->Size + 1;
} else {
return EFI_SUCCESS;
}
} else {
//
// if not at start of this line, just move to previous column
//
FCol--;
}
FileBufferMovePosition (FRow, FCol);
return EFI_SUCCESS;
}
/**
Delete a char in line
@param[in, out] Line The line to delete in.
@param[in] Pos Position to delete the char at ( start from 0 ).
**/
VOID
LineDeleteAt (
IN OUT EFI_EDITOR_LINE *Line,
IN UINTN Pos
)
{
UINTN Index;
//
// move the latter characters front
//
for (Index = Pos - 1; Index < Line->Size; Index++) {
Line->Buffer[Index] = Line->Buffer[Index + 1];
}
Line->Size--;
}
/**
Concatenate Src into Dest.
@param[in, out] Dest Destination string
@param[in] Src Src String.
**/
VOID
LineCat (
IN OUT EFI_EDITOR_LINE *Dest,
IN EFI_EDITOR_LINE *Src
)
{
CHAR16 *Str;
UINTN Size;
Size = Dest->Size;
Dest->Buffer[Size] = 0;
//
// concatenate the two strings
//
Str = CatSPrint (NULL, L"%s%s", Dest->Buffer, Src->Buffer);
if (Str == NULL) {
Dest->Buffer = NULL;
return;
}
Dest->Size = Size + Src->Size;
Dest->TotalSize = Dest->Size;
FreePool (Dest->Buffer);
FreePool (Src->Buffer);
//
// put str to dest->buffer
//
Dest->Buffer = Str;
}
/**
Delete the previous character.
@retval EFI_SUCCESS The delete was successful.
@retval EFI_OUT_OF_RESOURCES A memory allocation failed.
**/
EFI_STATUS
FileBufferDoBackspace (
VOID
)
{
EFI_EDITOR_LINE *Line;
EFI_EDITOR_LINE *End;
LIST_ENTRY *Link;
UINTN FileColumn;
FileColumn = FileBuffer.FilePosition.Column;
Line = FileBuffer.CurrentLine;
//
// the first column
//
if (FileColumn == 1) {
//
// the first row
//
if (FileBuffer.FilePosition.Row == 1) {
return EFI_SUCCESS;
}
FileBufferScrollLeft ();
Line = FileBuffer.CurrentLine;
Link = Line->Link.ForwardLink;
End = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
//
// concatenate this line with previous line
//
LineCat (Line, End);
if (Line->Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// remove End from line list
//
RemoveEntryList (&End->Link);
FreePool (End);
FileBuffer.NumLines--;
FileBufferNeedRefresh = TRUE;
FileBufferOnlyLineNeedRefresh = FALSE;
} else {
//
// just delete the previous character
//
LineDeleteAt (Line, FileColumn - 1);
FileBufferScrollLeft ();
FileBufferOnlyLineNeedRefresh = TRUE;
}
if (!FileBuffer.FileModified) {
FileBuffer.FileModified = TRUE;
}
return EFI_SUCCESS;
}
/**
Add a return into line at current position.
@retval EFI_SUCCESS The insetrion of the character was successful.
@retval EFI_OUT_OF_RESOURCES A memory allocation failed.
**/
EFI_STATUS
FileBufferDoReturn (
VOID
)
{
EFI_EDITOR_LINE *Line;
EFI_EDITOR_LINE *NewLine;
UINTN FileColumn;
UINTN Index;
CHAR16 *Buffer;
UINTN Row;
UINTN Col;
FileBufferNeedRefresh = TRUE;
FileBufferOnlyLineNeedRefresh = FALSE;
Line = FileBuffer.CurrentLine;
FileColumn = FileBuffer.FilePosition.Column;
NewLine = AllocateZeroPool (sizeof (EFI_EDITOR_LINE));
if (NewLine == NULL) {
return EFI_OUT_OF_RESOURCES;
}
NewLine->Signature = LINE_LIST_SIGNATURE;
NewLine->Size = Line->Size - FileColumn + 1;
NewLine->TotalSize = NewLine->Size;
NewLine->Buffer = CatSPrint (NULL, L"\0");
if (NewLine->Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
NewLine->Type = NewLineTypeDefault;
if (NewLine->Size > 0) {
//
// UNICODE + CHAR_NULL
//
Buffer = AllocateZeroPool (2 * (NewLine->Size + 1));
if (Buffer == NULL) {
FreePool (NewLine->Buffer);
FreePool (NewLine);
return EFI_OUT_OF_RESOURCES;
}
FreePool (NewLine->Buffer);
NewLine->Buffer = Buffer;
for (Index = 0; Index < NewLine->Size; Index++) {
NewLine->Buffer[Index] = Line->Buffer[Index + FileColumn - 1];
}
NewLine->Buffer[NewLine->Size] = CHAR_NULL;
Line->Buffer[FileColumn - 1] = CHAR_NULL;
Line->Size = FileColumn - 1;
}
//
// increase NumLines
//
FileBuffer.NumLines++;
//
// insert it into the correct position of line list
//
NewLine->Link.BackLink = &(Line->Link);
NewLine->Link.ForwardLink = Line->Link.ForwardLink;
Line->Link.ForwardLink->BackLink = &(NewLine->Link);
Line->Link.ForwardLink = &(NewLine->Link);
//
// move cursor to the start of next line
//
Row = FileBuffer.FilePosition.Row + 1;
Col = 1;
FileBufferMovePosition (Row, Col);
//
// set file is modified
//
if (!FileBuffer.FileModified) {
FileBuffer.FileModified = TRUE;
}
return EFI_SUCCESS;
}
/**
Delete current character from current line. This is the effect caused
by the 'del' key.
@retval EFI_SUCCESS
**/
EFI_STATUS
FileBufferDoDelete (
VOID
)
{
EFI_EDITOR_LINE *Line;
EFI_EDITOR_LINE *Next;
LIST_ENTRY *Link;
UINTN FileColumn;
Line = FileBuffer.CurrentLine;
FileColumn = FileBuffer.FilePosition.Column;
//
// the last column
//
if (FileColumn >= Line->Size + 1) {
//
// the last line
//
if (Line->Link.ForwardLink == FileBuffer.ListHead) {
return EFI_SUCCESS;
}
//
// since last character,
// so will add the next line to this line
//
Link = Line->Link.ForwardLink;
Next = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
LineCat (Line, Next);
if (Line->Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
RemoveEntryList (&Next->Link);
FreePool (Next);
FileBuffer.NumLines--;
FileBufferNeedRefresh = TRUE;
FileBufferOnlyLineNeedRefresh = FALSE;
} else {
//
// just delete current character
//
LineDeleteAt (Line, FileColumn);
FileBufferOnlyLineNeedRefresh = TRUE;
}
if (!FileBuffer.FileModified) {
FileBuffer.FileModified = TRUE;
}
return EFI_SUCCESS;
}
/**
Scroll cursor to right 1 character.
@retval EFI_SUCCESS The operation was successful.
**/
EFI_STATUS
FileBufferScrollRight (
VOID
)
{
EFI_EDITOR_LINE *Line;
UINTN FRow;
UINTN FCol;
Line = FileBuffer.CurrentLine;
if (Line->Buffer == NULL) {
return EFI_SUCCESS;
}
FRow = FileBuffer.FilePosition.Row;
FCol = FileBuffer.FilePosition.Column;
//
// if already at end of this line, scroll it to the start of next line
//
if (FCol > Line->Size) {
//
// has next line
//
if (Line->Link.ForwardLink != FileBuffer.ListHead) {
FRow++;
FCol = 1;
} else {
return EFI_SUCCESS;
}
} else {
//
// if not at end of this line, just move to next column
//
FCol++;
}
FileBufferMovePosition (FRow, FCol);
return EFI_SUCCESS;
}
/**
Insert a char into line
@param[in] Line The line to insert into.
@param[in] Char The char to insert.
@param[in] Pos The position to insert the char at ( start from 0 ).
@param[in] StrSize The current string size ( include CHAR_NULL ),unit is Unicode character.
@return The new string size ( include CHAR_NULL ) ( unit is Unicode character ).
**/
UINTN
LineStrInsert (
IN EFI_EDITOR_LINE *Line,
IN CHAR16 Char,
IN UINTN Pos,
IN UINTN StrSize
)
{
UINTN Index;
CHAR16 *TempStringPtr;
CHAR16 *Str;
Index = (StrSize) * 2;
Str = Line->Buffer;
//
// do not have free space
//
if (Line->TotalSize <= Line->Size) {
Str = ReallocatePool (Index, Index + 16, Str);
if (Str == NULL) {
return 0;
}
Line->TotalSize += 8;
}
//
// move the later part of the string one character right
//
TempStringPtr = Str;
for (Index = StrSize; Index > Pos; Index--) {
TempStringPtr[Index] = TempStringPtr[Index - 1];
}
//
// insert char into it.
//
TempStringPtr[Index] = Char;
Line->Buffer = Str;
Line->Size++;
return StrSize + 1;
}
/**
Add a character to the current line.
@param[in] Char The Character to input.
@retval EFI_SUCCESS The input was succesful.
**/
EFI_STATUS
FileBufferAddChar (
IN CHAR16 Char
)
{
EFI_EDITOR_LINE *Line;
UINTN FilePos;
Line = FileBuffer.CurrentLine;
//
// only needs to refresh current line
//
FileBufferOnlyLineNeedRefresh = TRUE;
//
// when is insert mode, or cursor is at end of this line,
// so insert this character
// or replace the character.
//
FilePos = FileBuffer.FilePosition.Column - 1;
if (FileBuffer.ModeInsert || (FilePos + 1 > Line->Size)) {
LineStrInsert (Line, Char, FilePos, Line->Size + 1);
} else {
Line->Buffer[FilePos] = Char;
}
//
// move cursor to right
//
FileBufferScrollRight ();
if (!FileBuffer.FileModified) {
FileBuffer.FileModified = TRUE;
}
return EFI_SUCCESS;
}
/**
Handles inputs from characters (ASCII key + Backspace + return)
@param[in] Char The input character.
@retval EFI_SUCCESS The operation was successful.
@retval EFI_LOAD_ERROR There was an error.
@retval EFI_OUT_OF_RESOURCES A memory allocation failed.
**/
EFI_STATUS
FileBufferDoCharInput (
IN CONST CHAR16 Char
)
{
EFI_STATUS Status;
Status = EFI_SUCCESS;
switch (Char) {
case CHAR_NULL:
break;
case CHAR_BACKSPACE:
Status = FileBufferDoBackspace ();
break;
case CHAR_TAB:
//
// Tabs are ignored
//
break;
case CHAR_LINEFEED:
case CHAR_CARRIAGE_RETURN:
Status = FileBufferDoReturn ();
break;
default:
//
// DEAL WITH ASCII CHAR, filter out thing like ctrl+f
//
if ((Char > 127) || (Char < 32)) {
Status = StatusBarSetStatusString (L"Unknown Command");
} else {
Status = FileBufferAddChar (Char);
}
break;
}
return Status;
}
/**
Scroll cursor to the next line.
@retval EFI_SUCCESS The operation was successful.
**/
EFI_STATUS
FileBufferScrollDown (
VOID
)
{
EFI_EDITOR_LINE *Line;
UINTN FRow;
UINTN FCol;
Line = FileBuffer.CurrentLine;
if (Line->Buffer == NULL) {
return EFI_SUCCESS;
}
FRow = FileBuffer.FilePosition.Row;
FCol = FileBuffer.FilePosition.Column;
//
// has next line
//
if (Line->Link.ForwardLink != FileBuffer.ListHead) {
FRow++;
Line = CR (Line->Link.ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
//
// if the next line is not that long, so move to end of next line
//
if (FCol > Line->Size) {
FCol = Line->Size + 1;
}
} else {
return EFI_SUCCESS;
}
FileBufferMovePosition (FRow, FCol);
return EFI_SUCCESS;
}
/**
Scroll the cursor to previous line.
@retval EFI_SUCCESS The operation was successful.
**/
EFI_STATUS
FileBufferScrollUp (
VOID
)
{
EFI_EDITOR_LINE *Line;
UINTN FRow;
UINTN FCol;
Line = FileBuffer.CurrentLine;
FRow = FileBuffer.FilePosition.Row;
FCol = FileBuffer.FilePosition.Column;
//
// has previous line
//
if (Line->Link.BackLink != FileBuffer.ListHead) {
FRow--;
Line = CR (Line->Link.BackLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
//
// if previous line is not that long, so move to the end of previous line
//
if (FCol > Line->Size) {
FCol = Line->Size + 1;
}
} else {
return EFI_SUCCESS;
}
FileBufferMovePosition (FRow, FCol);
return EFI_SUCCESS;
}
/**
Scroll cursor to next page.
@retval EFI_SUCCESS The operation wa successful.
**/
EFI_STATUS
FileBufferPageDown (
VOID
)
{
EFI_EDITOR_LINE *Line;
UINTN FRow;
UINTN FCol;
UINTN Gap;
Line = FileBuffer.CurrentLine;
FRow = FileBuffer.FilePosition.Row;
FCol = FileBuffer.FilePosition.Column;
//
// has next page
//
if (FileBuffer.NumLines >= FRow + (MainEditor.ScreenSize.Row - 2)) {
Gap = (MainEditor.ScreenSize.Row - 2);
} else {
//
// MOVE CURSOR TO LAST LINE
//
Gap = FileBuffer.NumLines - FRow;
}
//
// get correct line
//
Line = MoveLine (Gap);
//
// if that line, is not that long, so move to the end of that line
//
if ((Line != NULL) && (FCol > Line->Size)) {
FCol = Line->Size + 1;
}
FRow += Gap;
FileBufferMovePosition (FRow, FCol);
return EFI_SUCCESS;
}
/**
Scroll cursor to previous screen.
@retval EFI_SUCCESS The operation was successful.
**/
EFI_STATUS
FileBufferPageUp (
VOID
)
{
EFI_EDITOR_LINE *Line;
UINTN FRow;
UINTN FCol;
UINTN Gap;
INTN Retreat;
Line = FileBuffer.CurrentLine;
FRow = FileBuffer.FilePosition.Row;
FCol = FileBuffer.FilePosition.Column;
//
// has previous page
//
if (FRow > (MainEditor.ScreenSize.Row - 2)) {
Gap = (MainEditor.ScreenSize.Row - 2);
} else {
//
// the first line of file will displayed on the first line of screen
//
Gap = FRow - 1;
}
Retreat = Gap;
Retreat = -Retreat;
//
// get correct line
//
Line = MoveLine (Retreat);
//
// if that line is not that long, so move to the end of that line
//
if ((Line != NULL) && (FCol > Line->Size)) {
FCol = Line->Size + 1;
}
FRow -= Gap;
FileBufferMovePosition (FRow, FCol);
return EFI_SUCCESS;
}
/**
Scroll cursor to end of the current line.
@retval EFI_SUCCESS The operation was successful.
**/
EFI_STATUS
FileBufferEnd (
VOID
)
{
EFI_EDITOR_LINE *Line;
UINTN FRow;
UINTN FCol;
Line = FileBuffer.CurrentLine;
FRow = FileBuffer.FilePosition.Row;
//
// goto the last column of the line
//
FCol = Line->Size + 1;
FileBufferMovePosition (FRow, FCol);
return EFI_SUCCESS;
}
/**
Dispatch input to different handler
@param[in] Key The input key. One of:
ASCII KEY
Backspace/Delete
Return
Direction key: up/down/left/right/pgup/pgdn
Home/End
INS
@retval EFI_SUCCESS The dispatch was done successfully.
@retval EFI_LOAD_ERROR The dispatch was not successful.
@retval EFI_OUT_OF_RESOURCES A memory allocation failed.
**/
EFI_STATUS
FileBufferHandleInput (
IN CONST EFI_INPUT_KEY *Key
)
{
EFI_STATUS Status;
Status = EFI_SUCCESS;
switch (Key->ScanCode) {
//
// ordinary key input
//
case SCAN_NULL:
if (!FileBuffer.ReadOnly) {
Status = FileBufferDoCharInput (Key->UnicodeChar);
} else {
Status = StatusBarSetStatusString (L"Read Only File Can Not Be Modified");
}
break;
//
// up arrow
//
case SCAN_UP:
Status = FileBufferScrollUp ();
break;
//
// down arrow
//
case SCAN_DOWN:
Status = FileBufferScrollDown ();
break;
//
// right arrow
//
case SCAN_RIGHT:
Status = FileBufferScrollRight ();
break;
//
// left arrow
//
case SCAN_LEFT:
Status = FileBufferScrollLeft ();
break;
//
// page up
//
case SCAN_PAGE_UP:
Status = FileBufferPageUp ();
break;
//
// page down
//
case SCAN_PAGE_DOWN:
Status = FileBufferPageDown ();
break;
//
// delete
//
case SCAN_DELETE:
if (!FileBuffer.ReadOnly) {
Status = FileBufferDoDelete ();
} else {
Status = StatusBarSetStatusString (L"Read Only File Can Not Be Modified");
}
break;
//
// home
//
case SCAN_HOME:
FileBufferMovePosition (FileBuffer.FilePosition.Row, 1);
Status = EFI_SUCCESS;
break;
//
// end
//
case SCAN_END:
Status = FileBufferEnd ();
break;
//
// insert
//
case SCAN_INSERT:
FileBuffer.ModeInsert = (BOOLEAN) !FileBuffer.ModeInsert;
Status = EFI_SUCCESS;
break;
default:
Status = StatusBarSetStatusString (L"Unknown Command");
break;
}
return Status;
}
/**
Check user specified FileRow is above current screen.
@param[in] FileRow The row of file position ( start from 1 ).
@retval TRUE It is above the current screen.
@retval FALSE It is not above the current screen.
**/
BOOLEAN
AboveCurrentScreen (
IN UINTN FileRow
)
{
//
// if is to the above of the screen
//
if (FileRow < FileBuffer.LowVisibleRange.Row) {
return TRUE;
}
return FALSE;
}
/**
Check user specified FileRow is under current screen.
@param[in] FileRow The row of file position ( start from 1 ).
@retval TRUE It is under the current screen.
@retval FALSE It is not under the current screen.
**/
BOOLEAN
UnderCurrentScreen (
IN UINTN FileRow
)
{
//
// if is to the under of the screen
//
if (FileRow > FileBuffer.LowVisibleRange.Row + (MainEditor.ScreenSize.Row - 2) - 1) {
return TRUE;
}
return FALSE;
}
/**
Check user specified FileCol is left to current screen.
@param[in] FileCol The column of file position ( start from 1 ).
@retval TRUE It is to the left.
@retval FALSE It is not to the left.
**/
BOOLEAN
LeftCurrentScreen (
IN UINTN FileCol
)
{
//
// if is to the left of the screen
//
if (FileCol < FileBuffer.LowVisibleRange.Column) {
return TRUE;
}
return FALSE;
}
/**
Check user specified FileCol is right to current screen.
@param[in] FileCol The column of file position ( start from 1 ).
@retval TRUE It is to the right.
@retval FALSE It is not to the right.
**/
BOOLEAN
RightCurrentScreen (
IN UINTN FileCol
)
{
//
// if is to the right of the screen
//
if (FileCol > FileBuffer.LowVisibleRange.Column + MainEditor.ScreenSize.Column - 1) {
return TRUE;
}
return FALSE;
}
/**
Advance/Retreat lines and set CurrentLine in FileBuffer to it
@param[in] Count The line number to advance/retreat
>0 : advance
<0: retreat
@retval NULL An error occurred.
@return The line after advance/retreat.
**/
EFI_EDITOR_LINE *
MoveCurrentLine (
IN INTN Count
)
{
EFI_EDITOR_LINE *Line;
UINTN AbsCount;
if (Count <= 0) {
AbsCount = (UINTN)ABS (Count);
Line = InternalEditorMiscLineRetreat (AbsCount, MainEditor.FileBuffer->CurrentLine, MainEditor.FileBuffer->ListHead);
} else {
Line = InternalEditorMiscLineAdvance ((UINTN)Count, MainEditor.FileBuffer->CurrentLine, MainEditor.FileBuffer->ListHead);
}
if (Line == NULL) {
return NULL;
}
MainEditor.FileBuffer->CurrentLine = Line;
return Line;
}
/**
According to cursor's file position, adjust screen display
@param[in] NewFilePosRow The row of file position ( start from 1 ).
@param[in] NewFilePosCol The column of file position ( start from 1 ).
**/
VOID
FileBufferMovePosition (
IN CONST UINTN NewFilePosRow,
IN CONST UINTN NewFilePosCol
)
{
INTN RowGap;
INTN ColGap;
UINTN Abs;
BOOLEAN Above;
BOOLEAN Under;
BOOLEAN Right;
BOOLEAN Left;
//
// CALCULATE gap between current file position and new file position
//
RowGap = NewFilePosRow - FileBuffer.FilePosition.Row;
ColGap = NewFilePosCol - FileBuffer.FilePosition.Column;
Under = UnderCurrentScreen (NewFilePosRow);
Above = AboveCurrentScreen (NewFilePosRow);
//
// if is below current screen
//
if (Under) {
//
// display row will be unchanged
//
FileBuffer.FilePosition.Row = NewFilePosRow;
} else {
if (Above) {
//
// has enough above line, so display row unchanged
// not has enough above lines, so the first line is at the
// first display line
//
if (NewFilePosRow < (FileBuffer.DisplayPosition.Row - 1)) {
FileBuffer.DisplayPosition.Row = NewFilePosRow + 1;
}
FileBuffer.FilePosition.Row = NewFilePosRow;
} else {
//
// in current screen
//
FileBuffer.FilePosition.Row = NewFilePosRow;
if (RowGap < 0) {
Abs = (UINTN)ABS (RowGap);
FileBuffer.DisplayPosition.Row -= Abs;
} else {
FileBuffer.DisplayPosition.Row += RowGap;
}
}
}
FileBuffer.LowVisibleRange.Row = FileBuffer.FilePosition.Row - (FileBuffer.DisplayPosition.Row - 2);
Right = RightCurrentScreen (NewFilePosCol);
Left = LeftCurrentScreen (NewFilePosCol);
//
// if right to current screen
//
if (Right) {
//
// display column will be changed to end
//
FileBuffer.DisplayPosition.Column = MainEditor.ScreenSize.Column;
FileBuffer.FilePosition.Column = NewFilePosCol;
} else {
if (Left) {
//
// has enough left characters , so display row unchanged
// not has enough left characters,
// so the first character is at the first display column
//
if (NewFilePosCol < (FileBuffer.DisplayPosition.Column)) {
FileBuffer.DisplayPosition.Column = NewFilePosCol;
}
FileBuffer.FilePosition.Column = NewFilePosCol;
} else {
//
// in current screen
//
FileBuffer.FilePosition.Column = NewFilePosCol;
if (ColGap < 0) {
Abs = (UINTN)(-ColGap);
FileBuffer.DisplayPosition.Column -= Abs;
} else {
FileBuffer.DisplayPosition.Column += ColGap;
}
}
}
FileBuffer.LowVisibleRange.Column = FileBuffer.FilePosition.Column - (FileBuffer.DisplayPosition.Column - 1);
//
// let CurrentLine point to correct line;
//
FileBuffer.CurrentLine = MoveCurrentLine (RowGap);
}
/**
Cut current line out and return a pointer to it.
@param[out] CutLine Upon a successful return pointer to the pointer to
the allocated cut line.
@retval EFI_SUCCESS The cut was successful.
@retval EFI_NOT_FOUND There was no selection to cut.
@retval EFI_OUT_OF_RESOURCES A memory allocation failed.
**/
EFI_STATUS
FileBufferCutLine (
OUT EFI_EDITOR_LINE **CutLine
)
{
EFI_EDITOR_LINE *Line;
EFI_EDITOR_LINE *NewLine;
UINTN Row;
UINTN Col;
*CutLine = NULL;
if (FileBuffer.ReadOnly) {
StatusBarSetStatusString (L"Read Only File Can Not Be Modified");
return EFI_SUCCESS;
}
Line = FileBuffer.CurrentLine;
//
// if is the last dummy line, SO CAN not cut
//
if ((StrCmp (Line->Buffer, L"\0") == 0) && (Line->Link.ForwardLink == FileBuffer.ListHead)
//
// last line
//
)
{
//
// LAST LINE AND NOTHING ON THIS LINE, SO CUT NOTHING
//
StatusBarSetStatusString (L"Nothing to Cut");
return EFI_NOT_FOUND;
}
//
// if is the last line, so create a dummy line
//
if (Line->Link.ForwardLink == FileBuffer.ListHead) {
//
// last line
// create a new line
//
NewLine = FileBufferCreateLine ();
if (NewLine == NULL) {
return EFI_OUT_OF_RESOURCES;
}
}
FileBuffer.NumLines--;
Row = FileBuffer.FilePosition.Row;
Col = 1;
//
// move home
//
FileBuffer.CurrentLine = CR (
FileBuffer.CurrentLine->Link.ForwardLink,
EFI_EDITOR_LINE,
Link,
LINE_LIST_SIGNATURE
);
RemoveEntryList (&Line->Link);
FileBuffer.Lines = CR (FileBuffer.ListHead->ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
FileBufferMovePosition (Row, Col);
FileBuffer.FileModified = TRUE;
FileBufferNeedRefresh = TRUE;
FileBufferOnlyLineNeedRefresh = FALSE;
*CutLine = Line;
return EFI_SUCCESS;
}
/**
Paste a line into line list.
@retval EFI_SUCCESS The paste was successful.
@retval EFI_OUT_OF_RESOURCES A memory allocation failed.
**/
EFI_STATUS
FileBufferPasteLine (
VOID
)
{
EFI_EDITOR_LINE *Line;
EFI_EDITOR_LINE *NewLine;
UINTN Row;
UINTN Col;
//
// if nothing is on clip board
// then do nothing
//
if (MainEditor.CutLine == NULL) {
return EFI_SUCCESS;
}
//
// read only file can not be pasted on
//
if (FileBuffer.ReadOnly) {
StatusBarSetStatusString (L"Read Only File Can Not Be Modified");
return EFI_SUCCESS;
}
NewLine = LineDup (MainEditor.CutLine);
if (NewLine == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// insert it above current line
//
Line = FileBuffer.CurrentLine;
NewLine->Link.BackLink = Line->Link.BackLink;
NewLine->Link.ForwardLink = &Line->Link;
Line->Link.BackLink->ForwardLink = &NewLine->Link;
Line->Link.BackLink = &NewLine->Link;
FileBuffer.NumLines++;
FileBuffer.CurrentLine = NewLine;
FileBuffer.Lines = CR (FileBuffer.ListHead->ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
Col = 1;
//
// move home
//
Row = FileBuffer.FilePosition.Row;
FileBufferMovePosition (Row, Col);
//
// after paste, set some value so that refresh knows to do something
//
FileBuffer.FileModified = TRUE;
FileBufferNeedRefresh = TRUE;
FileBufferOnlyLineNeedRefresh = FALSE;
return EFI_SUCCESS;
}
/**
Search string from current position on in file
@param[in] Str The search string.
@param[in] Offset The offset from current position.
@retval EFI_SUCCESS The operation was successful.
@retval EFI_NOT_FOUND The string Str was not found.
**/
EFI_STATUS
FileBufferSearch (
IN CONST CHAR16 *Str,
IN CONST UINTN Offset
)
{
CHAR16 *Current;
UINTN Position;
UINTN Row;
UINTN Column;
EFI_EDITOR_LINE *Line;
CHAR16 *CharPos;
LIST_ENTRY *Link;
BOOLEAN Found;
Column = 0;
Position = 0;
//
// search if in current line
//
Current = FileBuffer.CurrentLine->Buffer + FileBuffer.FilePosition.Column - 1 + Offset;
if (Current >= (FileBuffer.CurrentLine->Buffer + FileBuffer.CurrentLine->Size)) {
//
// the end
//
Current = FileBuffer.CurrentLine->Buffer + FileBuffer.CurrentLine->Size;
}
Found = FALSE;
CharPos = StrStr (Current, Str);
if (CharPos != NULL) {
Position = CharPos - Current + 1;
Found = TRUE;
}
//
// found
//
if (Found) {
Column = (Position - 1) + FileBuffer.FilePosition.Column + Offset;
Row = FileBuffer.FilePosition.Row;
} else {
//
// not found so find through next lines
//
Link = FileBuffer.CurrentLine->Link.ForwardLink;
Row = FileBuffer.FilePosition.Row + 1;
while (Link != FileBuffer.ListHead) {
Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
// Position = StrStr (Line->Buffer, Str);
CharPos = StrStr (Line->Buffer, Str);
if (CharPos != NULL) {
Position = CharPos - Line->Buffer + 1;
Found = TRUE;
}
if (Found) {
//
// found
//
Column = Position;
break;
}
Row++;
Link = Link->ForwardLink;
}
if (Link == FileBuffer.ListHead) {
Found = FALSE;
} else {
Found = TRUE;
}
}
if (!Found) {
return EFI_NOT_FOUND;
}
FileBufferMovePosition (Row, Column);
//
// call refresh to fresh edit area,
// because the outer may loop to find multiply occurrence of this string
//
FileBufferRefresh ();
return EFI_SUCCESS;
}
/**
Replace SearchLen characters from current position on with Replace.
This will modify the current buffer at the current position.
@param[in] Replace The string to replace.
@param[in] SearchLen Search string's length.
@retval EFI_SUCCESS The operation was successful.
@retval EFI_OUT_OF_RESOURCES A memory allocation failed.
**/
EFI_STATUS
FileBufferReplace (
IN CONST CHAR16 *Replace,
IN CONST UINTN SearchLen
)
{
UINTN ReplaceLen;
UINTN Index;
CHAR16 *Buffer;
UINTN NewSize;
UINTN OldSize;
UINTN Gap;
ReplaceLen = StrLen (Replace);
OldSize = FileBuffer.CurrentLine->Size + 1;
//
// include CHAR_NULL
//
NewSize = OldSize + (ReplaceLen - SearchLen);
if (ReplaceLen > SearchLen) {
//
// do not have the enough space
//
if (FileBuffer.CurrentLine->TotalSize + 1 <= NewSize) {
FileBuffer.CurrentLine->Buffer = ReallocatePool (
2 * OldSize,
2 * NewSize,
FileBuffer.CurrentLine->Buffer
);
FileBuffer.CurrentLine->TotalSize = NewSize - 1;
}
if (FileBuffer.CurrentLine->Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// the end CHAR_NULL character;
//
Buffer = FileBuffer.CurrentLine->Buffer + (NewSize - 1);
Gap = ReplaceLen - SearchLen;
//
// keep the latter part
//
for (Index = 0; Index < (FileBuffer.CurrentLine->Size - FileBuffer.FilePosition.Column - SearchLen + 2); Index++) {
*Buffer = *(Buffer - Gap);
Buffer--;
}
//
// set replace into it
//
Buffer = FileBuffer.CurrentLine->Buffer + FileBuffer.FilePosition.Column - 1;
for (Index = 0; Index < ReplaceLen; Index++) {
Buffer[Index] = Replace[Index];
}
}
if (ReplaceLen < SearchLen) {
Buffer = FileBuffer.CurrentLine->Buffer + FileBuffer.FilePosition.Column - 1;
for (Index = 0; Index < ReplaceLen; Index++) {
Buffer[Index] = Replace[Index];
}
Buffer += ReplaceLen;
Gap = SearchLen - ReplaceLen;
//
// set replace into it
//
for (Index = 0; Index < (FileBuffer.CurrentLine->Size - FileBuffer.FilePosition.Column - ReplaceLen + 2); Index++) {
*Buffer = *(Buffer + Gap);
Buffer++;
}
}
if (ReplaceLen == SearchLen) {
Buffer = FileBuffer.CurrentLine->Buffer + FileBuffer.FilePosition.Column - 1;
for (Index = 0; Index < ReplaceLen; Index++) {
Buffer[Index] = Replace[Index];
}
}
FileBuffer.CurrentLine->Size += (ReplaceLen - SearchLen);
FileBufferOnlyLineNeedRefresh = TRUE;
FileBuffer.FileModified = TRUE;
MainTitleBarRefresh (MainEditor.FileBuffer->FileName, MainEditor.FileBuffer->FileType, MainEditor.FileBuffer->ReadOnly, MainEditor.FileBuffer->FileModified, MainEditor.ScreenSize.Column, MainEditor.ScreenSize.Row, 0, 0);
FileBufferRestorePosition ();
FileBufferRefresh ();
return EFI_SUCCESS;
}
/**
Move the mouse cursor position.
@param[in] TextX The new x-coordinate.
@param[in] TextY The new y-coordinate.
**/
VOID
FileBufferAdjustMousePosition (
IN CONST INT32 TextX,
IN CONST INT32 TextY
)
{
UINTN CoordinateX;
UINTN CoordinateY;
UINTN AbsX;
UINTN AbsY;
//
// TextX and TextY is mouse movement data returned by mouse driver
// This function will change it to MousePosition
//
//
// get absolute value
//
AbsX = ABS (TextX);
AbsY = ABS (TextY);
CoordinateX = FileBuffer.MousePosition.Column;
CoordinateY = FileBuffer.MousePosition.Row;
if (TextX >= 0) {
CoordinateX += TextX;
} else {
if (CoordinateX >= AbsX) {
CoordinateX -= AbsX;
} else {
CoordinateX = 0;
}
}
if (TextY >= 0) {
CoordinateY += TextY;
} else {
if (CoordinateY >= AbsY) {
CoordinateY -= AbsY;
} else {
CoordinateY = 0;
}
}
//
// check whether new mouse column position is beyond screen
// if not, adjust it
//
if ((CoordinateX >= 1) && (CoordinateX <= MainEditor.ScreenSize.Column)) {
FileBuffer.MousePosition.Column = CoordinateX;
} else if (CoordinateX < 1) {
FileBuffer.MousePosition.Column = 1;
} else if (CoordinateX > MainEditor.ScreenSize.Column) {
FileBuffer.MousePosition.Column = MainEditor.ScreenSize.Column;
}
//
// check whether new mouse row position is beyond screen
// if not, adjust it
//
if ((CoordinateY >= 2) && (CoordinateY <= (MainEditor.ScreenSize.Row - 1))) {
FileBuffer.MousePosition.Row = CoordinateY;
} else if (CoordinateY < 2) {
FileBuffer.MousePosition.Row = 2;
} else if (CoordinateY > (MainEditor.ScreenSize.Row - 1)) {
FileBuffer.MousePosition.Row = (MainEditor.ScreenSize.Row - 1);
}
}
/**
Search and replace operation.
@param[in] SearchStr The string to search for.
@param[in] ReplaceStr The string to replace with.
@param[in] Offset The column to start at.
**/
EFI_STATUS
FileBufferReplaceAll (
IN CHAR16 *SearchStr,
IN CHAR16 *ReplaceStr,
IN UINTN Offset
)
{
CHAR16 *Buffer;
UINTN Position;
UINTN Column;
UINTN ReplaceLen;
UINTN SearchLen;
UINTN Index;
UINTN NewSize;
UINTN OldSize;
UINTN Gap;
EFI_EDITOR_LINE *Line;
LIST_ENTRY *Link;
CHAR16 *CharPos;
SearchLen = StrLen (SearchStr);
ReplaceLen = StrLen (ReplaceStr);
Column = FileBuffer.FilePosition.Column + Offset - 1;
if (Column > FileBuffer.CurrentLine->Size) {
Column = FileBuffer.CurrentLine->Size;
}
Link = &(FileBuffer.CurrentLine->Link);
while (Link != FileBuffer.ListHead) {
Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
CharPos = StrStr (Line->Buffer + Column, SearchStr);
if (CharPos != NULL) {
Position = CharPos - Line->Buffer;// + Column;
//
// found
//
if (ReplaceLen > SearchLen) {
OldSize = Line->Size + 1;
//
// include CHAR_NULL
//
NewSize = OldSize + (ReplaceLen - SearchLen);
//
// do not have the enough space
//
if (Line->TotalSize + 1 <= NewSize) {
Line->Buffer = ReallocatePool (
2 * OldSize,
2 * NewSize,
Line->Buffer
);
Line->TotalSize = NewSize - 1;
}
if (Line->Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// the end CHAR_NULL character;
//
Buffer = Line->Buffer + (NewSize - 1);
Gap = ReplaceLen - SearchLen;
//
// keep the latter part
//
for (Index = 0; Index < (Line->Size - Position - SearchLen + 1); Index++) {
*Buffer = *(Buffer - Gap);
Buffer--;
}
} else if (ReplaceLen < SearchLen) {
Buffer = Line->Buffer + Position + ReplaceLen;
Gap = SearchLen - ReplaceLen;
for (Index = 0; Index < (Line->Size - Position - ReplaceLen + 1); Index++) {
*Buffer = *(Buffer + Gap);
Buffer++;
}
} else {
ASSERT (ReplaceLen == SearchLen);
}
//
// set replace into it
//
Buffer = Line->Buffer + Position;
for (Index = 0; Index < ReplaceLen; Index++) {
Buffer[Index] = ReplaceStr[Index];
}
Line->Size += (ReplaceLen - SearchLen);
Column += ReplaceLen;
} else {
//
// not found
//
Column = 0;
Link = Link->ForwardLink;
}
}
//
// call refresh to fresh edit area
//
FileBuffer.FileModified = TRUE;
FileBufferNeedRefresh = TRUE;
FileBufferRefresh ();
return EFI_SUCCESS;
}
/**
Set the modified state to TRUE.
**/
VOID
FileBufferSetModified (
VOID
)
{
FileBuffer.FileModified = TRUE;
}