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