CloverBootloader/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/FileBuffer.c
2019-09-03 12:58:42 +03:00

3342 lines
75 KiB
C

/** @file
Implements filebuffer interface functions.
Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved. <BR>
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 "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 occured.
@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 < 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;
UINTN LineSize2;
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;
LineSize2 = 0;
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 (AsciiBuffer[LineSize] == CHAR_TAB) {
LineSize2++;
}
} 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;
} else if (UnicodeBuffer[LineSize] == CHAR_TAB) {
LineSize2++;
}
}
//
// 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;
LineSize += LineSize2;
//
// 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) {
if (AsciiBuffer[LoopVar1] == CHAR_TAB) {
Line->Buffer[LoopVar2++] = L' ';
Line->Buffer[LoopVar2] = L' ';
} else {
Line->Buffer[LoopVar2] = (CHAR16) AsciiBuffer[LoopVar1];
}
} else {
if (UnicodeBuffer[LoopVar1] == CHAR_TAB) {
Line->Buffer[LoopVar2++] = L' ';
Line->Buffer[LoopVar2] = L' ';
} 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 Wrote", 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 occured.
@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;
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;
}