mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-27 16:58:09 +01:00
7c0aa811ec
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
3342 lines
75 KiB
C
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;
|
|
}
|
|
|