/** @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; | |
VOID *Buffer; | |
CHAR16 *UnicodeBuffer; | |
UINT8 *AsciiBuffer; | |
UINTN FileSize; | |
SHELL_FILE_HANDLE FileHandle; | |
BOOLEAN CreateFile; | |
EFI_STATUS Status; | |
UINTN LineSizeBackup; | |
EFI_FILE_INFO *Info; | |
Line = NULL; | |
LoopVar1 = 0; | |
FileSize = 0; | |
UnicodeBuffer = NULL; | |
Type = NewLineTypeDefault; | |
FileHandle = NULL; | |
CreateFile = FALSE; | |
// | |
// in this function, when you return error ( except EFI_OUT_OF_RESOURCES ) | |
// you should set status string via StatusBarSetStatusString(L"blah") | |
// since this function maybe called before the editorhandleinput loop | |
// so any error will cause editor return | |
// so if you want to print the error status | |
// you should set the status string | |
// | |
// | |
// try to open the file | |
// | |
Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ, 0); | |
if (!EFI_ERROR(Status)) { | |
CreateFile = FALSE; | |
if (FileHandle == NULL) { | |
StatusBarSetStatusString (L"Disk Error"); | |
return EFI_LOAD_ERROR; | |
} | |
Info = ShellGetFileInfo(FileHandle); | |
if (Info->Attribute & EFI_FILE_DIRECTORY) { | |
StatusBarSetStatusString (L"Directory Can Not Be Edited"); | |
FreePool (Info); | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Info->Attribute & EFI_FILE_READ_ONLY) { | |
FileBuffer.ReadOnly = TRUE; | |
} else { | |
FileBuffer.ReadOnly = FALSE; | |
} | |
// | |
// get file size | |
// | |
FileSize = (UINTN) Info->FileSize; | |
FreePool (Info); | |
} else if (Status == EFI_NOT_FOUND) { | |
// | |
// file not exists. add create and try again | |
// | |
Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, 0); | |
if (EFI_ERROR (Status)) { | |
if (Status == EFI_WRITE_PROTECTED || | |
Status == EFI_ACCESS_DENIED || | |
Status == EFI_NO_MEDIA || | |
Status == EFI_MEDIA_CHANGED | |
) { | |
StatusBarSetStatusString (L"Access Denied"); | |
} else if (Status == EFI_DEVICE_ERROR || Status == EFI_VOLUME_CORRUPTED || Status == EFI_VOLUME_FULL) { | |
StatusBarSetStatusString (L"Disk Error"); | |
} else { | |
StatusBarSetStatusString (L"Invalid File Name or Current-working-directory"); | |
} | |
return Status; | |
} else { | |
// | |
// it worked. now delete it and move on with the name (now validated) | |
// | |
Status = ShellDeleteFile (&FileHandle); | |
if (Status == EFI_WARN_DELETE_FAILURE) { | |
Status = EFI_ACCESS_DENIED; | |
} | |
FileHandle = NULL; | |
if (EFI_ERROR (Status)) { | |
StatusBarSetStatusString (L"Access Denied"); | |
return Status; | |
} | |
} | |
// | |
// file doesn't exist, so set CreateFile to TRUE | |
// | |
CreateFile = TRUE; | |
FileBuffer.ReadOnly = FALSE; | |
// | |
// all the check ends | |
// so now begin to set file name, free lines | |
// | |
if (StrCmp (FileName, FileBuffer.FileName) != 0) { | |
FileBufferSetFileName (FileName); | |
} | |
// | |
// free the old lines | |
// | |
FileBufferFree (); | |
} | |
// | |
// the file exists | |
// | |
if (!CreateFile) { | |
// | |
// allocate buffer to read file | |
// | |
Buffer = AllocateZeroPool (FileSize); | |
if (Buffer == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// read file into Buffer | |
// | |
Status = ShellReadFile (FileHandle, &FileSize, Buffer); | |
ShellCloseFile(&FileHandle); | |
FileHandle = NULL; | |
if (EFI_ERROR (Status)) { | |
StatusBarSetStatusString (L"Read File Failed"); | |
SHELL_FREE_NON_NULL (Buffer); | |
return EFI_LOAD_ERROR; | |
} | |
// | |
// nothing in this file | |
// | |
if (FileSize == 0) { | |
SHELL_FREE_NON_NULL (Buffer); | |
// | |
// since has no head, so only can be an ASCII file | |
// | |
FileBuffer.FileType = FileTypeAscii; | |
goto Done; | |
} | |
AsciiBuffer = Buffer; | |
if (FileSize < 2) { | |
// | |
// size < Unicode file header, so only can be ASCII file | |
// | |
FileBuffer.FileType = FileTypeAscii; | |
} else { | |
// | |
// Unicode file | |
// | |
if (*(UINT16 *) Buffer == EFI_UNICODE_BYTE_ORDER_MARK) { | |
// | |
// Unicode file's size should be even | |
// | |
if ((FileSize % 2) != 0) { | |
StatusBarSetStatusString (L"File Format Wrong"); | |
SHELL_FREE_NON_NULL (Buffer); | |
return EFI_LOAD_ERROR; | |
} | |
FileSize /= 2; | |
FileBuffer.FileType = FileTypeUnicode; | |
UnicodeBuffer = Buffer; | |
// | |
// pass this 0xff and 0xfe | |
// | |
UnicodeBuffer++; | |
FileSize--; | |
} else { | |
FileBuffer.FileType = FileTypeAscii; | |
} | |
// | |
// end of AsciiBuffer == | |
// | |
} | |
// | |
// end of FileSize < 2 | |
// all the check ends | |
// so now begin to set file name, free lines | |
// | |
if (StrCmp (FileName, FileBuffer.FileName) != 0) { | |
FileBufferSetFileName (FileName); | |
} | |
// | |
// free the old lines | |
// | |
FileBufferFree (); | |
// | |
// parse file content line by line | |
// | |
for (LoopVar1 = 0; LoopVar1 < FileSize; LoopVar1++) { | |
Type = NewLineTypeUnknown; | |
for (LineSize = LoopVar1; LineSize < FileSize; LineSize++) { | |
if (FileBuffer.FileType == FileTypeAscii) { | |
if (AsciiBuffer[LineSize] == CHAR_CARRIAGE_RETURN) { | |
Type = NewLineTypeCarriageReturn; | |
// | |
// has LF following | |
// | |
if (LineSize < FileSize - 1) { | |
if (AsciiBuffer[LineSize + 1] == CHAR_LINEFEED) { | |
Type = NewLineTypeCarriageReturnLineFeed; | |
} | |
} | |
break; | |
} else if (AsciiBuffer[LineSize] == CHAR_LINEFEED) { | |
Type = NewLineTypeLineFeed; | |
// | |
// has CR following | |
// | |
if (LineSize < FileSize - 1) { | |
if (AsciiBuffer[LineSize + 1] == CHAR_CARRIAGE_RETURN) { | |
Type = NewLineTypeLineFeedCarriageReturn; | |
} | |
} | |
break; | |
} | |
} else { | |
if (UnicodeBuffer[LineSize] == CHAR_CARRIAGE_RETURN) { | |
Type = NewLineTypeCarriageReturn; | |
// | |
// has LF following | |
// | |
if (LineSize < FileSize - 1) { | |
if (UnicodeBuffer[LineSize + 1] == CHAR_LINEFEED) { | |
Type = NewLineTypeCarriageReturnLineFeed; | |
} | |
} | |
break; | |
} else if (UnicodeBuffer[LineSize] == CHAR_LINEFEED) { | |
Type = NewLineTypeLineFeed; | |
// | |
// has CR following | |
// | |
if (LineSize < FileSize - 1) { | |
if (UnicodeBuffer[LineSize + 1] == CHAR_CARRIAGE_RETURN) { | |
Type = NewLineTypeLineFeedCarriageReturn; | |
} | |
} | |
break; | |
} | |
} | |
// | |
// endif == ASCII | |
// | |
} | |
// | |
// end of for LineSize | |
// | |
// if the type is wrong, then exit | |
// | |
if (Type == NewLineTypeUnknown) { | |
// | |
// Now if Type is NewLineTypeUnknown, it should be file end | |
// | |
Type = NewLineTypeDefault; | |
} | |
LineSizeBackup = LineSize; | |
// | |
// create a new line | |
// | |
Line = FileBufferCreateLine (); | |
if (Line == NULL) { | |
SHELL_FREE_NON_NULL (Buffer); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// calculate file length | |
// | |
LineSize -= LoopVar1; | |
// | |
// Unicode and one CHAR_NULL | |
// | |
SHELL_FREE_NON_NULL (Line->Buffer); | |
Line->Buffer = AllocateZeroPool (LineSize * 2 + 2); | |
if (Line->Buffer == NULL) { | |
RemoveEntryList (&Line->Link); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// copy this line to Line->Buffer | |
// | |
for (LoopVar2 = 0; LoopVar2 < LineSize; LoopVar2++) { | |
if (FileBuffer.FileType == FileTypeAscii) { | |
Line->Buffer[LoopVar2] = (CHAR16) AsciiBuffer[LoopVar1]; | |
} else { | |
Line->Buffer[LoopVar2] = UnicodeBuffer[LoopVar1]; | |
} | |
LoopVar1++; | |
} | |
// | |
// LoopVar1 now points to where CHAR_CARRIAGE_RETURN or CHAR_LINEFEED; | |
// | |
Line->Buffer[LineSize] = 0; | |
Line->Size = LineSize; | |
Line->TotalSize = LineSize; | |
Line->Type = Type; | |
if (Type == NewLineTypeCarriageReturnLineFeed || Type == NewLineTypeLineFeedCarriageReturn) { | |
LoopVar1++; | |
} | |
// | |
// last character is a return, SO create a new line | |
// | |
if (((Type == NewLineTypeCarriageReturnLineFeed || Type == NewLineTypeLineFeedCarriageReturn) && LineSizeBackup == FileSize - 2) || | |
((Type == NewLineTypeLineFeed || Type == NewLineTypeCarriageReturn) && LineSizeBackup == FileSize - 1) | |
) { | |
Line = FileBufferCreateLine (); | |
if (Line == NULL) { | |
SHELL_FREE_NON_NULL (Buffer); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
} | |
// | |
// end of if | |
// | |
} | |
// | |
// end of LoopVar1 | |
// | |
SHELL_FREE_NON_NULL (Buffer); | |
} | |
// | |
// end of if CreateFile | |
// | |
Done: | |
FileBuffer.DisplayPosition.Row = 2; | |
FileBuffer.DisplayPosition.Column = 1; | |
FileBuffer.LowVisibleRange.Row = 1; | |
FileBuffer.LowVisibleRange.Column = 1; | |
FileBuffer.FilePosition.Row = 1; | |
FileBuffer.FilePosition.Column = 1; | |
FileBuffer.MousePosition.Row = 2; | |
FileBuffer.MousePosition.Column = 1; | |
if (!Recover) { | |
UnicodeBuffer = CatSPrint (NULL, L"%d Lines Read", FileBuffer.NumLines); | |
if (UnicodeBuffer == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
StatusBarSetStatusString (UnicodeBuffer); | |
FreePool (UnicodeBuffer); | |
} | |
/* | |
// | |
// check whether we have fs?: in filename | |
// | |
LoopVar1 = 0; | |
FSMappingPtr = NULL; | |
while (FileName[LoopVar1] != 0) { | |
if (FileName[LoopVar1] == L':') { | |
FSMappingPtr = &FileName[LoopVar1]; | |
break; | |
} | |
LoopVar1++; | |
} | |
if (FSMappingPtr == NULL) { | |
CurDir = ShellGetCurrentDir (NULL); | |
} else { | |
LoopVar1 = 0; | |
LoopVar2 = 0; | |
while (FileName[LoopVar1] != 0) { | |
if (FileName[LoopVar1] == L':') { | |
break; | |
} | |
FSMapping[LoopVar2++] = FileName[LoopVar1]; | |
LoopVar1++; | |
} | |
FSMapping[LoopVar2] = 0; | |
CurDir = ShellGetCurrentDir (FSMapping); | |
} | |
if (CurDir != NULL) { | |
for (LoopVar1 = 0; LoopVar1 < StrLen (CurDir) && CurDir[LoopVar1] != ':'; LoopVar1++); | |
CurDir[LoopVar1] = 0; | |
DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) ShellGetMap (CurDir); | |
FreePool (CurDir); | |
} else { | |
return EFI_LOAD_ERROR; | |
} | |
Status = LibDevicePathToInterface ( | |
&gEfiSimpleFileSystemProtocolGuid, | |
DevicePath, | |
(VOID **) &Vol | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_LOAD_ERROR; | |
} | |
Status = Vol->OpenVolume (Vol, &RootFs); | |
if (EFI_ERROR (Status)) { | |
return EFI_LOAD_ERROR; | |
} | |
// | |
// Get volume information of file system | |
// | |
Size = SIZE_OF_EFI_FILE_SYSTEM_INFO + 100; | |
VolumeInfo = (EFI_FILE_SYSTEM_INFO *) AllocateZeroPool (Size); | |
Status = RootFs->GetInfo (RootFs, &gEfiFileSystemInfoGuid, &Size, VolumeInfo); | |
if (EFI_ERROR (Status)) { | |
RootFs->Close (RootFs); | |
return EFI_LOAD_ERROR; | |
} | |
if (VolumeInfo->ReadOnly) { | |
StatusBarSetStatusString (L"WARNING: Volume Read Only"); | |
} | |
FreePool (VolumeInfo); | |
RootFs->Close (RootFs); | |
} | |
// | |
*/ | |
// | |
// has line | |
// | |
if (FileBuffer.Lines != 0) { | |
FileBuffer.CurrentLine = CR (FileBuffer.ListHead->ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); | |
} else { | |
// | |
// create a dummy line | |
// | |
Line = FileBufferCreateLine (); | |
if (Line == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
FileBuffer.CurrentLine = Line; | |
} | |
FileBuffer.FileModified = FALSE; | |
FileBufferNeedRefresh = TRUE; | |
FileBufferOnlyLineNeedRefresh = FALSE; | |
FileBufferMouseNeedRefresh = TRUE; | |
return EFI_SUCCESS; | |
} | |
/** | |
According to FileBuffer.NewLineType & FileBuffer.FileType, | |
get the return buffer and size. | |
@param[in] Type The type of line. | |
@param[out] Buffer The buffer to fill. | |
@param[out] Size The amount of the buffer used on return. | |
**/ | |
VOID | |
GetNewLine ( | |
IN CONST EE_NEWLINE_TYPE Type, | |
OUT CHAR8 *Buffer, | |
OUT UINT8 *Size | |
) | |
{ | |
UINT8 NewLineSize; | |
// | |
// give new line buffer, | |
// and will judge unicode or ascii | |
// | |
NewLineSize = 0; | |
// | |
// not legal new line type | |
// | |
if (Type != NewLineTypeLineFeed && Type != NewLineTypeCarriageReturn && Type != NewLineTypeCarriageReturnLineFeed && Type != NewLineTypeLineFeedCarriageReturn) { | |
*Size = 0; | |
return ; | |
} | |
// | |
// use_cr: give 0x0d | |
// | |
if (Type == NewLineTypeCarriageReturn) { | |
if (MainEditor.FileBuffer->FileType == FileTypeUnicode) { | |
Buffer[0] = 0x0d; | |
Buffer[1] = 0; | |
NewLineSize = 2; | |
} else { | |
Buffer[0] = 0x0d; | |
NewLineSize = 1; | |
} | |
*Size = NewLineSize; | |
return ; | |
} | |
// | |
// use_lf: give 0x0a | |
// | |
if (Type == NewLineTypeLineFeed) { | |
if (MainEditor.FileBuffer->FileType == FileTypeUnicode) { | |
Buffer[0] = 0x0a; | |
Buffer[1] = 0; | |
NewLineSize = 2; | |
} else { | |
Buffer[0] = 0x0a; | |
NewLineSize = 1; | |
} | |
*Size = NewLineSize; | |
return ; | |
} | |
// | |
// use_crlf: give 0x0d 0x0a | |
// | |
if (Type == NewLineTypeCarriageReturnLineFeed) { | |
if (MainEditor.FileBuffer->FileType == FileTypeUnicode) { | |
Buffer[0] = 0x0d; | |
Buffer[1] = 0; | |
Buffer[2] = 0x0a; | |
Buffer[3] = 0; | |
NewLineSize = 4; | |
} else { | |
Buffer[0] = 0x0d; | |
Buffer[1] = 0x0a; | |
NewLineSize = 2; | |
} | |
*Size = NewLineSize; | |
return ; | |
} | |
// | |
// use_lfcr: give 0x0a 0x0d | |
// | |
if (Type == NewLineTypeLineFeedCarriageReturn) { | |
if (MainEditor.FileBuffer->FileType == FileTypeUnicode) { | |
Buffer[0] = 0x0a; | |
Buffer[1] = 0; | |
Buffer[2] = 0x0d; | |
Buffer[3] = 0; | |
NewLineSize = 4; | |
} else { | |
Buffer[0] = 0x0a; | |
Buffer[1] = 0x0d; | |
NewLineSize = 2; | |
} | |
*Size = NewLineSize; | |
return ; | |
} | |
} | |
/** | |
Change a Unicode string to an ASCII string. | |
@param[in] UStr The Unicode string. | |
@param[in] Length The maximum size of AStr. | |
@param[out] AStr ASCII string to pass out. | |
@return The actuall length. | |
**/ | |
UINTN | |
UnicodeToAscii ( | |
IN CONST CHAR16 *UStr, | |
IN CONST UINTN Length, | |
OUT CHAR8 *AStr | |
) | |
{ | |
UINTN Index; | |
// | |
// just buffer copy, not character copy | |
// | |
for (Index = 0; Index < Length; Index++) { | |
*AStr++ = (CHAR8) *UStr++; | |
} | |
return Index; | |
} | |
/** | |
Save lines in FileBuffer to disk | |
@param[in] FileName The file name for writing. | |
@retval EFI_SUCCESS Data was written. | |
@retval EFI_LOAD_ERROR | |
@retval EFI_OUT_OF_RESOURCES There were not enough resources to write the file. | |
**/ | |
EFI_STATUS | |
FileBufferSave ( | |
IN CONST CHAR16 *FileName | |
) | |
{ | |
SHELL_FILE_HANDLE FileHandle; | |
LIST_ENTRY *Link; | |
EFI_EDITOR_LINE *Line; | |
CHAR16 *Str; | |
EFI_STATUS Status; | |
UINTN Length; | |
UINTN NumLines; | |
CHAR8 NewLineBuffer[4]; | |
UINT8 NewLineSize; | |
EFI_FILE_INFO *Info; | |
UINT64 Attribute; | |
EE_NEWLINE_TYPE Type; | |
UINTN TotalSize; | |
// | |
// 2M | |
// | |
CHAR8 *Cache; | |
UINTN LeftSize; | |
UINTN Size; | |
CHAR8 *Ptr; | |
Length = 0; | |
// | |
// 2M | |
// | |
TotalSize = 0x200000; | |
Attribute = 0; | |
// | |
// if is the old file | |
// | |
if (FileBuffer.FileName != NULL && StrCmp (FileName, FileBuffer.FileName) == 0) { | |
// | |
// file has not been modified | |
// | |
if (!FileBuffer.FileModified) { | |
return EFI_SUCCESS; | |
} | |
// | |
// if file is read-only, set error | |
// | |
if (FileBuffer.ReadOnly) { | |
StatusBarSetStatusString (L"Read Only File Can Not Be Saved"); | |
return EFI_SUCCESS; | |
} | |
} | |
Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0); | |
if (!EFI_ERROR (Status)) { | |
Info = ShellGetFileInfo(FileHandle); | |
if (Info != NULL && Info->Attribute & EFI_FILE_DIRECTORY) { | |
StatusBarSetStatusString (L"Directory Can Not Be Saved"); | |
ShellCloseFile(FileHandle); | |
FreePool(Info); | |
return EFI_LOAD_ERROR; | |
} | |
if (Info != NULL) { | |
Attribute = Info->Attribute & ~EFI_FILE_READ_ONLY; | |
FreePool(Info); | |
} | |
// | |
// if file exits, so delete it | |
// | |
Status = ShellDeleteFile (&FileHandle); | |
if (EFI_ERROR (Status) || Status == EFI_WARN_DELETE_FAILURE) { | |
StatusBarSetStatusString (L"Write File Failed"); | |
return EFI_LOAD_ERROR; | |
} | |
} | |
Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, Attribute); | |
if (EFI_ERROR (Status)) { | |
StatusBarSetStatusString (L"Create File Failed"); | |
return EFI_LOAD_ERROR; | |
} | |
// | |
// if file is Unicode file, write Unicode header to it. | |
// | |
if (FileBuffer.FileType == FileTypeUnicode) { | |
Length = 2; | |
Status = ShellWriteFile (FileHandle, &Length, (VOID*)&gUnicodeFileTag); | |
if (EFI_ERROR (Status)) { | |
ShellDeleteFile (&FileHandle); | |
return EFI_LOAD_ERROR; | |
} | |
} | |
Cache = AllocateZeroPool (TotalSize); | |
if (Cache == NULL) { | |
ShellDeleteFile (&FileHandle); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// write all the lines back to disk | |
// | |
NumLines = 0; | |
Type = NewLineTypeCarriageReturnLineFeed; | |
Ptr = Cache; | |
LeftSize = TotalSize; | |
for (Link = FileBuffer.ListHead->ForwardLink; Link != FileBuffer.ListHead; Link = Link->ForwardLink) { | |
Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); | |
if (Line->Type != NewLineTypeDefault) { | |
Type = Line->Type; | |
} | |
// | |
// newline character is at most 4 bytes ( two Unicode characters ) | |
// | |
Length = 4; | |
if (Line->Buffer != NULL && Line->Size != 0) { | |
if (FileBuffer.FileType == FileTypeAscii) { | |
Length += Line->Size; | |
} else { | |
Length += (Line->Size * 2); | |
} | |
// | |
// end if FileTypeAscii | |
// | |
} | |
// | |
// no cache room left, so write cache to disk | |
// | |
if (LeftSize < Length) { | |
Size = TotalSize - LeftSize; | |
Status = ShellWriteFile (FileHandle, &Size, Cache); | |
if (EFI_ERROR (Status)) { | |
ShellDeleteFile (&FileHandle); | |
FreePool (Cache); | |
return EFI_LOAD_ERROR; | |
} | |
Ptr = Cache; | |
LeftSize = TotalSize; | |
} | |
if (Line->Buffer != NULL && Line->Size != 0) { | |
if (FileBuffer.FileType == FileTypeAscii) { | |
UnicodeToAscii (Line->Buffer, Line->Size, Ptr); | |
Length = Line->Size; | |
} else { | |
Length = (Line->Size * 2); | |
CopyMem (Ptr, (CHAR8 *) Line->Buffer, Length); | |
} | |
// | |
// end if FileTypeAscii | |
// | |
Ptr += Length; | |
LeftSize -= Length; | |
} | |
// | |
// end of if Line -> Buffer != NULL && Line -> Size != 0 | |
// | |
// if not the last line , write return buffer to disk | |
// | |
if (Link->ForwardLink != FileBuffer.ListHead) { | |
GetNewLine (Type, NewLineBuffer, &NewLineSize); | |
CopyMem (Ptr, (CHAR8 *) NewLineBuffer, NewLineSize); | |
Ptr += NewLineSize; | |
LeftSize -= NewLineSize; | |
} | |
NumLines++; | |
} | |
if (TotalSize != LeftSize) { | |
Size = TotalSize - LeftSize; | |
Status = ShellWriteFile (FileHandle, &Size, Cache); | |
if (EFI_ERROR (Status)) { | |
ShellDeleteFile (&FileHandle); | |
FreePool (Cache); | |
return EFI_LOAD_ERROR; | |
} | |
} | |
FreePool (Cache); | |
ShellCloseFile(&FileHandle); | |
FileBuffer.FileModified = FALSE; | |
// | |
// set status string | |
// | |
Str = CatSPrint (NULL, L"%d Lines 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; | |
} | |