From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../Library/UefiShellDebug1CommandsLib/Edit/Edit.c | 156 + .../UefiShellDebug1CommandsLib/Edit/FileBuffer.c | 3319 ++++++++++++++++++++ .../UefiShellDebug1CommandsLib/Edit/FileBuffer.h | 240 ++ .../Edit/MainTextEditor.c | 1974 ++++++++++++ .../Edit/MainTextEditor.h | 66 + .../Library/UefiShellDebug1CommandsLib/Edit/Misc.c | 84 + .../Library/UefiShellDebug1CommandsLib/Edit/Misc.h | 44 + .../Edit/TextEditStrings.uni | 69 + .../UefiShellDebug1CommandsLib/Edit/TextEditor.h | 26 + .../Edit/TextEditorTypes.h | 97 + 10 files changed, 6075 insertions(+) create mode 100644 roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/Edit.c create mode 100644 roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/FileBuffer.c create mode 100644 roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/FileBuffer.h create mode 100644 roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/MainTextEditor.c create mode 100644 roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/MainTextEditor.h create mode 100644 roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/Misc.c create mode 100644 roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/Misc.h create mode 100644 roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/TextEditStrings.uni create mode 100644 roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/TextEditor.h create mode 100644 roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/TextEditorTypes.h (limited to 'roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit') diff --git a/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/Edit.c b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/Edit.c new file mode 100644 index 000000000..1160efaa7 --- /dev/null +++ b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/Edit.c @@ -0,0 +1,156 @@ +/** @file + Main file for Edit shell Debug1 function. + + (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "UefiShellDebug1CommandsLib.h" +#include "TextEditor.h" + +/** + Function for 'edit' command. + + @param[in] ImageHandle Handle to the Image (NULL if Internal). + @param[in] SystemTable Pointer to the System Table (NULL if Internal). +**/ +SHELL_STATUS +EFIAPI +ShellCommandRunEdit ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + CHAR16 *Buffer; + CHAR16 *ProblemParam; + SHELL_STATUS ShellStatus; + LIST_ENTRY *Package; + CONST CHAR16 *Cwd; + CHAR16 *Nfs; + CHAR16 *Spot; + CONST CHAR16 *TempParam; +// SHELL_FILE_HANDLE TempHandle; + + Buffer = NULL; + ShellStatus = SHELL_SUCCESS; + Nfs = NULL; + + // + // initialize the shell lib (we must be in non-auto-init...) + // + Status = ShellInitialize(); + ASSERT_EFI_ERROR(Status); + + Status = CommandInit(); + ASSERT_EFI_ERROR(Status); + + // + // parse the command line + // + Status = ShellCommandLineParse (EmptyParamList, &Package, &ProblemParam, TRUE); + if (EFI_ERROR(Status)) { + if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellDebug1HiiHandle, L"edit", ProblemParam); + FreePool(ProblemParam); + ShellStatus = SHELL_INVALID_PARAMETER; + } else { + ASSERT(FALSE); + } + } else { + if (ShellCommandLineGetCount(Package) > 2) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellDebug1HiiHandle, L"edit"); + ShellStatus = SHELL_INVALID_PARAMETER; + } else { + Cwd = gEfiShellProtocol->GetCurDir(NULL); + if (Cwd == NULL) { + Cwd = ShellGetEnvironmentVariable(L"path"); + if (Cwd != NULL) { + Nfs = StrnCatGrow(&Nfs, NULL, Cwd+3, 0); + if (Nfs != NULL) { + Spot = StrStr(Nfs, L";"); + if (Spot != NULL) { + *Spot = CHAR_NULL; + } + Spot = StrStr(Nfs, L"\\"); + if (Spot != NULL) { + Spot[1] = CHAR_NULL; + } + gEfiShellProtocol->SetCurDir(NULL, Nfs); + FreePool(Nfs); + } + } + } + + Status = MainEditorInit (); + + if (EFI_ERROR (Status)) { + gST->ConOut->ClearScreen (gST->ConOut); + gST->ConOut->EnableCursor (gST->ConOut, TRUE); + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_MAIN_INIT_FAILED), gShellDebug1HiiHandle); + } else { + MainEditorBackup (); + + // + // if editor launched with file named + // + if (ShellCommandLineGetCount(Package) == 2) { + TempParam = ShellCommandLineGetRawValue(Package, 1); + ASSERT(TempParam != NULL); + FileBufferSetFileName (TempParam); +// if (EFI_ERROR(ShellFileExists(MainEditor.FileBuffer->FileName))) { +// Status = ShellOpenFileByName(MainEditor.FileBuffer->FileName, &TempHandle, EFI_FILE_MODE_CREATE|EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0); +// if (!EFI_ERROR(Status)) { +// ShellCloseFile(&TempHandle); +// } +// } + } + + Status = FileBufferRead (MainEditor.FileBuffer->FileName, FALSE); + if (!EFI_ERROR (Status)) { + MainEditorRefresh (); + + Status = MainEditorKeyInput (); + } + + if (Status != EFI_OUT_OF_RESOURCES) { + // + // back up the status string + // + Buffer = CatSPrint (NULL, L"%s", StatusBarGetString()); + } + + MainEditorCleanup (); + + // + // print editor exit code on screen + // + if (Status == EFI_SUCCESS) { + } else if (Status == EFI_OUT_OF_RESOURCES) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_GEN_OUT_MEM), gShellDebug1HiiHandle, L"edit"); + } else { + if (Buffer != NULL) { + if (StrCmp (Buffer, L"") != 0) { + // + // print out the status string + // + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_MAIN_BUFFER), gShellDebug1HiiHandle, Buffer); + } else { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_MAIN_UNKNOWN_EDITOR_ERR), gShellDebug1HiiHandle); + } + } else { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_MAIN_UNKNOWN_EDITOR_ERR), gShellDebug1HiiHandle); + } + } + + if (Status != EFI_OUT_OF_RESOURCES) { + SHELL_FREE_NON_NULL (Buffer); + } + } + } + ShellCommandLineFreeVarList (Package); + } + return ShellStatus; +} diff --git a/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/FileBuffer.c b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/FileBuffer.c new file mode 100644 index 000000000..5659ec981 --- /dev/null +++ b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/FileBuffer.c @@ -0,0 +1,3319 @@ +/** @file + Implements filebuffer interface functions. + + Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "TextEditor.h" +#include +#include + +EFI_EDITOR_FILE_BUFFER FileBuffer; +EFI_EDITOR_FILE_BUFFER FileBufferBackupVar; + +// +// for basic initialization of FileBuffer +// +EFI_EDITOR_FILE_BUFFER FileBufferConst = { + NULL, + FileTypeUnicode, + NULL, + NULL, + 0, + { + 0, + 0 + }, + { + 0, + 0 + }, + { + 0, + 0 + }, + { + 0, + 0 + }, + FALSE, + TRUE, + FALSE, + NULL +}; + +// +// the whole edit area needs to be refreshed +// +BOOLEAN FileBufferNeedRefresh; + +// +// only the current line in edit area needs to be refresh +// +BOOLEAN FileBufferOnlyLineNeedRefresh; + +BOOLEAN FileBufferMouseNeedRefresh; + +extern BOOLEAN EditorMouseAction; + +/** + Initialization function for FileBuffer. + + @param EFI_SUCCESS The initialization was successful. + @param EFI_LOAD_ERROR A default name could not be created. + @param EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +FileBufferInit ( + VOID + ) +{ + // + // basically initialize the FileBuffer + // + CopyMem (&FileBuffer , &FileBufferConst, sizeof (EFI_EDITOR_FILE_BUFFER)); + CopyMem (&FileBufferBackupVar, &FileBufferConst, sizeof (EFI_EDITOR_FILE_BUFFER)); + + // + // set default FileName + // + FileBuffer.FileName = EditGetDefaultFileName (L"txt"); + if (FileBuffer.FileName == NULL) { + return EFI_LOAD_ERROR; + } + + FileBuffer.ListHead = AllocateZeroPool (sizeof (LIST_ENTRY)); + if (FileBuffer.ListHead == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InitializeListHead (FileBuffer.ListHead); + + FileBuffer.DisplayPosition.Row = 2; + FileBuffer.DisplayPosition.Column = 1; + FileBuffer.LowVisibleRange.Row = 2; + FileBuffer.LowVisibleRange.Column = 1; + + FileBufferNeedRefresh = FALSE; + FileBufferMouseNeedRefresh = FALSE; + FileBufferOnlyLineNeedRefresh = FALSE; + + return EFI_SUCCESS; +} + +/** + Backup function for FileBuffer. Only backup the following items: + Mouse/Cursor position + File Name, Type, ReadOnly, Modified + Insert Mode + + This is for making the file buffer refresh as few as possible. + + @retval EFI_SUCCESS The backup operation was successful. +**/ +EFI_STATUS +FileBufferBackup ( + VOID + ) +{ + FileBufferBackupVar.MousePosition = FileBuffer.MousePosition; + + SHELL_FREE_NON_NULL (FileBufferBackupVar.FileName); + FileBufferBackupVar.FileName = NULL; + FileBufferBackupVar.FileName = StrnCatGrow (&FileBufferBackupVar.FileName, NULL, FileBuffer.FileName, 0); + + FileBufferBackupVar.ModeInsert = FileBuffer.ModeInsert; + FileBufferBackupVar.FileType = FileBuffer.FileType; + + FileBufferBackupVar.FilePosition = FileBuffer.FilePosition; + FileBufferBackupVar.LowVisibleRange = FileBuffer.LowVisibleRange; + + FileBufferBackupVar.FileModified = FileBuffer.FileModified; + FileBufferBackupVar.ReadOnly = FileBuffer.ReadOnly; + + return EFI_SUCCESS; +} + +/** + Advance to the next Count lines + + @param[in] Count The line number to advance by. + @param[in] CurrentLine The pointer to the current line structure. + @param[in] LineList The pointer to the linked list of lines. + + @retval NULL There was an error. + @return The line structure after the advance. +**/ +EFI_EDITOR_LINE * +InternalEditorMiscLineAdvance ( + IN CONST UINTN Count, + IN CONST EFI_EDITOR_LINE *CurrentLine, + IN CONST LIST_ENTRY *LineList + ) + +{ + UINTN Index; + CONST EFI_EDITOR_LINE *Line; + + if (CurrentLine == NULL || LineList == NULL) { + return NULL; + } + + for (Line = CurrentLine, Index = 0; Index < Count; Index++) { + // + // if already last line + // + if (Line->Link.ForwardLink == LineList) { + return NULL; + } + + Line = CR (Line->Link.ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); + } + + return ((EFI_EDITOR_LINE *)Line); +} + +/** + Retreat to the previous Count lines. + + @param[in] Count The line number to retreat by. + @param[in] CurrentLine The pointer to the current line structure. + @param[in] LineList The pointer to the linked list of lines. + + @retval NULL There was an error. + @return The line structure after the retreat. +**/ +EFI_EDITOR_LINE * +InternalEditorMiscLineRetreat ( + IN CONST UINTN Count, + IN CONST EFI_EDITOR_LINE *CurrentLine, + IN CONST LIST_ENTRY *LineList + ) + +{ + UINTN Index; + CONST EFI_EDITOR_LINE *Line; + + if (CurrentLine == NULL || LineList == NULL) { + return NULL; + } + + for (Line = CurrentLine, Index = 0; Index < Count; Index++) { + // + // already the first line + // + if (Line->Link.BackLink == LineList) { + return NULL; + } + + Line = CR (Line->Link.BackLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); + } + + return ((EFI_EDITOR_LINE *)Line); +} + +/** + Advance/Retreat lines + + @param[in] Count line number to advance/retreat + >0 : advance + <0 : retreat + + @retval NULL An error occurred. + @return The line after advance/retreat. +**/ +EFI_EDITOR_LINE * +MoveLine ( + IN CONST INTN Count + ) +{ + EFI_EDITOR_LINE *Line; + UINTN AbsCount; + + // + // if < 0, then retreat + // if > 0, the advance + // + if (Count <= 0) { + AbsCount = (UINTN)ABS(Count); + Line = InternalEditorMiscLineRetreat (AbsCount,MainEditor.FileBuffer->CurrentLine,MainEditor.FileBuffer->ListHead); + } else { + Line = InternalEditorMiscLineAdvance ((UINTN)Count,MainEditor.FileBuffer->CurrentLine,MainEditor.FileBuffer->ListHead); + } + + return Line; +} + +/** + Function to update the 'screen' to display the mouse position. + + @retval EFI_SUCCESS The backup operation was successful. +**/ +EFI_STATUS +FileBufferRestoreMousePosition ( + VOID + ) +{ + EFI_EDITOR_COLOR_UNION Orig; + EFI_EDITOR_COLOR_UNION New; + UINTN FRow; + UINTN FColumn; + BOOLEAN HasCharacter; + EFI_EDITOR_LINE *CurrentLine; + EFI_EDITOR_LINE *Line; + CHAR16 Value; + + // + // variable initialization + // + Line = NULL; + + if (MainEditor.MouseSupported) { + + if (FileBufferMouseNeedRefresh) { + + FileBufferMouseNeedRefresh = FALSE; + + // + // if mouse position not moved and only mouse action + // so do not need to refresh mouse position + // + if ((FileBuffer.MousePosition.Row == FileBufferBackupVar.MousePosition.Row && + FileBuffer.MousePosition.Column == FileBufferBackupVar.MousePosition.Column) + && EditorMouseAction) { + return EFI_SUCCESS; + } + // + // backup the old screen attributes + // + Orig = MainEditor.ColorAttributes; + New.Data = 0; + New.Colors.Foreground = Orig.Colors.Background & 0xF; + New.Colors.Background = Orig.Colors.Foreground & 0x7; + + // + // clear the old mouse position + // + FRow = FileBuffer.LowVisibleRange.Row + FileBufferBackupVar.MousePosition.Row - 2; + + FColumn = FileBuffer.LowVisibleRange.Column + FileBufferBackupVar.MousePosition.Column - 1; + + HasCharacter = TRUE; + if (FRow > FileBuffer.NumLines) { + HasCharacter = FALSE; + } else { + CurrentLine = FileBuffer.CurrentLine; + Line = MoveLine (FRow - FileBuffer.FilePosition.Row); + + if (Line == NULL || FColumn > Line->Size) { + HasCharacter = FALSE; + } + + FileBuffer.CurrentLine = CurrentLine; + } + + ShellPrintEx ( + (INT32)FileBufferBackupVar.MousePosition.Column - 1, + (INT32)FileBufferBackupVar.MousePosition.Row - 1, + L" " + ); + + if (HasCharacter) { + Value = (Line->Buffer[FColumn - 1]); + ShellPrintEx ( + (INT32)FileBufferBackupVar.MousePosition.Column - 1, + (INT32)FileBufferBackupVar.MousePosition.Row - 1, + L"%c", + Value + ); + } + // + // set the new mouse position + // + gST->ConOut->SetAttribute (gST->ConOut, New.Data & 0x7F); + + // + // clear the old mouse position + // + FRow = FileBuffer.LowVisibleRange.Row + FileBuffer.MousePosition.Row - 2; + FColumn = FileBuffer.LowVisibleRange.Column + FileBuffer.MousePosition.Column - 1; + + HasCharacter = TRUE; + if (FRow > FileBuffer.NumLines) { + HasCharacter = FALSE; + } else { + CurrentLine = FileBuffer.CurrentLine; + Line = MoveLine (FRow - FileBuffer.FilePosition.Row); + + if (Line == NULL || FColumn > Line->Size) { + HasCharacter = FALSE; + } + + FileBuffer.CurrentLine = CurrentLine; + } + + ShellPrintEx ( + (INT32)FileBuffer.MousePosition.Column - 1, + (INT32)FileBuffer.MousePosition.Row - 1, + L" " + ); + + if (HasCharacter) { + Value = Line->Buffer[FColumn - 1]; + ShellPrintEx ( + (INT32)FileBuffer.MousePosition.Column - 1, + (INT32)FileBuffer.MousePosition.Row - 1, + L"%c", + Value + ); + } + // + // end of HasCharacter + // + gST->ConOut->SetAttribute (gST->ConOut, Orig.Data); + } + // + // end of MouseNeedRefresh + // + } + // + // end of MouseSupported + // + return EFI_SUCCESS; +} + +/** + Free all the lines in FileBuffer + Fields affected: + Lines + CurrentLine + NumLines + ListHead + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +FileBufferFreeLines ( + VOID + ) +{ + LIST_ENTRY *Link; + EFI_EDITOR_LINE *Line; + + // + // free all the lines + // + if (FileBuffer.Lines != NULL) { + + Line = FileBuffer.Lines; + Link = &(Line->Link); + do { + Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); + Link = Link->ForwardLink; + + // + // free line's buffer and line itself + // + LineFree (Line); + } while (Link != FileBuffer.ListHead); + } + // + // clean the line list related structure + // + FileBuffer.Lines = NULL; + FileBuffer.CurrentLine = NULL; + FileBuffer.NumLines = 0; + + FileBuffer.ListHead->ForwardLink = FileBuffer.ListHead; + FileBuffer.ListHead->BackLink = FileBuffer.ListHead; + + return EFI_SUCCESS; +} + +/** + Cleanup function for FileBuffer. + + @retval EFI_SUCCESS The cleanup was successful. +**/ +EFI_STATUS +FileBufferCleanup ( + VOID + ) +{ + EFI_STATUS Status; + + SHELL_FREE_NON_NULL (FileBuffer.FileName); + + // + // free all the lines + // + Status = FileBufferFreeLines (); + + SHELL_FREE_NON_NULL (FileBuffer.ListHead); + FileBuffer.ListHead = NULL; + + SHELL_FREE_NON_NULL (FileBufferBackupVar.FileName); + return Status; + +} + +/** + Print a line specified by Line on a row specified by Row of the screen. + + @param[in] Line The line to print. + @param[in] Row The row on the screen to print onto (begin from 1). + + @retval EFI_SUCCESS The printing was successful. +**/ +EFI_STATUS +FileBufferPrintLine ( + IN CONST EFI_EDITOR_LINE *Line, + IN CONST UINTN Row + ) +{ + + CHAR16 *Buffer; + UINTN Limit; + CHAR16 *PrintLine; + CHAR16 *PrintLine2; + UINTN BufLen; + + // + // print start from correct character + // + Buffer = Line->Buffer + FileBuffer.LowVisibleRange.Column - 1; + + Limit = Line->Size - FileBuffer.LowVisibleRange.Column + 1; + if (Limit > Line->Size) { + Limit = 0; + } + + BufLen = (MainEditor.ScreenSize.Column + 1) * sizeof (CHAR16); + PrintLine = AllocatePool (BufLen); + if (PrintLine != NULL) { + StrnCpyS (PrintLine, BufLen/sizeof(CHAR16), Buffer, MIN(Limit, MainEditor.ScreenSize.Column)); + for (Limit = StrLen (PrintLine); Limit < MainEditor.ScreenSize.Column; Limit++) { + PrintLine[Limit] = L' '; + } + + PrintLine[MainEditor.ScreenSize.Column] = CHAR_NULL; + + PrintLine2 = AllocatePool (BufLen * 2); + if (PrintLine2 != NULL) { + ShellCopySearchAndReplace(PrintLine, PrintLine2, BufLen * 2, L"%", L"^%", FALSE, FALSE); + + ShellPrintEx ( + 0, + (INT32)Row - 1, + L"%s", + PrintLine2 + ); + FreePool (PrintLine2); + } + FreePool (PrintLine); + } + + return EFI_SUCCESS; +} + +/** + Set the cursor position according to FileBuffer.DisplayPosition. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +FileBufferRestorePosition ( + VOID + ) +{ + // + // set cursor position + // + return (gST->ConOut->SetCursorPosition ( + gST->ConOut, + FileBuffer.DisplayPosition.Column - 1, + FileBuffer.DisplayPosition.Row - 1 + )); +} + +/** + Refresh the screen with whats in the buffer. + + @retval EFI_SUCCESS The refresh was successful. + @retval EFI_LOAD_ERROR There was an error finding what to write. +**/ +EFI_STATUS +FileBufferRefresh ( + VOID + ) +{ + LIST_ENTRY *Link; + EFI_EDITOR_LINE *Line; + UINTN Row; + + // + // if it's the first time after editor launch, so should refresh + // + if (!EditorFirst) { + // + // no definite required refresh + // and file position displayed on screen has not been changed + // + if (!FileBufferNeedRefresh && + !FileBufferOnlyLineNeedRefresh && + FileBufferBackupVar.LowVisibleRange.Row == FileBuffer.LowVisibleRange.Row && + FileBufferBackupVar.LowVisibleRange.Column == FileBuffer.LowVisibleRange.Column + ) { + + FileBufferRestoreMousePosition (); + FileBufferRestorePosition (); + + return EFI_SUCCESS; + } + } + + gST->ConOut->EnableCursor (gST->ConOut, FALSE); + + // + // only need to refresh current line + // + if (FileBufferOnlyLineNeedRefresh && + FileBufferBackupVar.LowVisibleRange.Row == FileBuffer.LowVisibleRange.Row && + FileBufferBackupVar.LowVisibleRange.Column == FileBuffer.LowVisibleRange.Column + ) { + + EditorClearLine (FileBuffer.DisplayPosition.Row, MainEditor.ScreenSize.Column, MainEditor.ScreenSize.Row); + FileBufferPrintLine ( + FileBuffer.CurrentLine, + FileBuffer.DisplayPosition.Row + ); + } else { + // + // the whole edit area need refresh + // + + // + // no line + // + if (FileBuffer.Lines == NULL) { + FileBufferRestoreMousePosition (); + FileBufferRestorePosition (); + gST->ConOut->EnableCursor (gST->ConOut, TRUE); + + return EFI_SUCCESS; + } + // + // get the first line that will be displayed + // + Line = MoveLine (FileBuffer.LowVisibleRange.Row - FileBuffer.FilePosition.Row); + if (Line == NULL) { + gST->ConOut->EnableCursor (gST->ConOut, TRUE); + + return EFI_LOAD_ERROR; + } + + Link = &(Line->Link); + Row = 2; + do { + Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); + + // + // print line at row + // + FileBufferPrintLine (Line, Row); + + Link = Link->ForwardLink; + Row++; + } while (Link != FileBuffer.ListHead && Row <= (MainEditor.ScreenSize.Row - 1)); + // + // while not file end and not screen full + // + while (Row <= (MainEditor.ScreenSize.Row - 1)) { + EditorClearLine (Row, MainEditor.ScreenSize.Column, MainEditor.ScreenSize.Row); + Row++; + } + } + + FileBufferRestoreMousePosition (); + FileBufferRestorePosition (); + + FileBufferNeedRefresh = FALSE; + FileBufferOnlyLineNeedRefresh = FALSE; + + gST->ConOut->EnableCursor (gST->ConOut, TRUE); + return EFI_SUCCESS; +} + +/** + Create a new line and append it to the line list. + Fields affected: + NumLines + Lines + + @retval NULL The create line failed. + @return The line created. +**/ +EFI_EDITOR_LINE * +FileBufferCreateLine ( + VOID + ) +{ + EFI_EDITOR_LINE *Line; + + // + // allocate a line structure + // + Line = AllocateZeroPool (sizeof (EFI_EDITOR_LINE)); + if (Line == NULL) { + return NULL; + } + // + // initialize the structure + // + Line->Signature = LINE_LIST_SIGNATURE; + Line->Size = 0; + Line->TotalSize = 0; + Line->Type = NewLineTypeDefault; + + // + // initial buffer of the line is "\0" + // + ASSERT(CHAR_NULL == CHAR_NULL); + Line->Buffer = CatSPrint (NULL, L"\0"); + if (Line->Buffer == NULL) { + return NULL; + } + + FileBuffer.NumLines++; + + // + // insert the line into line list + // + InsertTailList (FileBuffer.ListHead, &Line->Link); + + if (FileBuffer.Lines == NULL) { + FileBuffer.Lines = CR (FileBuffer.ListHead->ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); + } + + return Line; +} + +/** + Set FileName field in FileBuffer. + + @param Str The file name to set. + + @retval EFI_SUCCESS The filename was successfully set. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_INVALID_PARAMETER Str is not a valid filename. +**/ +EFI_STATUS +FileBufferSetFileName ( + IN CONST CHAR16 *Str + ) +{ + // + // Verify the parameters + // + if (!IsValidFileName(Str)) { + return (EFI_INVALID_PARAMETER); + } + // + // free the old file name + // + SHELL_FREE_NON_NULL (FileBuffer.FileName); + + // + // Allocate and set the new name + // + FileBuffer.FileName = CatSPrint (NULL, L"%s", Str); + if (FileBuffer.FileName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} +/** + Free the existing file lines and reset the modified flag. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +FileBufferFree ( + VOID + ) +{ + // + // free all the lines + // + FileBufferFreeLines (); + FileBuffer.FileModified = FALSE; + + return EFI_SUCCESS; +} + + +/** + Read a file from disk into the FileBuffer. + + @param[in] FileName The filename to read. + @param[in] Recover TRUE if is for recover mode, no information printouts. + + @retval EFI_SUCCESS The load was successful. + @retval EFI_LOAD_ERROR The load failed. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_INVALID_PARAMETER FileName is a directory. +**/ +EFI_STATUS +FileBufferRead ( + IN CONST CHAR16 *FileName, + IN CONST BOOLEAN Recover + ) +{ + EFI_EDITOR_LINE *Line; + EE_NEWLINE_TYPE Type; + UINTN LoopVar1; + UINTN LoopVar2; + UINTN LineSize; + VOID *Buffer; + CHAR16 *UnicodeBuffer; + UINT8 *AsciiBuffer; + UINTN FileSize; + SHELL_FILE_HANDLE FileHandle; + BOOLEAN CreateFile; + EFI_STATUS Status; + UINTN LineSizeBackup; + EFI_FILE_INFO *Info; + + Line = NULL; + LoopVar1 = 0; + FileSize = 0; + UnicodeBuffer = NULL; + Type = NewLineTypeDefault; + FileHandle = NULL; + CreateFile = FALSE; + + // + // in this function, when you return error ( except EFI_OUT_OF_RESOURCES ) + // you should set status string via StatusBarSetStatusString(L"blah") + // since this function maybe called before the editorhandleinput loop + // so any error will cause editor return + // so if you want to print the error status + // you should set the status string + // + + // + // try to open the file + // + Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ, 0); + + if (!EFI_ERROR(Status)) { + CreateFile = FALSE; + if (FileHandle == NULL) { + StatusBarSetStatusString (L"Disk Error"); + return EFI_LOAD_ERROR; + } + + Info = ShellGetFileInfo(FileHandle); + + if (Info->Attribute & EFI_FILE_DIRECTORY) { + StatusBarSetStatusString (L"Directory Can Not Be Edited"); + FreePool (Info); + return EFI_INVALID_PARAMETER; + } + + if (Info->Attribute & EFI_FILE_READ_ONLY) { + FileBuffer.ReadOnly = TRUE; + } else { + FileBuffer.ReadOnly = FALSE; + } + // + // get file size + // + FileSize = (UINTN) Info->FileSize; + + FreePool (Info); + } else if (Status == EFI_NOT_FOUND) { + // + // file not exists. add create and try again + // + Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, 0); + if (EFI_ERROR (Status)) { + if (Status == EFI_WRITE_PROTECTED || + Status == EFI_ACCESS_DENIED || + Status == EFI_NO_MEDIA || + Status == EFI_MEDIA_CHANGED + ) { + StatusBarSetStatusString (L"Access Denied"); + } else if (Status == EFI_DEVICE_ERROR || Status == EFI_VOLUME_CORRUPTED || Status == EFI_VOLUME_FULL) { + StatusBarSetStatusString (L"Disk Error"); + } else { + StatusBarSetStatusString (L"Invalid File Name or Current-working-directory"); + } + + return Status; + } else { + // + // it worked. now delete it and move on with the name (now validated) + // + Status = ShellDeleteFile (&FileHandle); + if (Status == EFI_WARN_DELETE_FAILURE) { + Status = EFI_ACCESS_DENIED; + } + FileHandle = NULL; + if (EFI_ERROR (Status)) { + StatusBarSetStatusString (L"Access Denied"); + return Status; + } + } + // + // file doesn't exist, so set CreateFile to TRUE + // + CreateFile = TRUE; + FileBuffer.ReadOnly = FALSE; + + // + // all the check ends + // so now begin to set file name, free lines + // + if (StrCmp (FileName, FileBuffer.FileName) != 0) { + FileBufferSetFileName (FileName); + } + // + // free the old lines + // + FileBufferFree (); + + } + // + // the file exists + // + if (!CreateFile) { + // + // allocate buffer to read file + // + Buffer = AllocateZeroPool (FileSize); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // read file into Buffer + // + Status = ShellReadFile (FileHandle, &FileSize, Buffer); + ShellCloseFile(&FileHandle); + FileHandle = NULL; + if (EFI_ERROR (Status)) { + StatusBarSetStatusString (L"Read File Failed"); + SHELL_FREE_NON_NULL (Buffer); + return EFI_LOAD_ERROR; + } + // + // nothing in this file + // + if (FileSize == 0) { + SHELL_FREE_NON_NULL (Buffer); + // + // since has no head, so only can be an ASCII file + // + FileBuffer.FileType = FileTypeAscii; + + goto Done; + } + + AsciiBuffer = Buffer; + + if (FileSize < 2) { + // + // size < Unicode file header, so only can be ASCII file + // + FileBuffer.FileType = FileTypeAscii; + } else { + // + // Unicode file + // + if (*(UINT16 *) Buffer == EFI_UNICODE_BYTE_ORDER_MARK) { + // + // Unicode file's size should be even + // + if ((FileSize % 2) != 0) { + StatusBarSetStatusString (L"File Format Wrong"); + SHELL_FREE_NON_NULL (Buffer); + return EFI_LOAD_ERROR; + } + + FileSize /= 2; + + FileBuffer.FileType = FileTypeUnicode; + UnicodeBuffer = Buffer; + + // + // pass this 0xff and 0xfe + // + UnicodeBuffer++; + FileSize--; + } else { + FileBuffer.FileType = FileTypeAscii; + } + // + // end of AsciiBuffer == + // + } + // + // end of FileSize < 2 + // all the check ends + // so now begin to set file name, free lines + // + if (StrCmp (FileName, FileBuffer.FileName) != 0) { + FileBufferSetFileName (FileName); + } + + // + // free the old lines + // + FileBufferFree (); + + // + // parse file content line by line + // + for (LoopVar1 = 0; LoopVar1 < FileSize; LoopVar1++) { + Type = NewLineTypeUnknown; + + for (LineSize = LoopVar1; LineSize < FileSize; LineSize++) { + if (FileBuffer.FileType == FileTypeAscii) { + if (AsciiBuffer[LineSize] == CHAR_CARRIAGE_RETURN) { + Type = NewLineTypeCarriageReturn; + + // + // has LF following + // + if (LineSize < FileSize - 1) { + if (AsciiBuffer[LineSize + 1] == CHAR_LINEFEED) { + Type = NewLineTypeCarriageReturnLineFeed; + } + } + + break; + } else if (AsciiBuffer[LineSize] == CHAR_LINEFEED) { + Type = NewLineTypeLineFeed; + + // + // has CR following + // + if (LineSize < FileSize - 1) { + if (AsciiBuffer[LineSize + 1] == CHAR_CARRIAGE_RETURN) { + Type = NewLineTypeLineFeedCarriageReturn; + } + } + + break; + } + } else { + if (UnicodeBuffer[LineSize] == CHAR_CARRIAGE_RETURN) { + Type = NewLineTypeCarriageReturn; + + // + // has LF following + // + if (LineSize < FileSize - 1) { + if (UnicodeBuffer[LineSize + 1] == CHAR_LINEFEED) { + Type = NewLineTypeCarriageReturnLineFeed; + } + } + + break; + } else if (UnicodeBuffer[LineSize] == CHAR_LINEFEED) { + Type = NewLineTypeLineFeed; + + // + // has CR following + // + if (LineSize < FileSize - 1) { + if (UnicodeBuffer[LineSize + 1] == CHAR_CARRIAGE_RETURN) { + Type = NewLineTypeLineFeedCarriageReturn; + } + } + + break; + } + } + // + // endif == ASCII + // + } + // + // end of for LineSize + // + // if the type is wrong, then exit + // + if (Type == NewLineTypeUnknown) { + // + // Now if Type is NewLineTypeUnknown, it should be file end + // + Type = NewLineTypeDefault; + } + + LineSizeBackup = LineSize; + + // + // create a new line + // + Line = FileBufferCreateLine (); + if (Line == NULL) { + SHELL_FREE_NON_NULL (Buffer); + return EFI_OUT_OF_RESOURCES; + } + // + // calculate file length + // + LineSize -= LoopVar1; + + // + // Unicode and one CHAR_NULL + // + SHELL_FREE_NON_NULL (Line->Buffer); + Line->Buffer = AllocateZeroPool (LineSize * 2 + 2); + + if (Line->Buffer == NULL) { + RemoveEntryList (&Line->Link); + return EFI_OUT_OF_RESOURCES; + } + // + // copy this line to Line->Buffer + // + for (LoopVar2 = 0; LoopVar2 < LineSize; LoopVar2++) { + if (FileBuffer.FileType == FileTypeAscii) { + Line->Buffer[LoopVar2] = (CHAR16) AsciiBuffer[LoopVar1]; + } else { + Line->Buffer[LoopVar2] = UnicodeBuffer[LoopVar1]; + } + + LoopVar1++; + } + // + // LoopVar1 now points to where CHAR_CARRIAGE_RETURN or CHAR_LINEFEED; + // + Line->Buffer[LineSize] = 0; + + Line->Size = LineSize; + Line->TotalSize = LineSize; + Line->Type = Type; + + if (Type == NewLineTypeCarriageReturnLineFeed || Type == NewLineTypeLineFeedCarriageReturn) { + LoopVar1++; + } + + // + // last character is a return, SO create a new line + // + if (((Type == NewLineTypeCarriageReturnLineFeed || Type == NewLineTypeLineFeedCarriageReturn) && LineSizeBackup == FileSize - 2) || + ((Type == NewLineTypeLineFeed || Type == NewLineTypeCarriageReturn) && LineSizeBackup == FileSize - 1) + ) { + Line = FileBufferCreateLine (); + if (Line == NULL) { + SHELL_FREE_NON_NULL (Buffer); + return EFI_OUT_OF_RESOURCES; + } + } + // + // end of if + // + } + // + // end of LoopVar1 + // + SHELL_FREE_NON_NULL (Buffer); + + } + // + // end of if CreateFile + // +Done: + + FileBuffer.DisplayPosition.Row = 2; + FileBuffer.DisplayPosition.Column = 1; + FileBuffer.LowVisibleRange.Row = 1; + FileBuffer.LowVisibleRange.Column = 1; + FileBuffer.FilePosition.Row = 1; + FileBuffer.FilePosition.Column = 1; + FileBuffer.MousePosition.Row = 2; + FileBuffer.MousePosition.Column = 1; + + if (!Recover) { + UnicodeBuffer = CatSPrint (NULL, L"%d Lines Read", FileBuffer.NumLines); + if (UnicodeBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + StatusBarSetStatusString (UnicodeBuffer); + FreePool (UnicodeBuffer); + } +/* + // + // check whether we have fs?: in filename + // + LoopVar1 = 0; + FSMappingPtr = NULL; + while (FileName[LoopVar1] != 0) { + if (FileName[LoopVar1] == L':') { + FSMappingPtr = &FileName[LoopVar1]; + break; + } + + LoopVar1++; + } + + if (FSMappingPtr == NULL) { + CurDir = ShellGetCurrentDir (NULL); + } else { + LoopVar1 = 0; + LoopVar2 = 0; + while (FileName[LoopVar1] != 0) { + if (FileName[LoopVar1] == L':') { + break; + } + + FSMapping[LoopVar2++] = FileName[LoopVar1]; + + LoopVar1++; + } + + FSMapping[LoopVar2] = 0; + CurDir = ShellGetCurrentDir (FSMapping); + } + + if (CurDir != NULL) { + for (LoopVar1 = 0; LoopVar1 < StrLen (CurDir) && CurDir[LoopVar1] != ':'; LoopVar1++); + + CurDir[LoopVar1] = 0; + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) ShellGetMap (CurDir); + FreePool (CurDir); + } else { + return EFI_LOAD_ERROR; + } + + Status = LibDevicePathToInterface ( + &gEfiSimpleFileSystemProtocolGuid, + DevicePath, + (VOID **) &Vol + ); + if (EFI_ERROR (Status)) { + return EFI_LOAD_ERROR; + } + + Status = Vol->OpenVolume (Vol, &RootFs); + if (EFI_ERROR (Status)) { + return EFI_LOAD_ERROR; + } + // + // Get volume information of file system + // + Size = SIZE_OF_EFI_FILE_SYSTEM_INFO + 100; + VolumeInfo = (EFI_FILE_SYSTEM_INFO *) AllocateZeroPool (Size); + Status = RootFs->GetInfo (RootFs, &gEfiFileSystemInfoGuid, &Size, VolumeInfo); + if (EFI_ERROR (Status)) { + RootFs->Close (RootFs); + return EFI_LOAD_ERROR; + } + + if (VolumeInfo->ReadOnly) { + StatusBarSetStatusString (L"WARNING: Volume Read Only"); + } + + FreePool (VolumeInfo); + RootFs->Close (RootFs); + } +// +*/ + // + // has line + // + if (FileBuffer.Lines != 0) { + FileBuffer.CurrentLine = CR (FileBuffer.ListHead->ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); + } else { + // + // create a dummy line + // + Line = FileBufferCreateLine (); + if (Line == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + FileBuffer.CurrentLine = Line; + } + + FileBuffer.FileModified = FALSE; + FileBufferNeedRefresh = TRUE; + FileBufferOnlyLineNeedRefresh = FALSE; + FileBufferMouseNeedRefresh = TRUE; + + + return EFI_SUCCESS; +} + +/** + According to FileBuffer.NewLineType & FileBuffer.FileType, + get the return buffer and size. + + @param[in] Type The type of line. + @param[out] Buffer The buffer to fill. + @param[out] Size The amount of the buffer used on return. +**/ +VOID +GetNewLine ( + IN CONST EE_NEWLINE_TYPE Type, + OUT CHAR8 *Buffer, + OUT UINT8 *Size + ) +{ + UINT8 NewLineSize; + + // + // give new line buffer, + // and will judge unicode or ascii + // + NewLineSize = 0; + + // + // not legal new line type + // + if (Type != NewLineTypeLineFeed && Type != NewLineTypeCarriageReturn && Type != NewLineTypeCarriageReturnLineFeed && Type != NewLineTypeLineFeedCarriageReturn) { + *Size = 0; + return ; + } + // + // use_cr: give 0x0d + // + if (Type == NewLineTypeCarriageReturn) { + if (MainEditor.FileBuffer->FileType == FileTypeUnicode) { + Buffer[0] = 0x0d; + Buffer[1] = 0; + NewLineSize = 2; + } else { + Buffer[0] = 0x0d; + NewLineSize = 1; + } + + *Size = NewLineSize; + return ; + } + // + // use_lf: give 0x0a + // + if (Type == NewLineTypeLineFeed) { + if (MainEditor.FileBuffer->FileType == FileTypeUnicode) { + Buffer[0] = 0x0a; + Buffer[1] = 0; + NewLineSize = 2; + } else { + Buffer[0] = 0x0a; + NewLineSize = 1; + } + + *Size = NewLineSize; + return ; + } + // + // use_crlf: give 0x0d 0x0a + // + if (Type == NewLineTypeCarriageReturnLineFeed) { + if (MainEditor.FileBuffer->FileType == FileTypeUnicode) { + Buffer[0] = 0x0d; + Buffer[1] = 0; + Buffer[2] = 0x0a; + Buffer[3] = 0; + + NewLineSize = 4; + } else { + Buffer[0] = 0x0d; + Buffer[1] = 0x0a; + NewLineSize = 2; + } + + *Size = NewLineSize; + return ; + } + // + // use_lfcr: give 0x0a 0x0d + // + if (Type == NewLineTypeLineFeedCarriageReturn) { + if (MainEditor.FileBuffer->FileType == FileTypeUnicode) { + Buffer[0] = 0x0a; + Buffer[1] = 0; + Buffer[2] = 0x0d; + Buffer[3] = 0; + + NewLineSize = 4; + } else { + Buffer[0] = 0x0a; + Buffer[1] = 0x0d; + NewLineSize = 2; + } + + *Size = NewLineSize; + return ; + } + +} + +/** + Change a Unicode string to an ASCII string. + + @param[in] UStr The Unicode string. + @param[in] Length The maximum size of AStr. + @param[out] AStr ASCII string to pass out. + + @return The actuall length. +**/ +UINTN +UnicodeToAscii ( + IN CONST CHAR16 *UStr, + IN CONST UINTN Length, + OUT CHAR8 *AStr + ) +{ + UINTN Index; + + // + // just buffer copy, not character copy + // + for (Index = 0; Index < Length; Index++) { + *AStr++ = (CHAR8) *UStr++; + } + + return Index; +} + +/** + Save lines in FileBuffer to disk + + @param[in] FileName The file name for writing. + + @retval EFI_SUCCESS Data was written. + @retval EFI_LOAD_ERROR + @retval EFI_OUT_OF_RESOURCES There were not enough resources to write the file. +**/ +EFI_STATUS +FileBufferSave ( + IN CONST CHAR16 *FileName + ) +{ + SHELL_FILE_HANDLE FileHandle; + LIST_ENTRY *Link; + EFI_EDITOR_LINE *Line; + CHAR16 *Str; + + EFI_STATUS Status; + UINTN Length; + UINTN NumLines; + CHAR8 NewLineBuffer[4]; + UINT8 NewLineSize; + + EFI_FILE_INFO *Info; + + UINT64 Attribute; + + EE_NEWLINE_TYPE Type; + + UINTN TotalSize; + // + // 2M + // + CHAR8 *Cache; + UINTN LeftSize; + UINTN Size; + CHAR8 *Ptr; + + Length = 0; + // + // 2M + // + TotalSize = 0x200000; + + Attribute = 0; + + + + // + // if is the old file + // + if (FileBuffer.FileName != NULL && StrCmp (FileName, FileBuffer.FileName) == 0) { + // + // file has not been modified + // + if (!FileBuffer.FileModified) { + return EFI_SUCCESS; + } + + // + // if file is read-only, set error + // + if (FileBuffer.ReadOnly) { + StatusBarSetStatusString (L"Read Only File Can Not Be Saved"); + return EFI_SUCCESS; + } + } + + Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0); + + if (!EFI_ERROR (Status)) { + Info = ShellGetFileInfo(FileHandle); + + if (Info != NULL && Info->Attribute & EFI_FILE_DIRECTORY) { + StatusBarSetStatusString (L"Directory Can Not Be Saved"); + ShellCloseFile (&FileHandle); + FreePool(Info); + return EFI_LOAD_ERROR; + } + + if (Info != NULL) { + Attribute = Info->Attribute & ~EFI_FILE_READ_ONLY; + FreePool(Info); + } + + // + // if file exits, so delete it + // + Status = ShellDeleteFile (&FileHandle); + if (EFI_ERROR (Status) || Status == EFI_WARN_DELETE_FAILURE) { + StatusBarSetStatusString (L"Write File Failed"); + return EFI_LOAD_ERROR; + } + } + + Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, Attribute); + + if (EFI_ERROR (Status)) { + StatusBarSetStatusString (L"Create File Failed"); + return EFI_LOAD_ERROR; + } + + // + // if file is Unicode file, write Unicode header to it. + // + if (FileBuffer.FileType == FileTypeUnicode) { + Length = 2; + Status = ShellWriteFile (FileHandle, &Length, (VOID*)&gUnicodeFileTag); + if (EFI_ERROR (Status)) { + ShellDeleteFile (&FileHandle); + return EFI_LOAD_ERROR; + } + } + + Cache = AllocateZeroPool (TotalSize); + if (Cache == NULL) { + ShellDeleteFile (&FileHandle); + return EFI_OUT_OF_RESOURCES; + } + + // + // write all the lines back to disk + // + NumLines = 0; + Type = NewLineTypeCarriageReturnLineFeed; + + Ptr = Cache; + LeftSize = TotalSize; + + for (Link = FileBuffer.ListHead->ForwardLink; Link != FileBuffer.ListHead; Link = Link->ForwardLink) { + Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); + + if (Line->Type != NewLineTypeDefault) { + Type = Line->Type; + } + // + // newline character is at most 4 bytes ( two Unicode characters ) + // + Length = 4; + if (Line->Buffer != NULL && Line->Size != 0) { + if (FileBuffer.FileType == FileTypeAscii) { + Length += Line->Size; + } else { + Length += (Line->Size * 2); + } + // + // end if FileTypeAscii + // + } + + // + // no cache room left, so write cache to disk + // + if (LeftSize < Length) { + Size = TotalSize - LeftSize; + Status = ShellWriteFile (FileHandle, &Size, Cache); + if (EFI_ERROR (Status)) { + ShellDeleteFile (&FileHandle); + FreePool (Cache); + return EFI_LOAD_ERROR; + } + Ptr = Cache; + LeftSize = TotalSize; + } + + if (Line->Buffer != NULL && Line->Size != 0) { + if (FileBuffer.FileType == FileTypeAscii) { + UnicodeToAscii (Line->Buffer, Line->Size, Ptr); + Length = Line->Size; + } else { + Length = (Line->Size * 2); + CopyMem (Ptr, (CHAR8 *) Line->Buffer, Length); + } + // + // end if FileTypeAscii + // + Ptr += Length; + LeftSize -= Length; + + } + // + // end of if Line -> Buffer != NULL && Line -> Size != 0 + // + // if not the last line , write return buffer to disk + // + if (Link->ForwardLink != FileBuffer.ListHead) { + GetNewLine (Type, NewLineBuffer, &NewLineSize); + CopyMem (Ptr, (CHAR8 *) NewLineBuffer, NewLineSize); + + Ptr += NewLineSize; + LeftSize -= NewLineSize; + } + + NumLines++; + } + + if (TotalSize != LeftSize) { + Size = TotalSize - LeftSize; + Status = ShellWriteFile (FileHandle, &Size, Cache); + if (EFI_ERROR (Status)) { + ShellDeleteFile (&FileHandle); + FreePool (Cache); + return EFI_LOAD_ERROR; + } + } + + FreePool (Cache); + + ShellCloseFile(&FileHandle); + + FileBuffer.FileModified = FALSE; + + // + // set status string + // + Str = CatSPrint (NULL, L"%d Lines Written", NumLines); + if (Str == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + StatusBarSetStatusString (Str); + SHELL_FREE_NON_NULL (Str); + + // + // now everything is ready , you can set the new file name to filebuffer + // + if (FileName != NULL && FileBuffer.FileName != NULL && StrCmp (FileName, FileBuffer.FileName) != 0) { + // + // not the same + // + FileBufferSetFileName (FileName); + if (FileBuffer.FileName == NULL) { + ShellDeleteFile (&FileHandle); + return EFI_OUT_OF_RESOURCES; + } + } + + FileBuffer.ReadOnly = FALSE; + return EFI_SUCCESS; +} + +/** + Scroll cursor to left 1 character position. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +FileBufferScrollLeft ( + VOID + ) +{ + EFI_EDITOR_LINE *Line; + UINTN FRow; + UINTN FCol; + + Line = FileBuffer.CurrentLine; + + FRow = FileBuffer.FilePosition.Row; + FCol = FileBuffer.FilePosition.Column; + + // + // if already at start of this line, so move to the end of previous line + // + if (FCol <= 1) { + // + // has previous line + // + if (Line->Link.BackLink != FileBuffer.ListHead) { + FRow--; + Line = CR (Line->Link.BackLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); + FCol = Line->Size + 1; + } else { + return EFI_SUCCESS; + } + } else { + // + // if not at start of this line, just move to previous column + // + FCol--; + } + + FileBufferMovePosition (FRow, FCol); + + return EFI_SUCCESS; +} + +/** + Delete a char in line + + @param[in, out] Line The line to delete in. + @param[in] Pos Position to delete the char at ( start from 0 ). +**/ +VOID +LineDeleteAt ( + IN OUT EFI_EDITOR_LINE *Line, + IN UINTN Pos + ) +{ + UINTN Index; + + // + // move the latter characters front + // + for (Index = Pos - 1; Index < Line->Size; Index++) { + Line->Buffer[Index] = Line->Buffer[Index + 1]; + } + + Line->Size--; +} + +/** + Concatenate Src into Dest. + + @param[in, out] Dest Destination string + @param[in] Src Src String. +**/ +VOID +LineCat ( + IN OUT EFI_EDITOR_LINE *Dest, + IN EFI_EDITOR_LINE *Src + ) +{ + CHAR16 *Str; + UINTN Size; + + Size = Dest->Size; + + Dest->Buffer[Size] = 0; + + // + // concatenate the two strings + // + Str = CatSPrint (NULL, L"%s%s", Dest->Buffer, Src->Buffer); + if (Str == NULL) { + Dest->Buffer = NULL; + return ; + } + + Dest->Size = Size + Src->Size; + Dest->TotalSize = Dest->Size; + + FreePool (Dest->Buffer); + FreePool (Src->Buffer); + + // + // put str to dest->buffer + // + Dest->Buffer = Str; +} + +/** + Delete the previous character. + + @retval EFI_SUCCESS The delete was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +FileBufferDoBackspace ( + VOID + ) +{ + EFI_EDITOR_LINE *Line; + EFI_EDITOR_LINE *End; + LIST_ENTRY *Link; + UINTN FileColumn; + + FileColumn = FileBuffer.FilePosition.Column; + + Line = FileBuffer.CurrentLine; + + // + // the first column + // + if (FileColumn == 1) { + // + // the first row + // + if (FileBuffer.FilePosition.Row == 1) { + return EFI_SUCCESS; + } + + FileBufferScrollLeft (); + + Line = FileBuffer.CurrentLine; + Link = Line->Link.ForwardLink; + End = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); + + // + // concatenate this line with previous line + // + LineCat (Line, End); + if (Line->Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // remove End from line list + // + RemoveEntryList (&End->Link); + FreePool (End); + + FileBuffer.NumLines--; + + FileBufferNeedRefresh = TRUE; + FileBufferOnlyLineNeedRefresh = FALSE; + + } else { + // + // just delete the previous character + // + LineDeleteAt (Line, FileColumn - 1); + FileBufferScrollLeft (); + FileBufferOnlyLineNeedRefresh = TRUE; + } + + if (!FileBuffer.FileModified) { + FileBuffer.FileModified = TRUE; + } + + return EFI_SUCCESS; +} + +/** + Add a return into line at current position. + + @retval EFI_SUCCESS The insetrion of the character was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +FileBufferDoReturn ( + VOID + ) +{ + EFI_EDITOR_LINE *Line; + EFI_EDITOR_LINE *NewLine; + UINTN FileColumn; + UINTN Index; + CHAR16 *Buffer; + UINTN Row; + UINTN Col; + + FileBufferNeedRefresh = TRUE; + FileBufferOnlyLineNeedRefresh = FALSE; + + Line = FileBuffer.CurrentLine; + + FileColumn = FileBuffer.FilePosition.Column; + + NewLine = AllocateZeroPool (sizeof (EFI_EDITOR_LINE)); + if (NewLine == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + NewLine->Signature = LINE_LIST_SIGNATURE; + NewLine->Size = Line->Size - FileColumn + 1; + NewLine->TotalSize = NewLine->Size; + NewLine->Buffer = CatSPrint (NULL, L"\0"); + if (NewLine->Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + NewLine->Type = NewLineTypeDefault; + + if (NewLine->Size > 0) { + // + // UNICODE + CHAR_NULL + // + Buffer = AllocateZeroPool (2 * (NewLine->Size + 1)); + if (Buffer == NULL) { + FreePool (NewLine->Buffer); + FreePool (NewLine); + return EFI_OUT_OF_RESOURCES; + } + + FreePool (NewLine->Buffer); + + NewLine->Buffer = Buffer; + + for (Index = 0; Index < NewLine->Size; Index++) { + NewLine->Buffer[Index] = Line->Buffer[Index + FileColumn - 1]; + } + + NewLine->Buffer[NewLine->Size] = CHAR_NULL; + + Line->Buffer[FileColumn - 1] = CHAR_NULL; + Line->Size = FileColumn - 1; + } + // + // increase NumLines + // + FileBuffer.NumLines++; + + // + // insert it into the correct position of line list + // + NewLine->Link.BackLink = &(Line->Link); + NewLine->Link.ForwardLink = Line->Link.ForwardLink; + Line->Link.ForwardLink->BackLink = &(NewLine->Link); + Line->Link.ForwardLink = &(NewLine->Link); + + // + // move cursor to the start of next line + // + Row = FileBuffer.FilePosition.Row + 1; + Col = 1; + + FileBufferMovePosition (Row, Col); + + // + // set file is modified + // + if (!FileBuffer.FileModified) { + FileBuffer.FileModified = TRUE; + } + + return EFI_SUCCESS; +} + +/** + Delete current character from current line. This is the effect caused + by the 'del' key. + + @retval EFI_SUCCESS +**/ +EFI_STATUS +FileBufferDoDelete ( + VOID + ) +{ + EFI_EDITOR_LINE *Line; + EFI_EDITOR_LINE *Next; + LIST_ENTRY *Link; + UINTN FileColumn; + + Line = FileBuffer.CurrentLine; + FileColumn = FileBuffer.FilePosition.Column; + + // + // the last column + // + if (FileColumn >= Line->Size + 1) { + // + // the last line + // + if (Line->Link.ForwardLink == FileBuffer.ListHead) { + return EFI_SUCCESS; + } + // + // since last character, + // so will add the next line to this line + // + Link = Line->Link.ForwardLink; + Next = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); + LineCat (Line, Next); + if (Line->Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + RemoveEntryList (&Next->Link); + FreePool (Next); + + FileBuffer.NumLines--; + + FileBufferNeedRefresh = TRUE; + FileBufferOnlyLineNeedRefresh = FALSE; + + } else { + // + // just delete current character + // + LineDeleteAt (Line, FileColumn); + FileBufferOnlyLineNeedRefresh = TRUE; + } + + if (!FileBuffer.FileModified) { + FileBuffer.FileModified = TRUE; + } + + return EFI_SUCCESS; +} + +/** + Scroll cursor to right 1 character. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +FileBufferScrollRight ( + VOID + ) +{ + EFI_EDITOR_LINE *Line; + UINTN FRow; + UINTN FCol; + + Line = FileBuffer.CurrentLine; + if (Line->Buffer == NULL) { + return EFI_SUCCESS; + } + + FRow = FileBuffer.FilePosition.Row; + FCol = FileBuffer.FilePosition.Column; + + // + // if already at end of this line, scroll it to the start of next line + // + if (FCol > Line->Size) { + // + // has next line + // + if (Line->Link.ForwardLink != FileBuffer.ListHead) { + FRow++; + FCol = 1; + } else { + return EFI_SUCCESS; + } + } else { + // + // if not at end of this line, just move to next column + // + FCol++; + } + + FileBufferMovePosition (FRow, FCol); + + return EFI_SUCCESS; +} + +/** + Insert a char into line + + + @param[in] Line The line to insert into. + @param[in] Char The char to insert. + @param[in] Pos The position to insert the char at ( start from 0 ). + @param[in] StrSize The current string size ( include CHAR_NULL ),unit is Unicode character. + + @return The new string size ( include CHAR_NULL ) ( unit is Unicode character ). +**/ +UINTN +LineStrInsert ( + IN EFI_EDITOR_LINE *Line, + IN CHAR16 Char, + IN UINTN Pos, + IN UINTN StrSize + ) +{ + UINTN Index; + CHAR16 *TempStringPtr; + CHAR16 *Str; + + Index = (StrSize) * 2; + + Str = Line->Buffer; + + // + // do not have free space + // + if (Line->TotalSize <= Line->Size) { + Str = ReallocatePool (Index, Index + 16, Str); + if (Str == NULL) { + return 0; + } + + Line->TotalSize += 8; + } + // + // move the later part of the string one character right + // + TempStringPtr = Str; + for (Index = StrSize; Index > Pos; Index--) { + TempStringPtr[Index] = TempStringPtr[Index - 1]; + } + // + // insert char into it. + // + TempStringPtr[Index] = Char; + + Line->Buffer = Str; + Line->Size++; + + return StrSize + 1; +} + +/** + Add a character to the current line. + + @param[in] Char The Character to input. + + @retval EFI_SUCCESS The input was succesful. +**/ +EFI_STATUS +FileBufferAddChar ( + IN CHAR16 Char + ) +{ + EFI_EDITOR_LINE *Line; + UINTN FilePos; + + Line = FileBuffer.CurrentLine; + + // + // only needs to refresh current line + // + FileBufferOnlyLineNeedRefresh = TRUE; + + // + // when is insert mode, or cursor is at end of this line, + // so insert this character + // or replace the character. + // + FilePos = FileBuffer.FilePosition.Column - 1; + if (FileBuffer.ModeInsert || FilePos + 1 > Line->Size) { + LineStrInsert (Line, Char, FilePos, Line->Size + 1); + } else { + Line->Buffer[FilePos] = Char; + } + // + // move cursor to right + // + FileBufferScrollRight (); + + if (!FileBuffer.FileModified) { + FileBuffer.FileModified = TRUE; + } + + return EFI_SUCCESS; +} + +/** + Handles inputs from characters (ASCII key + Backspace + return) + + @param[in] Char The input character. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_LOAD_ERROR There was an error. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +FileBufferDoCharInput ( + IN CONST CHAR16 Char + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + switch (Char) { + case CHAR_NULL: + break; + + case CHAR_BACKSPACE: + Status = FileBufferDoBackspace (); + break; + + case CHAR_TAB: + // + // Tabs are ignored + // + break; + + case CHAR_LINEFEED: + case CHAR_CARRIAGE_RETURN: + Status = FileBufferDoReturn (); + break; + + default: + // + // DEAL WITH ASCII CHAR, filter out thing like ctrl+f + // + if (Char > 127 || Char < 32) { + Status = StatusBarSetStatusString (L"Unknown Command"); + } else { + Status = FileBufferAddChar (Char); + } + + break; + + } + + return Status; +} + +/** + Scroll cursor to the next line. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +FileBufferScrollDown ( + VOID + ) +{ + EFI_EDITOR_LINE *Line; + UINTN FRow; + UINTN FCol; + + Line = FileBuffer.CurrentLine; + if (Line->Buffer == NULL) { + return EFI_SUCCESS; + } + + FRow = FileBuffer.FilePosition.Row; + FCol = FileBuffer.FilePosition.Column; + + // + // has next line + // + if (Line->Link.ForwardLink != FileBuffer.ListHead) { + FRow++; + Line = CR (Line->Link.ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); + + // + // if the next line is not that long, so move to end of next line + // + if (FCol > Line->Size) { + FCol = Line->Size + 1; + } + + } else { + return EFI_SUCCESS; + } + + FileBufferMovePosition (FRow, FCol); + + return EFI_SUCCESS; +} + +/** + Scroll the cursor to previous line. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +FileBufferScrollUp ( + VOID + ) +{ + EFI_EDITOR_LINE *Line; + UINTN FRow; + UINTN FCol; + + Line = FileBuffer.CurrentLine; + + FRow = FileBuffer.FilePosition.Row; + FCol = FileBuffer.FilePosition.Column; + + // + // has previous line + // + if (Line->Link.BackLink != FileBuffer.ListHead) { + FRow--; + Line = CR (Line->Link.BackLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); + + // + // if previous line is not that long, so move to the end of previous line + // + if (FCol > Line->Size) { + FCol = Line->Size + 1; + } + + } else { + return EFI_SUCCESS; + } + + FileBufferMovePosition (FRow, FCol); + + return EFI_SUCCESS; +} + +/** + Scroll cursor to next page. + + @retval EFI_SUCCESS The operation wa successful. +**/ +EFI_STATUS +FileBufferPageDown ( + VOID + ) +{ + EFI_EDITOR_LINE *Line; + UINTN FRow; + UINTN FCol; + UINTN Gap; + + Line = FileBuffer.CurrentLine; + + FRow = FileBuffer.FilePosition.Row; + FCol = FileBuffer.FilePosition.Column; + + // + // has next page + // + if (FileBuffer.NumLines >= FRow + (MainEditor.ScreenSize.Row - 2)) { + Gap = (MainEditor.ScreenSize.Row - 2); + } else { + // + // MOVE CURSOR TO LAST LINE + // + Gap = FileBuffer.NumLines - FRow; + } + // + // get correct line + // + Line = MoveLine (Gap); + + // + // if that line, is not that long, so move to the end of that line + // + if (Line != NULL && FCol > Line->Size) { + FCol = Line->Size + 1; + } + + FRow += Gap; + + FileBufferMovePosition (FRow, FCol); + + return EFI_SUCCESS; +} + +/** + Scroll cursor to previous screen. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +FileBufferPageUp ( + VOID + ) +{ + EFI_EDITOR_LINE *Line; + UINTN FRow; + UINTN FCol; + UINTN Gap; + INTN Retreat; + + Line = FileBuffer.CurrentLine; + + FRow = FileBuffer.FilePosition.Row; + FCol = FileBuffer.FilePosition.Column; + + // + // has previous page + // + if (FRow > (MainEditor.ScreenSize.Row - 2)) { + Gap = (MainEditor.ScreenSize.Row - 2); + } else { + // + // the first line of file will displayed on the first line of screen + // + Gap = FRow - 1; + } + + Retreat = Gap; + Retreat = -Retreat; + + // + // get correct line + // + Line = MoveLine (Retreat); + + // + // if that line is not that long, so move to the end of that line + // + if (Line != NULL && FCol > Line->Size) { + FCol = Line->Size + 1; + } + + FRow -= Gap; + + FileBufferMovePosition (FRow, FCol); + + return EFI_SUCCESS; +} + +/** + Scroll cursor to end of the current line. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +FileBufferEnd ( + VOID + ) +{ + EFI_EDITOR_LINE *Line; + UINTN FRow; + UINTN FCol; + + Line = FileBuffer.CurrentLine; + + FRow = FileBuffer.FilePosition.Row; + + // + // goto the last column of the line + // + FCol = Line->Size + 1; + + FileBufferMovePosition (FRow, FCol); + + return EFI_SUCCESS; +} + +/** + Dispatch input to different handler + @param[in] Key The input key. One of: + ASCII KEY + Backspace/Delete + Return + Direction key: up/down/left/right/pgup/pgdn + Home/End + INS + + @retval EFI_SUCCESS The dispatch was done successfully. + @retval EFI_LOAD_ERROR The dispatch was not successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +FileBufferHandleInput ( + IN CONST EFI_INPUT_KEY *Key + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + switch (Key->ScanCode) { + // + // ordinary key input + // + case SCAN_NULL: + if (!FileBuffer.ReadOnly) { + Status = FileBufferDoCharInput (Key->UnicodeChar); + } else { + Status = StatusBarSetStatusString (L"Read Only File Can Not Be Modified"); + } + + break; + + // + // up arrow + // + case SCAN_UP: + Status = FileBufferScrollUp (); + break; + + // + // down arrow + // + case SCAN_DOWN: + Status = FileBufferScrollDown (); + break; + + // + // right arrow + // + case SCAN_RIGHT: + Status = FileBufferScrollRight (); + break; + + // + // left arrow + // + case SCAN_LEFT: + Status = FileBufferScrollLeft (); + break; + + // + // page up + // + case SCAN_PAGE_UP: + Status = FileBufferPageUp (); + break; + + // + // page down + // + case SCAN_PAGE_DOWN: + Status = FileBufferPageDown (); + break; + + // + // delete + // + case SCAN_DELETE: + if (!FileBuffer.ReadOnly) { + Status = FileBufferDoDelete (); + } else { + Status = StatusBarSetStatusString (L"Read Only File Can Not Be Modified"); + } + + break; + + // + // home + // + case SCAN_HOME: + FileBufferMovePosition (FileBuffer.FilePosition.Row, 1); + Status = EFI_SUCCESS; + break; + + // + // end + // + case SCAN_END: + Status = FileBufferEnd (); + break; + + // + // insert + // + case SCAN_INSERT: + FileBuffer.ModeInsert = (BOOLEAN)!FileBuffer.ModeInsert; + Status = EFI_SUCCESS; + break; + + default: + Status = StatusBarSetStatusString (L"Unknown Command"); + break; + } + + return Status; +} + +/** + Check user specified FileRow is above current screen. + + @param[in] FileRow The row of file position ( start from 1 ). + + @retval TRUE It is above the current screen. + @retval FALSE It is not above the current screen. +**/ +BOOLEAN +AboveCurrentScreen ( + IN UINTN FileRow + ) +{ + // + // if is to the above of the screen + // + if (FileRow < FileBuffer.LowVisibleRange.Row) { + return TRUE; + } + + return FALSE; +} + +/** + Check user specified FileRow is under current screen. + + @param[in] FileRow The row of file position ( start from 1 ). + + @retval TRUE It is under the current screen. + @retval FALSE It is not under the current screen. +**/ +BOOLEAN +UnderCurrentScreen ( + IN UINTN FileRow + ) +{ + // + // if is to the under of the screen + // + if (FileRow > FileBuffer.LowVisibleRange.Row + (MainEditor.ScreenSize.Row - 2) - 1) { + return TRUE; + } + + return FALSE; +} + +/** + Check user specified FileCol is left to current screen. + + @param[in] FileCol The column of file position ( start from 1 ). + + @retval TRUE It is to the left. + @retval FALSE It is not to the left. +**/ +BOOLEAN +LeftCurrentScreen ( + IN UINTN FileCol + ) +{ + // + // if is to the left of the screen + // + if (FileCol < FileBuffer.LowVisibleRange.Column) { + return TRUE; + } + + return FALSE; +} + +/** + Check user specified FileCol is right to current screen. + + @param[in] FileCol The column of file position ( start from 1 ). + + @retval TRUE It is to the right. + @retval FALSE It is not to the right. +**/ +BOOLEAN +RightCurrentScreen ( + IN UINTN FileCol + ) +{ + // + // if is to the right of the screen + // + if (FileCol > FileBuffer.LowVisibleRange.Column + MainEditor.ScreenSize.Column - 1) { + return TRUE; + } + + return FALSE; +} + +/** + Advance/Retreat lines and set CurrentLine in FileBuffer to it + + @param[in] Count The line number to advance/retreat + >0 : advance + <0: retreat + + @retval NULL An error occurred. + @return The line after advance/retreat. +**/ +EFI_EDITOR_LINE * +MoveCurrentLine ( + IN INTN Count + ) +{ + EFI_EDITOR_LINE *Line; + UINTN AbsCount; + + if (Count <= 0) { + AbsCount = (UINTN)ABS(Count); + Line = InternalEditorMiscLineRetreat (AbsCount,MainEditor.FileBuffer->CurrentLine,MainEditor.FileBuffer->ListHead); + } else { + Line = InternalEditorMiscLineAdvance ((UINTN)Count,MainEditor.FileBuffer->CurrentLine,MainEditor.FileBuffer->ListHead); + } + + if (Line == NULL) { + return NULL; + } + + MainEditor.FileBuffer->CurrentLine = Line; + + return Line; +} + +/** + According to cursor's file position, adjust screen display + + @param[in] NewFilePosRow The row of file position ( start from 1 ). + @param[in] NewFilePosCol The column of file position ( start from 1 ). +**/ +VOID +FileBufferMovePosition ( + IN CONST UINTN NewFilePosRow, + IN CONST UINTN NewFilePosCol + ) +{ + INTN RowGap; + INTN ColGap; + UINTN Abs; + BOOLEAN Above; + BOOLEAN Under; + BOOLEAN Right; + BOOLEAN Left; + + // + // CALCULATE gap between current file position and new file position + // + RowGap = NewFilePosRow - FileBuffer.FilePosition.Row; + ColGap = NewFilePosCol - FileBuffer.FilePosition.Column; + + Under = UnderCurrentScreen (NewFilePosRow); + Above = AboveCurrentScreen (NewFilePosRow); + // + // if is below current screen + // + if (Under) { + // + // display row will be unchanged + // + FileBuffer.FilePosition.Row = NewFilePosRow; + } else { + if (Above) { + // + // has enough above line, so display row unchanged + // not has enough above lines, so the first line is at the + // first display line + // + if (NewFilePosRow < (FileBuffer.DisplayPosition.Row - 1)) { + FileBuffer.DisplayPosition.Row = NewFilePosRow + 1; + } + + FileBuffer.FilePosition.Row = NewFilePosRow; + } else { + // + // in current screen + // + FileBuffer.FilePosition.Row = NewFilePosRow; + if (RowGap < 0) { + Abs = (UINTN)ABS(RowGap); + FileBuffer.DisplayPosition.Row -= Abs; + } else { + FileBuffer.DisplayPosition.Row += RowGap; + } + } + } + + FileBuffer.LowVisibleRange.Row = FileBuffer.FilePosition.Row - (FileBuffer.DisplayPosition.Row - 2); + + Right = RightCurrentScreen (NewFilePosCol); + Left = LeftCurrentScreen (NewFilePosCol); + + // + // if right to current screen + // + if (Right) { + // + // display column will be changed to end + // + FileBuffer.DisplayPosition.Column = MainEditor.ScreenSize.Column; + FileBuffer.FilePosition.Column = NewFilePosCol; + } else { + if (Left) { + // + // has enough left characters , so display row unchanged + // not has enough left characters, + // so the first character is at the first display column + // + if (NewFilePosCol < (FileBuffer.DisplayPosition.Column)) { + FileBuffer.DisplayPosition.Column = NewFilePosCol; + } + + FileBuffer.FilePosition.Column = NewFilePosCol; + } else { + // + // in current screen + // + FileBuffer.FilePosition.Column = NewFilePosCol; + if (ColGap < 0) { + Abs = (UINTN)(-ColGap); + FileBuffer.DisplayPosition.Column -= Abs; + } else { + FileBuffer.DisplayPosition.Column += ColGap; + } + } + } + + FileBuffer.LowVisibleRange.Column = FileBuffer.FilePosition.Column - (FileBuffer.DisplayPosition.Column - 1); + + // + // let CurrentLine point to correct line; + // + FileBuffer.CurrentLine = MoveCurrentLine (RowGap); + +} + +/** + Cut current line out and return a pointer to it. + + @param[out] CutLine Upon a successful return pointer to the pointer to + the allocated cut line. + + @retval EFI_SUCCESS The cut was successful. + @retval EFI_NOT_FOUND There was no selection to cut. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +FileBufferCutLine ( + OUT EFI_EDITOR_LINE **CutLine + ) +{ + EFI_EDITOR_LINE *Line; + EFI_EDITOR_LINE *NewLine; + UINTN Row; + UINTN Col; + + 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; +} + diff --git a/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/FileBuffer.h b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/FileBuffer.h new file mode 100644 index 000000000..6800e4be3 --- /dev/null +++ b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/FileBuffer.h @@ -0,0 +1,240 @@ +/** @file + Declares filebuffer interface functions. + + Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _LIB_FILE_BUFFER_H_ +#define _LIB_FILE_BUFFER_H_ + +#include "TextEditorTypes.h" + +/** + 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 + ); + +/** + Cleanup function for FileBuffer. + + @retval EFI_SUCCESS The cleanup was successful. +**/ +EFI_STATUS +FileBufferCleanup ( + VOID + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Set the cursor position according to FileBuffer.DisplayPosition. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +FileBufferRestorePosition ( + VOID + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 ( + CONST CHAR16 *FileName + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Set the modified state to TRUE. +**/ +VOID +FileBufferSetModified ( + VOID + ); + +#endif diff --git a/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/MainTextEditor.c b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/MainTextEditor.c new file mode 100644 index 000000000..a9423e0d1 --- /dev/null +++ b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/MainTextEditor.c @@ -0,0 +1,1974 @@ +/** @file + Implements editor interface functions. + + Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "TextEditor.h" +#include "EditStatusBar.h" +#include "EditInputBar.h" +#include "EditMenuBar.h" + +// +// the first time editor launch +// +BOOLEAN EditorFirst; + +// +// it's time editor should exit +// +BOOLEAN EditorExit; + +BOOLEAN EditorMouseAction; + +extern EFI_EDITOR_FILE_BUFFER FileBuffer; + +extern BOOLEAN FileBufferNeedRefresh; + +extern BOOLEAN FileBufferOnlyLineNeedRefresh; + +extern BOOLEAN FileBufferMouseNeedRefresh; + +extern EFI_EDITOR_FILE_BUFFER FileBufferBackupVar; + +EFI_EDITOR_GLOBAL_EDITOR MainEditor; + + +/** + Load a file from disk to editor + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_LOAD_ERROR A load error occurred. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +MainCommandOpenFile ( + VOID + ); + +/** + Switch a file from ASCII to UNICODE or vise-versa. + + @retval EFI_SUCCESS The switch was ok or a warning was presented. +**/ +EFI_STATUS +MainCommandSwitchFileType ( + VOID + ); + +/** + move cursor to specified lines + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +MainCommandGotoLine ( + VOID + ); + +/** + Save current file to disk, you can save to current file name or + save to another file name. + + @retval EFI_SUCCESS The file was saved correctly. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_LOAD_ERROR A file access error occurred. +**/ +EFI_STATUS +MainCommandSaveFile ( + VOID + ); + +/** + Show help information for the editor. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +MainCommandDisplayHelp ( + VOID + ); + +/** + exit editor + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainCommandExit ( + VOID + ); + +/** + search string in file buffer + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainCommandSearch ( + VOID + ); + +/** + search string in file buffer, and replace it with another str + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainCommandSearchReplace ( + VOID + ); + +/** + cut current line to clipboard + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainCommandCutLine ( + VOID + ); + +/** + paste line to file buffer. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainCommandPasteLine ( + VOID + ); + +/** + Help info that will be displayed. +**/ +EFI_STRING_ID MainMenuHelpInfo[] = { + STRING_TOKEN(STR_EDIT_HELP_TITLE), + STRING_TOKEN(STR_EDIT_HELP_BLANK), + STRING_TOKEN(STR_EDIT_HELP_LIST_TITLE), + STRING_TOKEN(STR_EDIT_HELP_DIV), + STRING_TOKEN(STR_EDIT_HELP_GO_TO_LINE), + STRING_TOKEN(STR_EDIT_HELP_SAVE_FILE), + STRING_TOKEN(STR_EDIT_HELP_EXIT), + STRING_TOKEN(STR_EDIT_HELP_SEARCH), + STRING_TOKEN(STR_EDIT_HELP_SEARCH_REPLACE), + STRING_TOKEN(STR_EDIT_HELP_CUT_LINE), + STRING_TOKEN(STR_EDIT_HELP_PASTE_LINE), + STRING_TOKEN(STR_EDIT_HELP_OPEN_FILE), + STRING_TOKEN(STR_EDIT_HELP_FILE_TYPE), + STRING_TOKEN(STR_EDIT_HELP_BLANK), + STRING_TOKEN(STR_EDIT_HELP_EXIT_HELP), + STRING_TOKEN(STR_EDIT_HELP_BLANK), + STRING_TOKEN(STR_EDIT_HELP_BLANK), + STRING_TOKEN(STR_EDIT_HELP_BLANK), + STRING_TOKEN(STR_EDIT_HELP_BLANK), + STRING_TOKEN(STR_EDIT_HELP_BLANK), + STRING_TOKEN(STR_EDIT_HELP_BLANK), + STRING_TOKEN(STR_EDIT_HELP_BLANK), + STRING_TOKEN(STR_EDIT_HELP_DIV), +0 +}; + +MENU_ITEM_FUNCTION MainControlBasedMenuFunctions[] = { + NULL, + NULL, /* Ctrl - A */ + NULL, /* Ctrl - B */ + NULL, /* Ctrl - C */ + NULL, /* Ctrl - D */ + MainCommandDisplayHelp, /* Ctrl - E */ + MainCommandSearch, /* Ctrl - F */ + MainCommandGotoLine, /* Ctrl - G */ + NULL, /* Ctrl - H */ + NULL, /* Ctrl - I */ + NULL, /* Ctrl - J */ + MainCommandCutLine, /* Ctrl - K */ + NULL, /* Ctrl - L */ + NULL, /* Ctrl - M */ + NULL, /* Ctrl - N */ + MainCommandOpenFile, /* Ctrl - O */ + NULL, /* Ctrl - P */ + MainCommandExit, /* Ctrl - Q */ + MainCommandSearchReplace, /* Ctrl - R */ + MainCommandSaveFile, /* Ctrl - S */ + MainCommandSwitchFileType, /* Ctrl - T */ + MainCommandPasteLine, /* Ctrl - U */ + NULL, /* Ctrl - V */ + NULL, /* Ctrl - W */ + NULL, /* Ctrl - X */ + NULL, /* Ctrl - Y */ + NULL, /* Ctrl - Z */ +}; + +EDITOR_MENU_ITEM MainMenuItems[] = { + { + STRING_TOKEN(STR_EDIT_LIBMENUBAR_GO_TO_LINE), + STRING_TOKEN(STR_EDIT_LIBMENUBAR_F1), + MainCommandGotoLine + }, + { + STRING_TOKEN(STR_EDIT_LIBMENUBAR_SAVE_FILE), + STRING_TOKEN(STR_EDIT_LIBMENUBAR_F2), + MainCommandSaveFile + }, + { + STRING_TOKEN(STR_EDIT_LIBMENUBAR_EXIT), + STRING_TOKEN(STR_EDIT_LIBMENUBAR_F3), + MainCommandExit + }, + + { + STRING_TOKEN(STR_EDIT_LIBMENUBAR_SEARCH), + STRING_TOKEN(STR_EDIT_LIBMENUBAR_F4), + MainCommandSearch + }, + { + STRING_TOKEN(STR_EDIT_LIBMENUBAR_SEARCH_REPLACE), + STRING_TOKEN(STR_EDIT_LIBMENUBAR_F5), + MainCommandSearchReplace + }, + { + STRING_TOKEN(STR_EDIT_LIBMENUBAR_CUT_LINE), + STRING_TOKEN(STR_EDIT_LIBMENUBAR_F6), + MainCommandCutLine + }, + { + STRING_TOKEN(STR_EDIT_LIBMENUBAR_PASTE_LINE), + STRING_TOKEN(STR_EDIT_LIBMENUBAR_F7), + MainCommandPasteLine + }, + + { + STRING_TOKEN(STR_EDIT_LIBMENUBAR_OPEN_FILE), + STRING_TOKEN(STR_EDIT_LIBMENUBAR_F8), + MainCommandOpenFile + }, + { + STRING_TOKEN(STR_EDIT_LIBMENUBAR_FILE_TYPE), + STRING_TOKEN(STR_EDIT_LIBMENUBAR_F9), + MainCommandSwitchFileType + }, + { + STRING_TOKEN(STR_EDIT_LIBMENUBAR_FILE_TYPE), + STRING_TOKEN(STR_EDIT_LIBMENUBAR_F11), + MainCommandSwitchFileType + }, + + { + 0, + 0, + NULL + } +}; + + +/** + Load a file from disk to editor + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_LOAD_ERROR A load error occurred. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +MainCommandOpenFile ( + VOID + ) +{ + BOOLEAN Done; + EFI_STATUS Status; + + // + // This command will open a file from current working directory. + // Read-only file can also be opened. But it can not be modified. + // Below is the scenario of Open File command: + // 1.IF currently opened file has not been modIFied, directly go to step . + // IF currently opened file has been modified, + // an Input Bar will be prompted as : + // "File Modified. Save ( Yes/No/Cancel) ?" + // IF user press 'y' or 'Y', currently opened file will be saved. + // IF user press 'n' or 'N', currently opened file will + // not be saved. + // IF user press 'c' or 'C' or ESC, Open File command ends and + // currently opened file is still opened. + // + // 2. An Input Bar will be prompted as : "File Name to Open: " + // IF user press ESC, Open File command ends and + // currently opened file is still opened. + // Any other inputs with a Return will + // cause currently opened file close. + // + // 3. IF user input file name is an existing file , this file will be read + // and opened. + // IF user input file name is a new file, this file will be created + // and opened. This file's type ( UNICODE or ASCII ) is the same + // with the old file. + // if current file is modified, so you need to choose + // whether to save it first. + // + if (MainEditor.FileBuffer->FileModified) { + + Status = InputBarSetPrompt (L"File modified. Save (Yes/No/Cancel) ? "); + if (EFI_ERROR (Status)) { + return Status; + } + // + // the answer is just one character + // + Status = InputBarSetStringSize (1); + if (EFI_ERROR (Status)) { + return Status; + } + // + // loop for user's answer + // valid answer is just 'y' 'Y', 'n' 'N', 'c' 'C' + // + Done = FALSE; + while (!Done) { + Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column); + StatusBarSetRefresh(); + + // + // ESC pressed + // + if (Status == EFI_NOT_READY) { + return EFI_SUCCESS; + } + + switch (InputBarGetString()[0]) { + case L'y': + case L'Y': + // + // want to save this file first + // + Status = FileBufferSave (MainEditor.FileBuffer->FileName); + if (EFI_ERROR (Status)) { + return Status; + } + + MainTitleBarRefresh (MainEditor.FileBuffer->FileName, MainEditor.FileBuffer->FileType, MainEditor.FileBuffer->ReadOnly, MainEditor.FileBuffer->FileModified, MainEditor.ScreenSize.Column, MainEditor.ScreenSize.Row, 0, 0); + FileBufferRestorePosition (); + Done = TRUE; + break; + + case L'n': + case L'N': + // + // the file won't be saved + // + Done = TRUE; + break; + + case L'c': + case L'C': + return EFI_SUCCESS; + } + } + } + // + // TO get the open file name + // + Status = InputBarSetPrompt (L"File Name to Open: "); + if (EFI_ERROR (Status)) { + FileBufferRead (MainEditor.FileBuffer->FileName, TRUE); + return Status; + } + + Status = InputBarSetStringSize (100); + if (EFI_ERROR (Status)) { + FileBufferRead (MainEditor.FileBuffer->FileName, TRUE); + return Status; + } + + while (1) { + Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column); + StatusBarSetRefresh(); + + // + // ESC pressed + // + if (Status == EFI_NOT_READY) { + return EFI_SUCCESS; + } + // + // The input string length should > 0 + // + if (StrLen (InputBarGetString()) > 0) { + // + // CHECK if filename is valid + // + if (!IsValidFileName (InputBarGetString())) { + FileBufferRead (MainEditor.FileBuffer->FileName, TRUE); + StatusBarSetStatusString (L"Invalid File Name"); + return EFI_SUCCESS; + } + + break; + } + } + // + // read from disk + // + Status = FileBufferRead (InputBarGetString(), FALSE); + + if (EFI_ERROR (Status)) { + FileBufferRead (MainEditor.FileBuffer->FileName, TRUE); + return EFI_LOAD_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Switch a file from ASCII to UNICODE or vise-versa. + + @retval EFI_SUCCESS The switch was ok or a warning was presented. +**/ +EFI_STATUS +MainCommandSwitchFileType ( + VOID + ) +{ + // + // Below is the scenario of File Type command: + // After File Type is executed, file type will be changed to another type + // if file is read-only, can not be modified + // + if (MainEditor.FileBuffer->ReadOnly) { + StatusBarSetStatusString (L"Read Only File Can Not Be Modified"); + return EFI_SUCCESS; + } + + if (MainEditor.FileBuffer->FileType == FileTypeUnicode) { + MainEditor.FileBuffer->FileType = FileTypeAscii; + } else { + MainEditor.FileBuffer->FileType = FileTypeUnicode; + } + + MainEditor.FileBuffer->FileModified = TRUE; + + return EFI_SUCCESS; +} + +/** + cut current line to clipboard + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainCommandCutLine ( + VOID + ) +{ + EFI_STATUS Status; + EFI_EDITOR_LINE *Line; + + // + // This command will cut current line ( where cursor is on ) to clip board. + // And cursor will move to the beginning of next line. + // Below is the scenario of Cut Line command: + // 1. IF cursor is on valid line, current line will be cut to clip board. + // IF cursor is not on valid line, an Status String will be prompted : + // "Nothing to Cut". + // + Line = NULL; + Status = FileBufferCutLine (&Line); + if (Status == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + MainEditor.CutLine = Line; + + return EFI_SUCCESS; +} + +/** + paste line to file buffer. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainCommandPasteLine ( + VOID + ) +{ + EFI_STATUS Status; + + // + // Below is the scenario of Paste Line command: + // 1. IF nothing is on clipboard, a Status String will be prompted : + // "No Line to Paste" and Paste Line command ends. + // IF something is on clipboard, insert it above current line. + // nothing on clipboard + // + if (MainEditor.CutLine == NULL) { + StatusBarSetStatusString (L"No Line to Paste"); + return EFI_SUCCESS; + } + + Status = FileBufferPasteLine (); + + return Status; +} + + +/** + search string in file buffer + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainCommandSearch ( + VOID + ) +{ + EFI_STATUS Status; + CHAR16 *Buffer; + BOOLEAN Done; + UINTN Offset; + + // + // Below is the scenario of Search command: + // 1. An Input Bar will be prompted : "Enter Search String:". + // IF user press ESC, Search command ends. + // IF user just press Enter, Search command ends. + // IF user inputs the search string, do Step 2. + // + // 2. IF input search string is found, cursor will move to the first + // occurrence and do Step 3. + // IF input search string is not found, a Status String + // "Search String Not Found" will be prompted and Search command ends. + // + // 3. An Input Bar will be prompted: "Find Next (Yes/No/Cancel ) ?". + // IF user press ESC, Search command ends. + // IF user press 'y' or 'Y', do Step 2. + // IF user press 'n' or 'N', Search command ends. + // IF user press 'c' or 'C', Search command ends. + // + Status = InputBarSetPrompt (L"Enter Search String: "); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = InputBarSetStringSize (40); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column); + StatusBarSetRefresh(); + + // + // ESC + // + if (Status == EFI_NOT_READY) { + return EFI_SUCCESS; + } + // + // just enter pressed + // + if (StrLen (InputBarGetString()) == 0) { + return EFI_SUCCESS; + } + + Buffer = CatSPrint (NULL, L"%s", InputBarGetString()); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // the first time , search from current position + // + Offset = 0; + do { + // + // since search may be continued to search multiple times + // so we need to backup editor each time + // + MainEditorBackup (); + + Status = FileBufferSearch (Buffer, Offset); + + if (Status == EFI_NOT_FOUND) { + break; + } + // + // Find next + // + Status = InputBarSetPrompt (L"Find Next (Yes/No) ?"); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + + Status = InputBarSetStringSize (1); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + + Done = FALSE; + while (!Done) { + Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column); + StatusBarSetRefresh(); + + // + // ESC pressed + // + if (Status == EFI_NOT_READY) { + FreePool (Buffer); + return EFI_SUCCESS; + } + + switch (InputBarGetString()[0]) { + case L'y': + case L'Y': + Done = TRUE; + break; + + case L'n': + case L'N': + FreePool (Buffer); + return EFI_SUCCESS; + + } + // + // end of which + // + } + // + // end of while !Done + // for search second, third time, search from current position + strlen + // + Offset = StrLen (Buffer); + + } while (1); + // + // end of do + // + FreePool (Buffer); + StatusBarSetStatusString (L"Search String Not Found"); + + return EFI_SUCCESS; +} + +/** + Search string in file buffer, and replace it with another str. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainCommandSearchReplace ( + VOID + ) +{ + EFI_STATUS Status; + CHAR16 *Search; + CHAR16 *Replace; + BOOLEAN Done; + BOOLEAN First; + BOOLEAN ReplaceOption; + UINTN SearchLen; + UINTN ReplaceLen; + BOOLEAN ReplaceAll; + + ReplaceOption = FALSE; + + // + // Below is the scenario of Search/Replace command: + // 1. An Input Bar is prompted : "Enter Search String:". + // IF user press ESC, Search/Replace command ends. + // IF user just press Enter, Search/Replace command ends. + // IF user inputs the search string S, do Step 2. + // + // 2. An Input Bar is prompted: "Replace With:". + // IF user press ESC, Search/Replace command ends. + // IF user inputs the replace string R, do Step 3. + // + // 3. IF input search string is not found, an Status String + // "Search String Not Found" will be prompted + // and Search/Replace command ends + // IF input search string is found, do Step 4. + // + // 4. An Input Bar will be prompted: "Replace ( Yes/No/All/Cancel )?" + // IF user press 'y' or 'Y', S will be replaced with R and do Step 5 + // IF user press 'n' or 'N', S will not be replaced and do Step 5. + // IF user press 'a' or 'A', all the S from file current position on + // will be replaced with R and Search/Replace command ends. + // IF user press 'c' or 'C' or ESC, Search/Replace command ends. + // + // 5. An Input Bar will be prompted: "Find Next (Yes/No/Cancel) ?". + // IF user press ESC, Search/Replace command ends. + // IF user press 'y' or 'Y', do Step 3. + // IF user press 'n' or 'N', Search/Replace command ends. + // IF user press 'c' or 'C', Search/Replace command ends. + // input search string + // + Status = InputBarSetPrompt (L"Enter Search String: "); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = InputBarSetStringSize (40); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column); + StatusBarSetRefresh(); + + // + // ESC + // + if (Status == EFI_NOT_READY) { + return EFI_SUCCESS; + } + // + // if just pressed enter + // + if (StrLen (InputBarGetString()) == 0) { + return EFI_SUCCESS; + } + + Search = CatSPrint (NULL, L"%s", InputBarGetString()); + if (Search == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SearchLen = StrLen (Search); + + // + // input replace string + // + Status = InputBarSetPrompt (L"Replace With: "); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = InputBarSetStringSize (40); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column); + StatusBarSetRefresh(); + + // + // ESC + // + if (Status == EFI_NOT_READY) { + return EFI_SUCCESS; + } + + Replace = CatSPrint (NULL, L"%s", InputBarGetString()); + if (Replace == NULL) { + FreePool (Search); + return EFI_OUT_OF_RESOURCES; + } + + ReplaceLen = StrLen (Replace); + + First = TRUE; + ReplaceAll = FALSE; + do { + // + // since search may be continued to search multiple times + // so we need to backup editor each time + // + MainEditorBackup (); + + if (First) { + Status = FileBufferSearch (Search, 0); + } else { + // + // if just replace, so skip this replace string + // if replace string is an empty string, so skip to next character + // + if (ReplaceOption) { + Status = FileBufferSearch (Search, (ReplaceLen == 0) ? 1 : ReplaceLen); + } else { + Status = FileBufferSearch (Search, SearchLen); + } + } + + if (Status == EFI_NOT_FOUND) { + break; + } + // + // replace or not? + // + Status = InputBarSetPrompt (L"Replace (Yes/No/All/Cancel) ?"); + + if (EFI_ERROR (Status)) { + FreePool (Search); + FreePool (Replace); + return Status; + } + + Status = InputBarSetStringSize (1); + if (EFI_ERROR (Status)) { + FreePool (Search); + FreePool (Replace); + return Status; + } + + Done = FALSE; + while (!Done) { + Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column); + StatusBarSetRefresh(); + + // + // ESC pressed + // + if (Status == EFI_NOT_READY) { + FreePool (Search); + FreePool (Replace); + return EFI_SUCCESS; + } + + switch (InputBarGetString()[0]) { + case L'y': + case L'Y': + Done = TRUE; + ReplaceOption = TRUE; + break; + + case L'n': + case L'N': + Done = TRUE; + ReplaceOption = FALSE; + break; + + case L'a': + case L'A': + Done = TRUE; + ReplaceOption = TRUE; + ReplaceAll = TRUE; + break; + + case L'c': + case L'C': + FreePool (Search); + FreePool (Replace); + return EFI_SUCCESS; + + } + // + // end of which + // + } + // + // end of while !Done + // Decide to Replace + // + if (ReplaceOption) { + // + // file is read-only + // + if (MainEditor.FileBuffer->ReadOnly) { + StatusBarSetStatusString (L"Read Only File Can Not Be Modified"); + return EFI_SUCCESS; + } + // + // replace all + // + if (ReplaceAll) { + Status = FileBufferReplaceAll (Search, Replace, 0); + FreePool (Search); + FreePool (Replace); + return Status; + } + // + // replace + // + Status = FileBufferReplace (Replace, SearchLen); + if (EFI_ERROR (Status)) { + FreePool (Search); + FreePool (Replace); + return Status; + } + } + // + // Find next + // + Status = InputBarSetPrompt (L"Find Next (Yes/No) ?"); + if (EFI_ERROR (Status)) { + FreePool (Search); + FreePool (Replace); + return Status; + } + + Status = InputBarSetStringSize (1); + if (EFI_ERROR (Status)) { + FreePool (Search); + FreePool (Replace); + return Status; + } + + Done = FALSE; + while (!Done) { + Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column); + StatusBarSetRefresh(); + + // + // ESC pressed + // + if (Status == EFI_NOT_READY) { + FreePool (Search); + FreePool (Replace); + return EFI_SUCCESS; + } + + switch (InputBarGetString()[0]) { + case L'y': + case L'Y': + Done = TRUE; + break; + + case L'n': + case L'N': + FreePool (Search); + FreePool (Replace); + return EFI_SUCCESS; + + } + // + // end of which + // + } + // + // end of while !Done + // + First = FALSE; + + } while (1); + // + // end of do + // + FreePool (Search); + FreePool (Replace); + + StatusBarSetStatusString (L"Search String Not Found"); + + return EFI_SUCCESS; +} + +/** + exit editor + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainCommandExit ( + VOID + ) +{ + EFI_STATUS Status; + + // + // Below is the scenario of Exit command: + // 1. IF currently opened file is not modified, exit the editor and + // Exit command ends. + // IF currently opened file is modified, do Step 2 + // + // 2. An Input Bar will be prompted: + // "File modified. Save ( Yes/No/Cancel )?" + // IF user press 'y' or 'Y', currently opened file will be saved + // and Editor exits + // IF user press 'n' or 'N', currently opened file will not be saved + // and Editor exits. + // IF user press 'c' or 'C' or ESC, Exit command ends. + // if file has been modified, so will prompt user whether to save the changes + // + if (MainEditor.FileBuffer->FileModified) { + + Status = InputBarSetPrompt (L"File modified. Save (Yes/No/Cancel) ? "); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = InputBarSetStringSize (1); + if (EFI_ERROR (Status)) { + return Status; + } + + while (1) { + Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column); + StatusBarSetRefresh(); + + // + // ESC pressed + // + if (Status == EFI_NOT_READY) { + return EFI_SUCCESS; + } + + switch (InputBarGetString()[0]) { + case L'y': + case L'Y': + // + // write file back to disk + // + Status = FileBufferSave (MainEditor.FileBuffer->FileName); + if (!EFI_ERROR (Status)) { + EditorExit = TRUE; + } + + return Status; + + case L'n': + case L'N': + EditorExit = TRUE; + return EFI_SUCCESS; + + case L'c': + case L'C': + return EFI_SUCCESS; + + } + } + } + + EditorExit = TRUE; + return EFI_SUCCESS; + +} + +/** + move cursor to specified lines + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +MainCommandGotoLine ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Row; + + // + // Below is the scenario of Go To Line command: + // 1. An Input Bar will be prompted : "Go To Line:". + // IF user press ESC, Go To Line command ends. + // IF user just press Enter, cursor remains unchanged. + // IF user inputs line number, do Step 2. + // + // 2. IF input line number is valid, move cursor to the beginning + // of specified line and Go To Line command ends. + // IF input line number is invalid, a Status String will be prompted: + // "No Such Line" and Go To Line command ends. + // + Status = InputBarSetPrompt (L"Go To Line: "); + if (EFI_ERROR (Status)) { + return Status; + } + // + // line number's digit <= 6 + // + Status = InputBarSetStringSize (6); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column); + StatusBarSetRefresh(); + + // + // press ESC + // + if (Status == EFI_NOT_READY) { + return EFI_SUCCESS; + } + // + // if JUST press enter + // + if (StrLen (InputBarGetString()) == 0) { + return EFI_SUCCESS; + } + + Row = ShellStrToUintn (InputBarGetString()); + + // + // invalid line number + // + if (Row > MainEditor.FileBuffer->NumLines || Row <= 0) { + StatusBarSetStatusString (L"No Such Line"); + return EFI_SUCCESS; + } + // + // move cursor to that line's start + // + FileBufferMovePosition (Row, 1); + + return EFI_SUCCESS; +} + +/** + Save current file to disk, you can save to current file name or + save to another file name. + + @retval EFI_SUCCESS The file was saved correctly. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_LOAD_ERROR A file access error occurred. +**/ +EFI_STATUS +MainCommandSaveFile ( + VOID + ) +{ + EFI_STATUS Status; + CHAR16 *FileName; + BOOLEAN OldFile; + CHAR16 *Str; + SHELL_FILE_HANDLE FileHandle; + EFI_FILE_INFO *Info; + + // + // This command will save currently opened file to disk. + // You can choose save to another file name or just save to + // current file name. + // Below is the scenario of Save File command: + // ( Suppose the old file name is A ) + // 1. An Input Bar will be prompted: "File To Save: [ old file name]" + // IF user press ESC, Save File command ends . + // IF user press Enter, input file name will be A. + // IF user inputs a new file name B, input file name will be B. + // + // 2. IF input file name is A, go to do Step 3. + // IF input file name is B, go to do Step 4. + // + // 3. IF A is read only, Status Bar will show "Access Denied" and + // Save File commands ends. + // IF A is not read only, save file buffer to disk and remove modified + // flag in Title Bar , then Save File command ends. + // + // 4. IF B does not exist, create this file and save file buffer to it. + // Go to do Step 7. + // IF B exits, do Step 5. + // + // 5.An Input Bar will be prompted: + // "File Exists. Overwrite ( Yes/No/Cancel )?" + // IF user press 'y' or 'Y', do Step 6. + // IF user press 'n' or 'N', Save File commands ends. + // IF user press 'c' or 'C' or ESC, Save File commands ends. + // + // 6. IF B is a read-only file, Status Bar will show "Access Denied" and + // Save File commands ends. + // IF B can be read and write, save file buffer to B. + // + // 7. Update File Name field in Title Bar to B and remove the modified + // flag in Title Bar. + // + Str = CatSPrint (NULL, L"File to Save: [%s]", MainEditor.FileBuffer->FileName); + if (Str == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (StrLen (Str) >= 50) { + // + // replace the long file name with "..." + // + Str[46] = L'.'; + Str[47] = L'.'; + Str[48] = L'.'; + Str[49] = L']'; + Str[50] = CHAR_NULL; + } + + Status = InputBarSetPrompt (Str); + FreePool(Str); + + if (EFI_ERROR (Status)) { + return Status; + } + + + Status = InputBarSetStringSize (100); + if (EFI_ERROR (Status)) { + return Status; + } + // + // get new file name + // + Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column); + StatusBarSetRefresh(); + + // + // if user pressed ESC + // + if (Status == EFI_NOT_READY) { + return EFI_SUCCESS; + } + + // + // if just enter pressed, so think save to current file name + // + if (StrLen (InputBarGetString()) == 0) { + FileName = CatSPrint (NULL, L"%s", MainEditor.FileBuffer->FileName); + } else { + FileName = CatSPrint (NULL, L"%s", InputBarGetString()); + } + + if (FileName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (!IsValidFileName (FileName)) { + StatusBarSetStatusString (L"Invalid File Name"); + FreePool (FileName); + return EFI_SUCCESS; + } + + OldFile = FALSE; + + // + // save to the old file + // + if (StringNoCaseCompare (&FileName, &MainEditor.FileBuffer->FileName) == 0) { + OldFile = TRUE; + } + + if (OldFile) { + // + // if the file is read only, so can not write back to it. + // + if (MainEditor.FileBuffer->ReadOnly == TRUE) { + StatusBarSetStatusString (L"Access Denied"); + FreePool (FileName); + return EFI_SUCCESS; + } + } else { + // + // if the file exists + // + if (ShellFileExists(FileName) != EFI_NOT_FOUND) { + // + // check for read only + // + Status = ShellOpenFileByName(FileName, &FileHandle, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(Status)) { + StatusBarSetStatusString (L"Open Failed"); + FreePool (FileName); + return EFI_SUCCESS; + } + + Info = ShellGetFileInfo(FileHandle); + if (Info == NULL) { + StatusBarSetStatusString (L"Access Denied"); + FreePool (FileName); + return (EFI_SUCCESS); + } + + if (Info->Attribute & EFI_FILE_READ_ONLY) { + StatusBarSetStatusString (L"Access Denied - Read Only"); + FreePool (Info); + FreePool (FileName); + return (EFI_SUCCESS); + } + FreePool (Info); + + // + // ask user whether to overwrite this file + // + Status = InputBarSetPrompt (L"File exists. Overwrite (Yes/No/Cancel) ? "); + if (EFI_ERROR (Status)) { + SHELL_FREE_NON_NULL (FileName); + return Status; + } + + Status = InputBarSetStringSize (1); + if (EFI_ERROR (Status)) { + SHELL_FREE_NON_NULL (FileName); + return Status; + } + + while (TRUE) { + Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column); + StatusBarSetRefresh(); + + // + // ESC pressed + // + if (Status == EFI_NOT_READY) { + SHELL_FREE_NON_NULL (FileName); + return EFI_SUCCESS; + } + + switch (InputBarGetString()[0]) { + case L'y': + case L'Y': + break; + + case L'n': + case L'N': + case L'c': + case L'C': + SHELL_FREE_NON_NULL (FileName); + return EFI_SUCCESS; + } // end switch + } // while (!done) + } // file does exist + } // if old file name same + + // + // save file to disk with specified name + // + FileBufferSetModified(); + Status = FileBufferSave (FileName); + SHELL_FREE_NON_NULL (FileName); + + return Status; +} + +/** + Show help information for the editor. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +MainCommandDisplayHelp ( + VOID + ) +{ + INT32 CurrentLine; + CHAR16 *InfoString; + EFI_KEY_DATA KeyData; + EFI_STATUS Status; + UINTN EventIndex; + + // + // print helpInfo + // + for (CurrentLine = 0; 0 != MainMenuHelpInfo[CurrentLine]; CurrentLine++) { + InfoString = HiiGetString(gShellDebug1HiiHandle, MainMenuHelpInfo[CurrentLine], NULL); + ShellPrintEx (0, CurrentLine+1, L"%E%s%N", InfoString); + } + + // + // scan for ctrl+w + // + while (TRUE) { + Status = gBS->WaitForEvent (1, &MainEditor.TextInputEx->WaitForKeyEx, &EventIndex); + if (EFI_ERROR (Status) || (EventIndex != 0)) { + continue; + } + Status = MainEditor.TextInputEx->ReadKeyStrokeEx (MainEditor.TextInputEx, &KeyData); + if (EFI_ERROR (Status)) { + continue; + } + + if (((KeyData.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) == 0) || + (KeyData.KeyState.KeyShiftState == EFI_SHIFT_STATE_VALID)) { + // + // For consoles that don't support/report shift state, + // CTRL+W is translated to L'W' - L'A' + 1. + // + if (KeyData.Key.UnicodeChar == L'W' - L'A' + 1) { + break; + } + } else if (((KeyData.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) != 0) && + ((KeyData.KeyState.KeyShiftState & (EFI_LEFT_CONTROL_PRESSED | EFI_RIGHT_CONTROL_PRESSED)) != 0) && + ((KeyData.KeyState.KeyShiftState & ~(EFI_SHIFT_STATE_VALID | EFI_LEFT_CONTROL_PRESSED | EFI_RIGHT_CONTROL_PRESSED)) == 0)) { + // + // For consoles that supports/reports shift state, + // make sure that only CONTROL shift key is pressed. + // + if ((KeyData.Key.UnicodeChar == 'w') || (KeyData.Key.UnicodeChar == 'W')) { + break; + } + } + } + // + // update screen with file buffer's info + // + FileBufferRestorePosition (); + FileBufferNeedRefresh = TRUE; + FileBufferOnlyLineNeedRefresh = FALSE; + FileBufferRefresh (); + + return EFI_SUCCESS; +} + +EFI_EDITOR_COLOR_ATTRIBUTES OriginalColors; +INTN OriginalMode; + + +// +// basic initialization for MainEditor +// +EFI_EDITOR_GLOBAL_EDITOR MainEditorConst = { + &FileBuffer, + { + {0, 0} + }, + { + 0, + 0 + }, + NULL, + NULL, + FALSE, + NULL +}; + +/** + The initialization function for MainEditor. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainEditorInit ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN HandleCount; + UINTN Index; + + // + // basic initialization + // + CopyMem (&MainEditor, &MainEditorConst, sizeof (MainEditor)); + + // + // set screen attributes + // + MainEditor.ColorAttributes.Colors.Foreground = gST->ConOut->Mode->Attribute & 0x000000ff; + + MainEditor.ColorAttributes.Colors.Background = (UINT8) (gST->ConOut->Mode->Attribute >> 4); + OriginalColors = MainEditor.ColorAttributes.Colors; + + OriginalMode = gST->ConOut->Mode->Mode; + + // + // query screen size + // + gST->ConOut->QueryMode ( + gST->ConOut, + gST->ConOut->Mode->Mode, + &(MainEditor.ScreenSize.Column), + &(MainEditor.ScreenSize.Row) + ); + + // + // Find TextInEx in System Table ConsoleInHandle + // Per UEFI Spec, TextInEx is required for a console capable platform. + // + Status = gBS->HandleProtocol ( + gST->ConsoleInHandle, + &gEfiSimpleTextInputExProtocolGuid, + (VOID**)&MainEditor.TextInputEx + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Find mouse in System Table ConsoleInHandle + // + Status = gBS->HandleProtocol ( + gST->ConsoleInHandle, + &gEfiSimplePointerProtocolGuid, + (VOID**)&MainEditor.MouseInterface + ); + if (EFI_ERROR (Status)) { + // + // If there is no Simple Pointer Protocol on System Table + // + HandleBuffer = NULL; + MainEditor.MouseInterface = NULL; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimplePointerProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + if (!EFI_ERROR (Status) && HandleCount > 0) { + // + // Try to find the first available mouse device + // + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiSimplePointerProtocolGuid, + (VOID**)&MainEditor.MouseInterface + ); + if (!EFI_ERROR (Status)) { + break; + } + } + } + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } + } + + if (!EFI_ERROR (Status) && MainEditor.MouseInterface != NULL) { + MainEditor.MouseAccumulatorX = 0; + MainEditor.MouseAccumulatorY = 0; + MainEditor.MouseSupported = TRUE; + } + + // + // below will call the five components' init function + // + Status = MainTitleBarInit (L"UEFI EDIT"); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_TITLEBAR), gShellDebug1HiiHandle); + return EFI_LOAD_ERROR; + } + + Status = ControlHotKeyInit (MainControlBasedMenuFunctions); + Status = MenuBarInit (MainMenuItems); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_MAINMENU), gShellDebug1HiiHandle); + return EFI_LOAD_ERROR; + } + + Status = StatusBarInit (); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_STATUSBAR), gShellDebug1HiiHandle); + return EFI_LOAD_ERROR; + } + + InputBarInit (MainEditor.TextInputEx); + + Status = FileBufferInit (); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_FILEBUFFER), gShellDebug1HiiHandle); + return EFI_LOAD_ERROR; + } + // + // clear whole screen and enable cursor + // + gST->ConOut->ClearScreen (gST->ConOut); + gST->ConOut->EnableCursor (gST->ConOut, TRUE); + + // + // initialize EditorFirst and EditorExit + // + EditorFirst = TRUE; + EditorExit = FALSE; + EditorMouseAction = FALSE; + + return EFI_SUCCESS; +} + +/** + The cleanup function for MainEditor. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainEditorCleanup ( + VOID + ) +{ + EFI_STATUS Status; + + // + // call the five components' cleanup function + // if error, do not exit + // just print some warning + // + MainTitleBarCleanup(); + StatusBarCleanup(); + InputBarCleanup(); + MenuBarCleanup (); + + Status = FileBufferCleanup (); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_FILEBUFFER_CLEANUP), gShellDebug1HiiHandle); + } + // + // restore old mode + // + if (OriginalMode != gST->ConOut->Mode->Mode) { + gST->ConOut->SetMode (gST->ConOut, OriginalMode); + } + // + // restore old screen color + // + gST->ConOut->SetAttribute ( + gST->ConOut, + EFI_TEXT_ATTR (OriginalColors.Foreground, OriginalColors.Background) + ); + + gST->ConOut->ClearScreen (gST->ConOut); + + return EFI_SUCCESS; +} + +/** + Refresh the main editor component. +**/ +VOID +MainEditorRefresh ( + VOID + ) +{ + // + // The Stall value is from experience. NOT from spec. avoids 'flicker' + // + gBS->Stall (50); + + // + // call the components refresh function + // + if (EditorFirst + || StrCmp (FileBufferBackupVar.FileName, FileBuffer.FileName) != 0 + || FileBufferBackupVar.FileType != FileBuffer.FileType + || FileBufferBackupVar.FileModified != FileBuffer.FileModified + || FileBufferBackupVar.ReadOnly != FileBuffer.ReadOnly) { + + MainTitleBarRefresh (MainEditor.FileBuffer->FileName, MainEditor.FileBuffer->FileType, MainEditor.FileBuffer->ReadOnly, MainEditor.FileBuffer->FileModified, MainEditor.ScreenSize.Column, MainEditor.ScreenSize.Row, 0, 0); + FileBufferRestorePosition (); + } + + if (EditorFirst + || FileBufferBackupVar.FilePosition.Row != FileBuffer.FilePosition.Row + || FileBufferBackupVar.FilePosition.Column != FileBuffer.FilePosition.Column + || FileBufferBackupVar.ModeInsert != FileBuffer.ModeInsert + || StatusBarGetRefresh()) { + + StatusBarRefresh (EditorFirst, MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column, MainEditor.FileBuffer->FilePosition.Row, MainEditor.FileBuffer->FilePosition.Column, MainEditor.FileBuffer->ModeInsert); + FileBufferRestorePosition (); + } + + if (EditorFirst) { + FileBufferRestorePosition (); + } + + FileBufferRefresh (); + + // + // EditorFirst is now set to FALSE + // + EditorFirst = FALSE; +} + +/** + Get's the resultant location of the cursor based on the relative movement of the Mouse. + + @param[in] GuidX The relative mouse movement. + + @return The X location of the mouse. +**/ +INT32 +GetTextX ( + IN INT32 GuidX + ) +{ + INT32 Gap; + + MainEditor.MouseAccumulatorX += GuidX; + Gap = (MainEditor.MouseAccumulatorX * (INT32) MainEditor.ScreenSize.Column) / (INT32) (50 * (INT32) MainEditor.MouseInterface->Mode->ResolutionX); + MainEditor.MouseAccumulatorX = (MainEditor.MouseAccumulatorX * (INT32) MainEditor.ScreenSize.Column) % (INT32) (50 * (INT32) MainEditor.MouseInterface->Mode->ResolutionX); + MainEditor.MouseAccumulatorX = MainEditor.MouseAccumulatorX / (INT32) MainEditor.ScreenSize.Column; + return Gap; +} + +/** + Get's the resultant location of the cursor based on the relative movement of the Mouse. + + @param[in] GuidY The relative mouse movement. + + @return The Y location of the mouse. +**/ +INT32 +GetTextY ( + IN INT32 GuidY + ) +{ + INT32 Gap; + + MainEditor.MouseAccumulatorY += GuidY; + Gap = (MainEditor.MouseAccumulatorY * (INT32) MainEditor.ScreenSize.Row) / (INT32) (50 * (INT32) MainEditor.MouseInterface->Mode->ResolutionY); + MainEditor.MouseAccumulatorY = (MainEditor.MouseAccumulatorY * (INT32) MainEditor.ScreenSize.Row) % (INT32) (50 * (INT32) MainEditor.MouseInterface->Mode->ResolutionY); + MainEditor.MouseAccumulatorY = MainEditor.MouseAccumulatorY / (INT32) MainEditor.ScreenSize.Row; + + return Gap; +} + +/** + Support mouse movement. Move the cursor. + + @param[in] MouseState The current mouse state. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_NOT_FOUND There was no mouse support found. +**/ +EFI_STATUS +MainEditorHandleMouseInput ( + IN EFI_SIMPLE_POINTER_STATE MouseState + ) +{ + + INT32 TextX; + INT32 TextY; + UINTN FRow; + UINTN FCol; + + LIST_ENTRY *Link; + EFI_EDITOR_LINE *Line; + + UINTN Index; + BOOLEAN Action; + + // + // mouse action means: + // mouse movement + // mouse left button + // + Action = FALSE; + + // + // have mouse movement + // + if (MouseState.RelativeMovementX || MouseState.RelativeMovementY) { + // + // handle + // + TextX = GetTextX (MouseState.RelativeMovementX); + TextY = GetTextY (MouseState.RelativeMovementY); + + FileBufferAdjustMousePosition (TextX, TextY); + + Action = TRUE; + + } + + // + // if left button pushed down + // + if (MouseState.LeftButton) { + + FCol = MainEditor.FileBuffer->MousePosition.Column - 1 + 1; + + FRow = MainEditor.FileBuffer->FilePosition.Row + + MainEditor.FileBuffer->MousePosition.Row - + MainEditor.FileBuffer->DisplayPosition.Row; + + // + // beyond the file line length + // + if (MainEditor.FileBuffer->NumLines < FRow) { + FRow = MainEditor.FileBuffer->NumLines; + } + + Link = MainEditor.FileBuffer->ListHead->ForwardLink; + for (Index = 0; Index < FRow - 1; Index++) { + Link = Link->ForwardLink; + } + + Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE); + + // + // beyond the line's column length + // + if (FCol > Line->Size + 1) { + FCol = Line->Size + 1; + } + + FileBufferMovePosition (FRow, FCol); + + MainEditor.FileBuffer->MousePosition.Row = MainEditor.FileBuffer->DisplayPosition.Row; + + MainEditor.FileBuffer->MousePosition.Column = MainEditor.FileBuffer->DisplayPosition.Column; + + Action = TRUE; + } + // + // mouse has action + // + if (Action) { + return EFI_SUCCESS; + } + + // + // no mouse action + // + return EFI_NOT_FOUND; +} + +/** + Handle user key input. This routes to other functions for the actions. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_LOAD_ERROR A load error occurred. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +MainEditorKeyInput ( + VOID + ) +{ + EFI_KEY_DATA KeyData; + EFI_STATUS Status; + EFI_SIMPLE_POINTER_STATE MouseState; + BOOLEAN NoShiftState; + + do { + + Status = EFI_SUCCESS; + EditorMouseAction = FALSE; + + // + // backup some key elements, so that can aVOID some refresh work + // + MainEditorBackup (); + + // + // change priority of checking mouse/keyboard activity dynamically + // so prevent starvation of keyboard. + // if last time, mouse moves then this time check keyboard + // + if (MainEditor.MouseSupported) { + Status = MainEditor.MouseInterface->GetState ( + MainEditor.MouseInterface, + &MouseState + ); + if (!EFI_ERROR (Status)) { + + Status = MainEditorHandleMouseInput (MouseState); + + if (!EFI_ERROR (Status)) { + EditorMouseAction = TRUE; + FileBufferMouseNeedRefresh = TRUE; + } else if (Status == EFI_LOAD_ERROR) { + StatusBarSetStatusString (L"Invalid Mouse Movement "); + } + } + } + + // + // CheckEvent() returns Success when non-partial key is pressed. + // + Status = gBS->CheckEvent (MainEditor.TextInputEx->WaitForKeyEx); + if (!EFI_ERROR (Status)) { + Status = MainEditor.TextInputEx->ReadKeyStrokeEx (MainEditor.TextInputEx, &KeyData); + if (!EFI_ERROR (Status)) { + // + // dispatch to different components' key handling function + // so not everywhere has to set this variable + // + FileBufferMouseNeedRefresh = TRUE; + // + // clear previous status string + // + StatusBarSetRefresh(); + // + // NoShiftState: TRUE when no shift key is pressed. + // + NoShiftState = ((KeyData.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) == 0) || (KeyData.KeyState.KeyShiftState == EFI_SHIFT_STATE_VALID); + // + // dispatch to different components' key handling function + // + if (EFI_NOT_FOUND != MenuBarDispatchControlHotKey(&KeyData)) { + Status = EFI_SUCCESS; + } else if (NoShiftState && ((KeyData.Key.ScanCode == SCAN_NULL) || ((KeyData.Key.ScanCode >= SCAN_UP) && (KeyData.Key.ScanCode <= SCAN_PAGE_DOWN)))) { + Status = FileBufferHandleInput (&KeyData.Key); + } else if (NoShiftState && (KeyData.Key.ScanCode >= SCAN_F1) && (KeyData.Key.ScanCode <= SCAN_F12)) { + Status = MenuBarDispatchFunctionKey (&KeyData.Key); + } else { + StatusBarSetStatusString (L"Unknown Command"); + FileBufferMouseNeedRefresh = FALSE; + } + + if (Status != EFI_SUCCESS && Status != EFI_OUT_OF_RESOURCES) { + // + // not already has some error status + // + if (StatusBarGetString() != NULL && StrCmp (L"", StatusBarGetString()) == 0) { + StatusBarSetStatusString (L"Disk Error. Try Again"); + } + } + + } + } + // + // after handling, refresh editor + // + MainEditorRefresh (); + + } while (Status != EFI_OUT_OF_RESOURCES && !EditorExit); + + return Status; +} + +/** + Set clipboard + + @param[in] Line A pointer to the line to be set to clipboard + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +MainEditorSetCutLine ( + EFI_EDITOR_LINE *Line + ) +{ + if (Line == NULL) { + return EFI_SUCCESS; + } + + if (MainEditor.CutLine != NULL) { + // + // free the old clipboard + // + LineFree (MainEditor.CutLine); + } + // + // duplicate the line to clipboard + // + MainEditor.CutLine = LineDup (Line); + if (MainEditor.CutLine == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + +/** + Backup function for MainEditor + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +MainEditorBackup ( + VOID + ) +{ + FileBufferBackup (); + + return EFI_SUCCESS; +} diff --git a/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/MainTextEditor.h b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/MainTextEditor.h new file mode 100644 index 000000000..b22b6587b --- /dev/null +++ b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/MainTextEditor.h @@ -0,0 +1,66 @@ +/** @file + Declares editor interface functions. + + Copyright (c) 2005 - 2011, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _LIB_EDITOR_H_ +#define _LIB_EDITOR_H_ + +#include "TextEditorTypes.h" + +/** + The initialization function for MainEditor. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainEditorInit ( + VOID + ); + +/** + The cleanup function for MainEditor. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_LOAD_ERROR A load error occurred. +**/ +EFI_STATUS +MainEditorCleanup ( + VOID + ); + +/** + Refresh the main editor component. +**/ +VOID +MainEditorRefresh ( + VOID + ); + +/** + Handle user key input. This routes to other functions for the actions. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_LOAD_ERROR A load error occurred. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. +**/ +EFI_STATUS +MainEditorKeyInput ( + VOID + ); + +/** + Backup function for MainEditor + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +MainEditorBackup ( + VOID + ); + +#endif diff --git a/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/Misc.c b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/Misc.c new file mode 100644 index 000000000..db18010e4 --- /dev/null +++ b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/Misc.c @@ -0,0 +1,84 @@ +/** @file + Implementation of various string and line routines. + + Copyright (c) 2005 - 2011, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "TextEditor.h" +#include "Misc.h" + +/** + Duplicate a EFI_EDITOR_LINE structure. + + @param Src The line structure to copy from. + + @retval NULL A memory allocation failed. + @return a pointer to the newly allcoated line. +**/ +EFI_EDITOR_LINE * +LineDup ( + IN EFI_EDITOR_LINE *Src + ) +{ + EFI_EDITOR_LINE *Dest; + + // + // allocate for the line structure + // + Dest = AllocateZeroPool (sizeof (EFI_EDITOR_LINE)); + if (Dest == NULL) { + return NULL; + } + // + // allocate and set the line buffer + // + Dest->Buffer = CatSPrint (NULL, L"%s", Src->Buffer); + if (Dest->Buffer == NULL) { + FreePool (Dest); + return NULL; + } + + // + // set the other structure members + // + Dest->Signature = LINE_LIST_SIGNATURE; + Dest->Size = Src->Size; + Dest->TotalSize = Dest->Size; + Dest->Type = Src->Type; + Dest->Link = Src->Link; + + return Dest; +} + +/** + Free a EFI_EDITOR_LINE structure. + + @param Src The line structure to free. +**/ +VOID +LineFree ( + IN EFI_EDITOR_LINE *Src + ) +{ + if (Src == NULL) { + return ; + } + // + // free the line buffer and then the line structure itself + // + SHELL_FREE_NON_NULL (Src->Buffer); + SHELL_FREE_NON_NULL (Src); + +} + + + + + + + + + + diff --git a/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/Misc.h b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/Misc.h new file mode 100644 index 000000000..1a4cdd7b1 --- /dev/null +++ b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/Misc.h @@ -0,0 +1,44 @@ +/** @file + Declares generic editor helper functions. + + Copyright (c) 2005 - 2011, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _LIB_MISC_H_ +#define _LIB_MISC_H_ + +#include "TextEditorTypes.h" + + + +/** + Free a EFI_EDITOR_LINE structure. + + @param Src The line structure to free. +**/ +VOID +LineFree ( + IN EFI_EDITOR_LINE *Src + ); + +/** + Duplicate a EFI_EDITOR_LINE structure. + + @param Src The line structure to copy from. + + @retval NULL A memory allocation failed. + @return a pointer to the newly allcoated line. +**/ +EFI_EDITOR_LINE * +LineDup ( + IN EFI_EDITOR_LINE *Src + ); + + + + + + +#endif diff --git a/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/TextEditStrings.uni b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/TextEditStrings.uni new file mode 100644 index 000000000..f38221dfe --- /dev/null +++ b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/TextEditStrings.uni @@ -0,0 +1,69 @@ +// /** +// +// Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// Abstract: +// +// Additional string definitions for UEFI Shell 2.0 Debug1 profile Edit command +// +// +// **/ + +/=# + +#langdef en-US "english" + +#string STR_EDIT_LIBEDITOR_TITLEBAR #language en-US "%EMainEditor init failed on TitleBar init\r\n%N" +#string STR_EDIT_LIBEDITOR_MAINMENU #language en-US "%EMainEditor init was not successful on MainMenu init\r\n%N" +#string STR_EDIT_LIBEDITOR_STATUSBAR #language en-US "%EMainEditor init was not successful on StatusBar init\r\n%N" +#string STR_EDIT_LIBEDITOR_INPUTBAR #language en-US "%EMainEditor init was not successful on InputBar init\r\n%N" +#string STR_EDIT_LIBEDITOR_FILEBUFFER #language en-US "%EMainEditor init was not successful on FileBuffer init\r\n%N" +#string STR_EDIT_LIBEDITOR_TITLEBAR_CLEANUP #language en-US "TitleBar cleanup was not successful\r\n" +#string STR_EDIT_LIBEDITOR_MENUBAR_CLEANUP #language en-US "MenuBar cleanup was not successful\r\n" +#string STR_EDIT_LIBEDITOR_STATUSBAR_CLEANUP #language en-US "StatusBar cleanup was not successful\r\n" +#string STR_EDIT_LIBEDITOR_INPUTBAR_CLEANUP #language en-US "InputBar cleanup was not successful\r\n" +#string STR_EDIT_LIBEDITOR_FILEBUFFER_CLEANUP #language en-US "FileBuffer cleanup was not successful\r\n" +#string STR_EDIT_LIBEDITOR_MAINEDITOR_INIT #language en-US "%EMainEditor init was not succesful on TitleBar init\r\n%N" +#string STR_EDIT_LIBINPUTBAR_MAININPUTBAR #language en-US "%s" +#string STR_EDIT_LIBMENUBAR_OPEN_FILE #language en-US "Open File" +#string STR_EDIT_LIBMENUBAR_SAVE_FILE #language en-US "Save File" +#string STR_EDIT_LIBMENUBAR_EXIT #language en-US "Exit" +#string STR_EDIT_LIBMENUBAR_CUT_LINE #language en-US "Cut Line" +#string STR_EDIT_LIBMENUBAR_PASTE_LINE #language en-US "Paste Line" +#string STR_EDIT_LIBMENUBAR_GO_TO_LINE #language en-US "Go To Line" +#string STR_EDIT_LIBMENUBAR_SEARCH #language en-US "Search" +#string STR_EDIT_LIBMENUBAR_SEARCH_REPLACE #language en-US "Search/Replace" +#string STR_EDIT_LIBMENUBAR_FILE_TYPE #language en-US "File Type" +#string STR_EDIT_LIBMENUBAR_F1 #language en-US "F1" +#string STR_EDIT_LIBMENUBAR_F2 #language en-US "F2" +#string STR_EDIT_LIBMENUBAR_F3 #language en-US "F3" +#string STR_EDIT_LIBMENUBAR_F4 #language en-US "F4" +#string STR_EDIT_LIBMENUBAR_F5 #language en-US "F5" +#string STR_EDIT_LIBMENUBAR_F6 #language en-US "F6" +#string STR_EDIT_LIBMENUBAR_F7 #language en-US "F7" +#string STR_EDIT_LIBMENUBAR_F8 #language en-US "F8" +#string STR_EDIT_LIBMENUBAR_F9 #language en-US "F9" +#string STR_EDIT_LIBMENUBAR_F10 #language en-US "F10" +#string STR_EDIT_LIBMENUBAR_F11 #language en-US "F11" +#string STR_EDIT_LIBMENUBAR_F12 #language en-US "F12" +#string STR_EDIT_LIBMENUBAR_CTRL_E #language en-US "Ctrl+E" +#string STR_EDIT_LIBMENUBAR_CTRL_W #language en-US "Ctrl+W" +#string STR_EDIT_HELP_TITLE #language en-US "Help \n" +#string STR_EDIT_HELP_BLANK #language en-US " \n" +#string STR_EDIT_HELP_LIST_TITLE #language en-US "Control Key Function Key Command \n" +#string STR_EDIT_HELP_DIV #language en-US "----------- ------------ ----------------- \n" +#string STR_EDIT_HELP_GO_TO_LINE #language en-US "Ctrl-G F1 Go To Line \n" +#string STR_EDIT_HELP_SAVE_FILE #language en-US "Ctrl-S F2 Save File \n" +#string STR_EDIT_HELP_EXIT #language en-US "Ctrl-Q F3 Exit \n" +#string STR_EDIT_HELP_SEARCH #language en-US "Ctrl-F F4 Search \n" +#string STR_EDIT_HELP_SEARCH_REPLACE #language en-US "Ctrl-R F5 Search/Replace \n" +#string STR_EDIT_HELP_CUT_LINE #language en-US "Ctrl-K F6 Cut Line \n" +#string STR_EDIT_HELP_PASTE_LINE #language en-US "Ctrl-U F7 Paste Line \n" +#string STR_EDIT_HELP_OPEN_FILE #language en-US "Ctrl-O F8 Open File \n" +#string STR_EDIT_HELP_FILE_TYPE #language en-US "Ctrl-T F9 File Type \n" +#string STR_EDIT_HELP_EXIT_HELP #language en-US "Use Ctrl-W to exit this help \n" +#string STR_EDIT_MAIN_INVALID_FILE_NAME #language en-US "%Hedit%N: Invalid File Name\r\n" +#string STR_EDIT_MAIN_INIT_FAILED #language en-US "%Hedit%N: Initialization was not successful\r\n" +#string STR_EDIT_MAIN_BUFFER #language en-US "%Hedit%N: %s\r\n" +#string STR_EDIT_MAIN_UNKNOWN_EDITOR_ERR #language en-US "%Hedit%N: Unknown Editor Error\r\n" diff --git a/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/TextEditor.h b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/TextEditor.h new file mode 100644 index 000000000..0807f80cc --- /dev/null +++ b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/TextEditor.h @@ -0,0 +1,26 @@ +/** @file + Main include file for Edit shell Debug1 function. + + Copyright (c) 2005 - 2011, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EFI_EDIT_H_ +#define _EFI_EDIT_H_ + +#include "TextEditorTypes.h" + +#include "MainTextEditor.h" +#include "FileBuffer.h" +#include "EditTitleBar.h" +#include "EditStatusBar.h" +#include "EditInputBar.h" +#include "EditMenuBar.h" +#include "Misc.h" + +extern EFI_EDITOR_GLOBAL_EDITOR MainEditor; +extern BOOLEAN EditorFirst; +extern BOOLEAN EditorExit; + +#endif // _EFI_EDIT_H_ diff --git a/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/TextEditorTypes.h b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/TextEditorTypes.h new file mode 100644 index 000000000..327ecd225 --- /dev/null +++ b/roms/edk2/ShellPkg/Library/UefiShellDebug1CommandsLib/Edit/TextEditorTypes.h @@ -0,0 +1,97 @@ +/** @file + Declares editor types. + + Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EDITOR_TYPE_H_ +#define _EDITOR_TYPE_H_ + +#include "UefiShellDebug1CommandsLib.h" +#include "EditTitleBar.h" +#include "EditMenuBar.h" + +#define MIN_POOL_SIZE 125 +#define MAX_STRING_LENGTH 127 + +typedef struct { + UINTN Row; + UINTN Column; +} EFI_EDITOR_POSITION; + +typedef +EFI_STATUS +(*EFI_MENU_ITEM_FUNCTION) ( + VOID + ); + +typedef enum { + NewLineTypeDefault, + NewLineTypeLineFeed, + NewLineTypeCarriageReturn, + NewLineTypeCarriageReturnLineFeed, + NewLineTypeLineFeedCarriageReturn, + NewLineTypeUnknown +} EE_NEWLINE_TYPE; + +#define LINE_LIST_SIGNATURE SIGNATURE_32 ('e', 'e', 'l', 'l') +typedef struct _EFI_EDITOR_LINE { + UINTN Signature; + CHAR16 *Buffer; + UINTN Size; // unit is Unicode + UINTN TotalSize; // unit is Unicode, exclude CHAR_NULL + EE_NEWLINE_TYPE Type; + LIST_ENTRY Link; +} EFI_EDITOR_LINE; + +typedef struct { + UINT32 Foreground : 4; + UINT32 Background : 4; +} EFI_EDITOR_COLOR_ATTRIBUTES; + +typedef union { + EFI_EDITOR_COLOR_ATTRIBUTES Colors; + UINTN Data; +} EFI_EDITOR_COLOR_UNION; + +typedef struct { + UINTN Columns; + UINTN Rows; +} EFI_EDITOR_TEXT_MODE; + +typedef struct { + CHAR16 *FileName; // file name current edited in editor + EDIT_FILE_TYPE FileType; // Unicode file or ASCII file + LIST_ENTRY *ListHead; // list head of lines + EFI_EDITOR_LINE *Lines; // lines of current file + UINTN NumLines; // total line numbers + EFI_EDITOR_POSITION DisplayPosition; // cursor position in screen + EFI_EDITOR_POSITION FilePosition; // cursor position in file + EFI_EDITOR_POSITION MousePosition; // mouse position in screen + // file position of first byte displayed on screen + // + EFI_EDITOR_POSITION LowVisibleRange; + + BOOLEAN FileModified; // file is modified or not + BOOLEAN ModeInsert; // input mode INS or OVR + BOOLEAN ReadOnly; // file is read-only or not + EFI_EDITOR_LINE *CurrentLine; // current line cursor is at +} EFI_EDITOR_FILE_BUFFER; + +typedef struct { + EFI_EDITOR_FILE_BUFFER *FileBuffer; + + EFI_EDITOR_COLOR_UNION ColorAttributes; + EFI_EDITOR_POSITION ScreenSize; // row number and column number + EFI_EDITOR_LINE *CutLine; // clip board + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx; + BOOLEAN MouseSupported; + EFI_SIMPLE_POINTER_PROTOCOL *MouseInterface; + INT32 MouseAccumulatorX; + INT32 MouseAccumulatorY; + +} EFI_EDITOR_GLOBAL_EDITOR; + +#endif -- cgit 1.2.3-korg