Home | History | Annotate | Download | only in Edit
      1 /** @file
      2   Implements filebuffer interface functions.
      3 
      4   Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved. <BR>
      5   This program and the accompanying materials
      6   are licensed and made available under the terms and conditions of the BSD License
      7   which accompanies this distribution.  The full text of the license may be found at
      8   http://opensource.org/licenses/bsd-license.php
      9 
     10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "TextEditor.h"
     16 #include <Guid/FileSystemInfo.h>
     17 #include <Library/FileHandleLib.h>
     18 
     19 EFI_EDITOR_FILE_BUFFER  FileBuffer;
     20 EFI_EDITOR_FILE_BUFFER  FileBufferBackupVar;
     21 
     22 //
     23 // for basic initialization of FileBuffer
     24 //
     25 EFI_EDITOR_FILE_BUFFER  FileBufferConst = {
     26   NULL,
     27   FileTypeUnicode,
     28   NULL,
     29   NULL,
     30   0,
     31   {
     32     0,
     33     0
     34   },
     35   {
     36     0,
     37     0
     38   },
     39   {
     40     0,
     41     0
     42   },
     43   {
     44     0,
     45     0
     46   },
     47   FALSE,
     48   TRUE,
     49   FALSE,
     50   NULL
     51 };
     52 
     53 //
     54 // the whole edit area needs to be refreshed
     55 //
     56 BOOLEAN          FileBufferNeedRefresh;
     57 
     58 //
     59 // only the current line in edit area needs to be refresh
     60 //
     61 BOOLEAN                 FileBufferOnlyLineNeedRefresh;
     62 
     63 BOOLEAN                 FileBufferMouseNeedRefresh;
     64 
     65 extern BOOLEAN          EditorMouseAction;
     66 
     67 /**
     68   Initialization function for FileBuffer.
     69 
     70   @param EFI_SUCCESS            The initialization was successful.
     71   @param EFI_LOAD_ERROR         A default name could not be created.
     72   @param EFI_OUT_OF_RESOURCES   A memory allocation failed.
     73 **/
     74 EFI_STATUS
     75 EFIAPI
     76 FileBufferInit (
     77   VOID
     78   )
     79 {
     80   //
     81   // basically initialize the FileBuffer
     82   //
     83   CopyMem (&FileBuffer         , &FileBufferConst, sizeof (EFI_EDITOR_FILE_BUFFER));
     84   CopyMem (&FileBufferBackupVar, &FileBufferConst, sizeof (EFI_EDITOR_FILE_BUFFER));
     85 
     86   //
     87   // set default FileName
     88   //
     89   FileBuffer.FileName = EditGetDefaultFileName (L"txt");
     90   if (FileBuffer.FileName == NULL) {
     91     return EFI_LOAD_ERROR;
     92   }
     93 
     94   FileBuffer.ListHead = AllocateZeroPool (sizeof (LIST_ENTRY));
     95   if (FileBuffer.ListHead == NULL) {
     96     return EFI_OUT_OF_RESOURCES;
     97   }
     98 
     99   InitializeListHead (FileBuffer.ListHead);
    100 
    101   FileBuffer.DisplayPosition.Row    = 2;
    102   FileBuffer.DisplayPosition.Column = 1;
    103   FileBuffer.LowVisibleRange.Row    = 2;
    104   FileBuffer.LowVisibleRange.Column = 1;
    105 
    106   FileBufferNeedRefresh             = FALSE;
    107   FileBufferMouseNeedRefresh        = FALSE;
    108   FileBufferOnlyLineNeedRefresh     = FALSE;
    109 
    110   return EFI_SUCCESS;
    111 }
    112 
    113 /**
    114   Backup function for FileBuffer.  Only backup the following items:
    115       Mouse/Cursor position
    116       File Name, Type, ReadOnly, Modified
    117       Insert Mode
    118 
    119   This is for making the file buffer refresh as few as possible.
    120 
    121   @retval EFI_SUCCESS           The backup operation was successful.
    122 **/
    123 EFI_STATUS
    124 EFIAPI
    125 FileBufferBackup (
    126   VOID
    127   )
    128 {
    129   FileBufferBackupVar.MousePosition = FileBuffer.MousePosition;
    130 
    131   SHELL_FREE_NON_NULL (FileBufferBackupVar.FileName);
    132   FileBufferBackupVar.FileName        = NULL;
    133   FileBufferBackupVar.FileName        = StrnCatGrow (&FileBufferBackupVar.FileName, NULL, FileBuffer.FileName, 0);
    134 
    135   FileBufferBackupVar.ModeInsert      = FileBuffer.ModeInsert;
    136   FileBufferBackupVar.FileType        = FileBuffer.FileType;
    137 
    138   FileBufferBackupVar.FilePosition    = FileBuffer.FilePosition;
    139   FileBufferBackupVar.LowVisibleRange = FileBuffer.LowVisibleRange;
    140 
    141   FileBufferBackupVar.FileModified    = FileBuffer.FileModified;
    142   FileBufferBackupVar.ReadOnly        = FileBuffer.ReadOnly;
    143 
    144   return EFI_SUCCESS;
    145 }
    146 
    147 /**
    148   Advance to the next Count lines
    149 
    150   @param[in] Count              The line number to advance by.
    151   @param[in] CurrentLine        The pointer to the current line structure.
    152   @param[in] LineList           The pointer to the linked list of lines.
    153 
    154   @retval NULL                  There was an error.
    155   @return  The line structure after the advance.
    156 **/
    157 EFI_EDITOR_LINE *
    158 EFIAPI
    159 InternalEditorMiscLineAdvance (
    160   IN CONST UINTN            Count,
    161   IN CONST EFI_EDITOR_LINE  *CurrentLine,
    162   IN CONST LIST_ENTRY       *LineList
    163   )
    164 
    165 {
    166   UINTN                 Index;
    167   CONST EFI_EDITOR_LINE *Line;
    168 
    169   if (CurrentLine == NULL || LineList == NULL) {
    170     return NULL;
    171   }
    172 
    173   for (Line = CurrentLine, Index = 0; Index < Count; Index++) {
    174     //
    175     // if already last line
    176     //
    177     if (Line->Link.ForwardLink == LineList) {
    178       return NULL;
    179     }
    180 
    181     Line = CR (Line->Link.ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
    182   }
    183 
    184   return ((EFI_EDITOR_LINE *)Line);
    185 }
    186 
    187 /**
    188   Retreat to the previous Count lines.
    189 
    190   @param[in] Count              The line number to retreat by.
    191   @param[in] CurrentLine        The pointer to the current line structure.
    192   @param[in] LineList           The pointer to the linked list of lines.
    193 
    194   @retval NULL                  There was an error.
    195   @return  The line structure after the retreat.
    196 **/
    197 EFI_EDITOR_LINE *
    198 EFIAPI
    199 InternalEditorMiscLineRetreat (
    200   IN CONST UINTN            Count,
    201   IN CONST EFI_EDITOR_LINE  *CurrentLine,
    202   IN CONST LIST_ENTRY       *LineList
    203   )
    204 
    205 {
    206   UINTN                 Index;
    207   CONST EFI_EDITOR_LINE *Line;
    208 
    209   if (CurrentLine == NULL || LineList == NULL) {
    210     return NULL;
    211   }
    212 
    213   for (Line = CurrentLine, Index = 0; Index < Count; Index++) {
    214     //
    215     // already the first line
    216     //
    217     if (Line->Link.BackLink == LineList) {
    218       return NULL;
    219     }
    220 
    221     Line = CR (Line->Link.BackLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
    222   }
    223 
    224   return ((EFI_EDITOR_LINE *)Line);
    225 }
    226 
    227 /**
    228   Advance/Retreat lines
    229 
    230   @param[in] Count  line number to advance/retreat
    231                        >0 : advance
    232                        <0 : retreat
    233 
    234   @retval NULL An error occured.
    235   @return The line after advance/retreat.
    236 **/
    237 EFI_EDITOR_LINE *
    238 MoveLine (
    239   IN CONST INTN Count
    240   )
    241 {
    242   EFI_EDITOR_LINE *Line;
    243   UINTN           AbsCount;
    244 
    245   //
    246   // if < 0, then retreat
    247   // if > 0, the advance
    248   //
    249   if (Count <= 0) {
    250     AbsCount  = (UINTN)ABS(Count);
    251     Line      = InternalEditorMiscLineRetreat (AbsCount,MainEditor.FileBuffer->CurrentLine,MainEditor.FileBuffer->ListHead);
    252   } else {
    253     Line = InternalEditorMiscLineAdvance ((UINTN)Count,MainEditor.FileBuffer->CurrentLine,MainEditor.FileBuffer->ListHead);
    254   }
    255 
    256   return Line;
    257 }
    258 
    259 /**
    260   Function to update the 'screen' to display the mouse position.
    261 
    262   @retval EFI_SUCCESS           The backup operation was successful.
    263 **/
    264 EFI_STATUS
    265 EFIAPI
    266 FileBufferRestoreMousePosition (
    267   VOID
    268   )
    269 {
    270   EFI_EDITOR_COLOR_UNION  Orig;
    271   EFI_EDITOR_COLOR_UNION  New;
    272   UINTN                   FRow;
    273   UINTN                   FColumn;
    274   BOOLEAN                 HasCharacter;
    275   EFI_EDITOR_LINE         *CurrentLine;
    276   EFI_EDITOR_LINE         *Line;
    277   CHAR16                  Value;
    278 
    279   //
    280   // variable initialization
    281   //
    282   Line = NULL;
    283 
    284   if (MainEditor.MouseSupported) {
    285 
    286     if (FileBufferMouseNeedRefresh) {
    287 
    288       FileBufferMouseNeedRefresh = FALSE;
    289 
    290       //
    291       // if mouse position not moved and only mouse action
    292       // so do not need to refresh mouse position
    293       //
    294       if ((FileBuffer.MousePosition.Row == FileBufferBackupVar.MousePosition.Row &&
    295           FileBuffer.MousePosition.Column == FileBufferBackupVar.MousePosition.Column)
    296           && EditorMouseAction) {
    297         return EFI_SUCCESS;
    298       }
    299       //
    300       // backup the old screen attributes
    301       //
    302       Orig                  = MainEditor.ColorAttributes;
    303       New.Data              = 0;
    304       New.Colors.Foreground = Orig.Colors.Background & 0xF;
    305       New.Colors.Background = Orig.Colors.Foreground & 0x7;
    306 
    307       //
    308       // clear the old mouse position
    309       //
    310       FRow          = FileBuffer.LowVisibleRange.Row + FileBufferBackupVar.MousePosition.Row - 2;
    311 
    312       FColumn       = FileBuffer.LowVisibleRange.Column + FileBufferBackupVar.MousePosition.Column - 1;
    313 
    314       HasCharacter  = TRUE;
    315       if (FRow > FileBuffer.NumLines) {
    316         HasCharacter = FALSE;
    317       } else {
    318         CurrentLine = FileBuffer.CurrentLine;
    319         Line        = MoveLine (FRow - FileBuffer.FilePosition.Row);
    320 
    321         if (Line == NULL || FColumn > Line->Size) {
    322           HasCharacter = FALSE;
    323         }
    324 
    325         FileBuffer.CurrentLine = CurrentLine;
    326       }
    327 
    328       ShellPrintEx (
    329         (INT32)FileBufferBackupVar.MousePosition.Column - 1,
    330         (INT32)FileBufferBackupVar.MousePosition.Row - 1,
    331         L" "
    332         );
    333 
    334       if (HasCharacter) {
    335         Value = (Line->Buffer[FColumn - 1]);
    336         ShellPrintEx (
    337           (INT32)FileBufferBackupVar.MousePosition.Column - 1,
    338           (INT32)FileBufferBackupVar.MousePosition.Row - 1,
    339           L"%c",
    340           Value
    341           );
    342       }
    343       //
    344       // set the new mouse position
    345       //
    346       gST->ConOut->SetAttribute (gST->ConOut, New.Data & 0x7F);
    347 
    348       //
    349       // clear the old mouse position
    350       //
    351       FRow          = FileBuffer.LowVisibleRange.Row + FileBuffer.MousePosition.Row - 2;
    352       FColumn       = FileBuffer.LowVisibleRange.Column + FileBuffer.MousePosition.Column - 1;
    353 
    354       HasCharacter  = TRUE;
    355       if (FRow > FileBuffer.NumLines) {
    356         HasCharacter = FALSE;
    357       } else {
    358         CurrentLine = FileBuffer.CurrentLine;
    359         Line        = MoveLine (FRow - FileBuffer.FilePosition.Row);
    360 
    361         if (Line == NULL || FColumn > Line->Size) {
    362           HasCharacter = FALSE;
    363         }
    364 
    365         FileBuffer.CurrentLine = CurrentLine;
    366       }
    367 
    368       ShellPrintEx (
    369         (INT32)FileBuffer.MousePosition.Column - 1,
    370         (INT32)FileBuffer.MousePosition.Row - 1,
    371         L" "
    372         );
    373 
    374       if (HasCharacter) {
    375         Value = Line->Buffer[FColumn - 1];
    376         ShellPrintEx (
    377           (INT32)FileBuffer.MousePosition.Column - 1,
    378           (INT32)FileBuffer.MousePosition.Row - 1,
    379           L"%c",
    380           Value
    381           );
    382       }
    383       //
    384       // end of HasCharacter
    385       //
    386       gST->ConOut->SetAttribute (gST->ConOut, Orig.Data);
    387     }
    388     //
    389     // end of MouseNeedRefresh
    390     //
    391   }
    392   //
    393   // end of MouseSupported
    394   //
    395   return EFI_SUCCESS;
    396 }
    397 
    398 /**
    399   Free all the lines in FileBuffer
    400    Fields affected:
    401      Lines
    402      CurrentLine
    403      NumLines
    404      ListHead
    405 
    406   @retval EFI_SUCCESS     The operation was successful.
    407 **/
    408 EFI_STATUS
    409 EFIAPI
    410 FileBufferFreeLines (
    411   VOID
    412   )
    413 {
    414   LIST_ENTRY  *Link;
    415   EFI_EDITOR_LINE *Line;
    416 
    417   //
    418   // free all the lines
    419   //
    420   if (FileBuffer.Lines != NULL) {
    421 
    422     Line  = FileBuffer.Lines;
    423     Link  = &(Line->Link);
    424     do {
    425       Line  = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
    426       Link  = Link->ForwardLink;
    427 
    428       //
    429       // free line's buffer and line itself
    430       //
    431       LineFree (Line);
    432     } while (Link != FileBuffer.ListHead);
    433   }
    434   //
    435   // clean the line list related structure
    436   //
    437   FileBuffer.Lines            = NULL;
    438   FileBuffer.CurrentLine      = NULL;
    439   FileBuffer.NumLines         = 0;
    440 
    441   FileBuffer.ListHead->ForwardLink  = FileBuffer.ListHead;
    442   FileBuffer.ListHead->BackLink  = FileBuffer.ListHead;
    443 
    444   return EFI_SUCCESS;
    445 }
    446 
    447 /**
    448   Cleanup function for FileBuffer.
    449 
    450   @retval EFI_SUCCESS   The cleanup was successful.
    451 **/
    452 EFI_STATUS
    453 EFIAPI
    454 FileBufferCleanup (
    455   VOID
    456   )
    457 {
    458   EFI_STATUS  Status;
    459 
    460   SHELL_FREE_NON_NULL (FileBuffer.FileName);
    461 
    462   //
    463   // free all the lines
    464   //
    465   Status = FileBufferFreeLines ();
    466 
    467   SHELL_FREE_NON_NULL (FileBuffer.ListHead);
    468   FileBuffer.ListHead = NULL;
    469 
    470   SHELL_FREE_NON_NULL (FileBufferBackupVar.FileName);
    471   return Status;
    472 
    473 }
    474 
    475 /**
    476   Print a line specified by Line on a row specified by Row of the screen.
    477 
    478   @param[in] Line               The line to print.
    479   @param[in] Row                The row on the screen to print onto (begin from 1).
    480 
    481   @retval EFI_SUCCESS           The printing was successful.
    482 **/
    483 EFI_STATUS
    484 FileBufferPrintLine (
    485   IN CONST EFI_EDITOR_LINE  *Line,
    486   IN CONST UINTN            Row
    487   )
    488 {
    489 
    490   CHAR16  *Buffer;
    491   UINTN   Limit;
    492   CHAR16  *PrintLine;
    493   CHAR16  *PrintLine2;
    494   UINTN   BufLen;
    495 
    496   //
    497   // print start from correct character
    498   //
    499   Buffer  = Line->Buffer + FileBuffer.LowVisibleRange.Column - 1;
    500 
    501   Limit   = Line->Size - FileBuffer.LowVisibleRange.Column + 1;
    502   if (Limit > Line->Size) {
    503     Limit = 0;
    504   }
    505 
    506   BufLen = (MainEditor.ScreenSize.Column + 1) * sizeof (CHAR16);
    507   PrintLine = AllocatePool (BufLen);
    508   ASSERT (PrintLine != NULL);
    509 
    510   StrnCpyS (PrintLine, BufLen/sizeof(CHAR16), Buffer, MIN(Limit, MainEditor.ScreenSize.Column));
    511   for (; Limit < MainEditor.ScreenSize.Column; Limit++) {
    512     PrintLine[Limit] = L' ';
    513   }
    514 
    515   PrintLine[MainEditor.ScreenSize.Column] = CHAR_NULL;
    516 
    517   PrintLine2 = AllocatePool (BufLen * 2);
    518   ASSERT (PrintLine2 != NULL);
    519 
    520   ShellCopySearchAndReplace(PrintLine, PrintLine2, BufLen * 2, L"%", L"^%", FALSE, FALSE);
    521 
    522   ShellPrintEx (
    523     0,
    524     (INT32)Row - 1,
    525     L"%s",
    526     PrintLine2
    527     );
    528 
    529   FreePool (PrintLine);
    530   FreePool (PrintLine2);
    531 
    532   return EFI_SUCCESS;
    533 }
    534 
    535 /**
    536   Set the cursor position according to FileBuffer.DisplayPosition.
    537 
    538   @retval EFI_SUCCESS           The operation was successful.
    539 **/
    540 EFI_STATUS
    541 EFIAPI
    542 FileBufferRestorePosition (
    543   VOID
    544   )
    545 {
    546   //
    547   // set cursor position
    548   //
    549   return (gST->ConOut->SetCursorPosition (
    550         gST->ConOut,
    551         FileBuffer.DisplayPosition.Column - 1,
    552         FileBuffer.DisplayPosition.Row - 1
    553         ));
    554 }
    555 
    556 /**
    557   Refresh the screen with whats in the buffer.
    558 
    559   @retval EFI_SUCCESS     The refresh was successful.
    560   @retval EFI_LOAD_ERROR  There was an error finding what to write.
    561 **/
    562 EFI_STATUS
    563 EFIAPI
    564 FileBufferRefresh (
    565   VOID
    566   )
    567 {
    568   LIST_ENTRY  *Link;
    569   EFI_EDITOR_LINE *Line;
    570   UINTN           Row;
    571 
    572   //
    573   // if it's the first time after editor launch, so should refresh
    574   //
    575   if (!EditorFirst) {
    576     //
    577     // no definite required refresh
    578     // and file position displayed on screen has not been changed
    579     //
    580     if (!FileBufferNeedRefresh &&
    581         !FileBufferOnlyLineNeedRefresh &&
    582         FileBufferBackupVar.LowVisibleRange.Row == FileBuffer.LowVisibleRange.Row &&
    583         FileBufferBackupVar.LowVisibleRange.Column == FileBuffer.LowVisibleRange.Column
    584         ) {
    585 
    586       FileBufferRestoreMousePosition ();
    587       FileBufferRestorePosition ();
    588 
    589       return EFI_SUCCESS;
    590     }
    591   }
    592 
    593   gST->ConOut->EnableCursor (gST->ConOut, FALSE);
    594 
    595   //
    596   // only need to refresh current line
    597   //
    598   if (FileBufferOnlyLineNeedRefresh &&
    599       FileBufferBackupVar.LowVisibleRange.Row == FileBuffer.LowVisibleRange.Row &&
    600       FileBufferBackupVar.LowVisibleRange.Column == FileBuffer.LowVisibleRange.Column
    601       ) {
    602 
    603     EditorClearLine (FileBuffer.DisplayPosition.Row, MainEditor.ScreenSize.Column, MainEditor.ScreenSize.Row);
    604     FileBufferPrintLine (
    605       FileBuffer.CurrentLine,
    606       FileBuffer.DisplayPosition.Row
    607       );
    608   } else {
    609     //
    610     // the whole edit area need refresh
    611     //
    612 
    613     //
    614     // no line
    615     //
    616     if (FileBuffer.Lines == NULL) {
    617       FileBufferRestoreMousePosition ();
    618       FileBufferRestorePosition ();
    619       gST->ConOut->EnableCursor (gST->ConOut, TRUE);
    620 
    621       return EFI_SUCCESS;
    622     }
    623     //
    624     // get the first line that will be displayed
    625     //
    626     Line = MoveLine (FileBuffer.LowVisibleRange.Row - FileBuffer.FilePosition.Row);
    627     if (Line == NULL) {
    628       gST->ConOut->EnableCursor (gST->ConOut, TRUE);
    629 
    630       return EFI_LOAD_ERROR;
    631     }
    632 
    633     Link  = &(Line->Link);
    634     Row   = 2;
    635     do {
    636       Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
    637 
    638       //
    639       // print line at row
    640       //
    641       FileBufferPrintLine (Line, Row);
    642 
    643       Link = Link->ForwardLink;
    644       Row++;
    645     } while (Link != FileBuffer.ListHead && Row <= (MainEditor.ScreenSize.Row - 1));
    646     //
    647     // while not file end and not screen full
    648     //
    649     while (Row <= (MainEditor.ScreenSize.Row - 1)) {
    650       EditorClearLine (Row, MainEditor.ScreenSize.Column, MainEditor.ScreenSize.Row);
    651       Row++;
    652     }
    653   }
    654 
    655   FileBufferRestoreMousePosition ();
    656   FileBufferRestorePosition ();
    657 
    658   FileBufferNeedRefresh         = FALSE;
    659   FileBufferOnlyLineNeedRefresh = FALSE;
    660 
    661   gST->ConOut->EnableCursor (gST->ConOut, TRUE);
    662   return EFI_SUCCESS;
    663 }
    664 
    665 /**
    666   Create a new line and append it to the line list.
    667     Fields affected:
    668       NumLines
    669       Lines
    670 
    671   @retval NULL    The create line failed.
    672   @return         The line created.
    673 **/
    674 EFI_EDITOR_LINE *
    675 EFIAPI
    676 FileBufferCreateLine (
    677   VOID
    678   )
    679 {
    680   EFI_EDITOR_LINE *Line;
    681 
    682   //
    683   // allocate a line structure
    684   //
    685   Line = AllocateZeroPool (sizeof (EFI_EDITOR_LINE));
    686   if (Line == NULL) {
    687     return NULL;
    688   }
    689   //
    690   // initialize the structure
    691   //
    692   Line->Signature = LINE_LIST_SIGNATURE;
    693   Line->Size      = 0;
    694   Line->TotalSize = 0;
    695   Line->Type      = NewLineTypeDefault;
    696 
    697   //
    698   // initial buffer of the line is "\0"
    699   //
    700   ASSERT(CHAR_NULL == CHAR_NULL);
    701   Line->Buffer = CatSPrint (NULL, L"\0");
    702   if (Line->Buffer == NULL) {
    703     return NULL;
    704   }
    705 
    706   FileBuffer.NumLines++;
    707 
    708   //
    709   // insert the line into line list
    710   //
    711   InsertTailList (FileBuffer.ListHead, &Line->Link);
    712 
    713   if (FileBuffer.Lines == NULL) {
    714     FileBuffer.Lines = CR (FileBuffer.ListHead->ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
    715   }
    716 
    717   return Line;
    718 }
    719 
    720 /**
    721   Set FileName field in FileBuffer.
    722 
    723   @param Str                    The file name to set.
    724 
    725   @retval EFI_SUCCESS           The filename was successfully set.
    726   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
    727   @retval EFI_INVALID_PARAMETER Str is not a valid filename.
    728 **/
    729 EFI_STATUS
    730 EFIAPI
    731 FileBufferSetFileName (
    732   IN CONST CHAR16 *Str
    733   )
    734 {
    735   //
    736   // Verify the parameters
    737   //
    738   if (!IsValidFileName(Str)) {
    739     return (EFI_INVALID_PARAMETER);
    740   }
    741   //
    742   // free the old file name
    743   //
    744   SHELL_FREE_NON_NULL (FileBuffer.FileName);
    745 
    746   //
    747   // Allocate and set the new name
    748   //
    749   FileBuffer.FileName = CatSPrint (NULL, L"%s", Str);
    750   if (FileBuffer.FileName == NULL) {
    751     return EFI_OUT_OF_RESOURCES;
    752   }
    753 
    754   return EFI_SUCCESS;
    755 }
    756 /**
    757   Free the existing file lines and reset the modified flag.
    758 
    759   @retval EFI_SUCCESS           The operation was successful.
    760 **/
    761 EFI_STATUS
    762 EFIAPI
    763 FileBufferFree (
    764   VOID
    765   )
    766 {
    767   //
    768   // free all the lines
    769   //
    770   FileBufferFreeLines ();
    771   FileBuffer.FileModified = FALSE;
    772 
    773   return EFI_SUCCESS;
    774 }
    775 
    776 
    777 /**
    778   Read a file from disk into the FileBuffer.
    779 
    780   @param[in] FileName           The filename to read.
    781   @param[in] Recover            TRUE if is for recover mode, no information printouts.
    782 
    783   @retval EFI_SUCCESS            The load was successful.
    784   @retval EFI_LOAD_ERROR         The load failed.
    785   @retval EFI_OUT_OF_RESOURCES   A memory allocation failed.
    786   @retval EFI_INVALID_PARAMETER  FileName is a directory.
    787 **/
    788 EFI_STATUS
    789 EFIAPI
    790 FileBufferRead (
    791   IN CONST CHAR16  *FileName,
    792   IN CONST BOOLEAN Recover
    793   )
    794 {
    795   EFI_EDITOR_LINE                 *Line;
    796   EE_NEWLINE_TYPE                 Type;
    797   UINTN                           LoopVar1;
    798   UINTN                           LoopVar2;
    799   UINTN                           LineSize;
    800   VOID                            *Buffer;
    801   CHAR16                          *UnicodeBuffer;
    802   UINT8                           *AsciiBuffer;
    803   UINTN                           FileSize;
    804   SHELL_FILE_HANDLE               FileHandle;
    805   BOOLEAN                         CreateFile;
    806   EFI_STATUS                      Status;
    807   UINTN                           LineSizeBackup;
    808   EFI_FILE_INFO                   *Info;
    809 
    810   Line          = NULL;
    811   LoopVar1      = 0;
    812   FileSize      = 0;
    813   UnicodeBuffer = NULL;
    814   Type          = NewLineTypeDefault;
    815   FileHandle    = NULL;
    816   CreateFile    = FALSE;
    817 
    818   //
    819   // in this function, when you return error ( except EFI_OUT_OF_RESOURCES )
    820   // you should set status string via StatusBarSetStatusString(L"blah")
    821   // since this function maybe called before the editorhandleinput loop
    822   // so any error will cause editor return
    823   // so if you want to print the error status
    824   // you should set the status string
    825   //
    826 
    827   //
    828   // try to open the file
    829   //
    830   Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ, 0);
    831 
    832   if (!EFI_ERROR(Status)) {
    833     CreateFile = FALSE;
    834     if (FileHandle == NULL) {
    835       StatusBarSetStatusString (L"Disk Error");
    836       return EFI_LOAD_ERROR;
    837     }
    838 
    839     Info = ShellGetFileInfo(FileHandle);
    840 
    841     if (Info->Attribute & EFI_FILE_DIRECTORY) {
    842       StatusBarSetStatusString (L"Directory Can Not Be Edited");
    843       FreePool (Info);
    844       return EFI_INVALID_PARAMETER;
    845     }
    846 
    847     if (Info->Attribute & EFI_FILE_READ_ONLY) {
    848       FileBuffer.ReadOnly = TRUE;
    849     } else {
    850       FileBuffer.ReadOnly = FALSE;
    851     }
    852     //
    853     // get file size
    854     //
    855     FileSize = (UINTN) Info->FileSize;
    856 
    857     FreePool (Info);
    858   } else if (Status == EFI_NOT_FOUND) {
    859     //
    860     // file not exists.  add create and try again
    861     //
    862     Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, 0);
    863     if (EFI_ERROR (Status)) {
    864       if (Status == EFI_WRITE_PROTECTED ||
    865           Status == EFI_ACCESS_DENIED ||
    866           Status == EFI_NO_MEDIA ||
    867           Status == EFI_MEDIA_CHANGED
    868           ) {
    869         StatusBarSetStatusString (L"Access Denied");
    870       } else if (Status == EFI_DEVICE_ERROR || Status == EFI_VOLUME_CORRUPTED || Status == EFI_VOLUME_FULL) {
    871         StatusBarSetStatusString (L"Disk Error");
    872       } else {
    873         StatusBarSetStatusString (L"Invalid File Name or Current-working-directory");
    874       }
    875 
    876       return Status;
    877     } else {
    878       //
    879       // it worked.  now delete it and move on with the name (now validated)
    880       //
    881       Status = ShellDeleteFile (&FileHandle);
    882       if (Status == EFI_WARN_DELETE_FAILURE) {
    883         Status = EFI_ACCESS_DENIED;
    884       }
    885       FileHandle = NULL;
    886       if (EFI_ERROR (Status)) {
    887         StatusBarSetStatusString (L"Access Denied");
    888         return Status;
    889       }
    890     }
    891     //
    892     // file doesn't exist, so set CreateFile to TRUE
    893     //
    894     CreateFile          = TRUE;
    895     FileBuffer.ReadOnly = FALSE;
    896 
    897     //
    898     // all the check ends
    899     // so now begin to set file name, free lines
    900     //
    901     if (StrCmp (FileName, FileBuffer.FileName) != 0) {
    902       FileBufferSetFileName (FileName);
    903     }
    904     //
    905     // free the old lines
    906     //
    907     FileBufferFree ();
    908 
    909   }
    910   //
    911   // the file exists
    912   //
    913   if (!CreateFile) {
    914     //
    915     // allocate buffer to read file
    916     //
    917     Buffer = AllocateZeroPool (FileSize);
    918     if (Buffer == NULL) {
    919       return EFI_OUT_OF_RESOURCES;
    920     }
    921     //
    922     // read file into Buffer
    923     //
    924     Status = ShellReadFile (FileHandle, &FileSize, Buffer);
    925     ShellCloseFile(&FileHandle);
    926     FileHandle = NULL;
    927     if (EFI_ERROR (Status)) {
    928       StatusBarSetStatusString (L"Read File Failed");
    929       SHELL_FREE_NON_NULL (Buffer);
    930       return EFI_LOAD_ERROR;
    931     }
    932     //
    933     // nothing in this file
    934     //
    935     if (FileSize == 0) {
    936       SHELL_FREE_NON_NULL (Buffer);
    937       //
    938       // since has no head, so only can be an ASCII file
    939       //
    940       FileBuffer.FileType = FileTypeAscii;
    941 
    942       goto Done;
    943     }
    944 
    945     AsciiBuffer = Buffer;
    946 
    947     if (FileSize < 2) {
    948       //
    949       // size < Unicode file header, so only can be ASCII file
    950       //
    951       FileBuffer.FileType = FileTypeAscii;
    952     } else {
    953       //
    954       // Unicode file
    955       //
    956       if (*(UINT16 *) Buffer == EFI_UNICODE_BYTE_ORDER_MARK) {
    957         //
    958         // Unicode file's size should be even
    959         //
    960         if ((FileSize % 2) != 0) {
    961           StatusBarSetStatusString (L"File Format Wrong");
    962           SHELL_FREE_NON_NULL (Buffer);
    963           return EFI_LOAD_ERROR;
    964         }
    965 
    966         FileSize /= 2;
    967 
    968         FileBuffer.FileType = FileTypeUnicode;
    969         UnicodeBuffer       = Buffer;
    970 
    971         //
    972         // pass this 0xff and 0xfe
    973         //
    974         UnicodeBuffer++;
    975         FileSize--;
    976       } else {
    977         FileBuffer.FileType = FileTypeAscii;
    978       }
    979       //
    980       // end of AsciiBuffer ==
    981       //
    982     }
    983     //
    984     // end of FileSize < 2
    985     // all the check ends
    986     // so now begin to set file name, free lines
    987     //
    988     if (StrCmp (FileName, FileBuffer.FileName) != 0) {
    989       FileBufferSetFileName (FileName);
    990     }
    991 
    992     //
    993     // free the old lines
    994     //
    995     FileBufferFree ();
    996 
    997     //
    998     // parse file content line by line
    999     //
   1000     for (LoopVar1 = 0; LoopVar1 < FileSize; LoopVar1++) {
   1001       Type = NewLineTypeUnknown;
   1002 
   1003       for (LineSize = LoopVar1; LineSize < FileSize; LineSize++) {
   1004         if (FileBuffer.FileType == FileTypeAscii) {
   1005           if (AsciiBuffer[LineSize] == CHAR_CARRIAGE_RETURN) {
   1006             Type = NewLineTypeCarriageReturn;
   1007 
   1008             //
   1009             // has LF following
   1010             //
   1011             if (LineSize < FileSize - 1) {
   1012               if (AsciiBuffer[LineSize + 1] == CHAR_LINEFEED) {
   1013                 Type = NewLineTypeCarriageReturnLineFeed;
   1014               }
   1015             }
   1016 
   1017             break;
   1018           } else if (AsciiBuffer[LineSize] == CHAR_LINEFEED) {
   1019             Type = NewLineTypeLineFeed;
   1020 
   1021             //
   1022             // has CR following
   1023             //
   1024             if (LineSize < FileSize - 1) {
   1025               if (AsciiBuffer[LineSize + 1] == CHAR_CARRIAGE_RETURN) {
   1026                 Type = NewLineTypeLineFeedCarriageReturn;
   1027               }
   1028             }
   1029 
   1030             break;
   1031           }
   1032         } else {
   1033           if (UnicodeBuffer[LineSize] == CHAR_CARRIAGE_RETURN) {
   1034             Type = NewLineTypeCarriageReturn;
   1035 
   1036             //
   1037             // has LF following
   1038             //
   1039             if (LineSize < FileSize - 1) {
   1040               if (UnicodeBuffer[LineSize + 1] == CHAR_LINEFEED) {
   1041                 Type = NewLineTypeCarriageReturnLineFeed;
   1042               }
   1043             }
   1044 
   1045             break;
   1046           } else if (UnicodeBuffer[LineSize] == CHAR_LINEFEED) {
   1047             Type = NewLineTypeLineFeed;
   1048 
   1049             //
   1050             // has CR following
   1051             //
   1052             if (LineSize < FileSize - 1) {
   1053               if (UnicodeBuffer[LineSize + 1] == CHAR_CARRIAGE_RETURN) {
   1054                 Type = NewLineTypeLineFeedCarriageReturn;
   1055               }
   1056             }
   1057 
   1058             break;
   1059           }
   1060         }
   1061         //
   1062         // endif == ASCII
   1063         //
   1064       }
   1065       //
   1066       // end of for LineSize
   1067       //
   1068       // if the type is wrong, then exit
   1069       //
   1070       if (Type == NewLineTypeUnknown) {
   1071         //
   1072         // Now if Type is NewLineTypeUnknown, it should be file end
   1073         //
   1074         Type = NewLineTypeDefault;
   1075       }
   1076 
   1077       LineSizeBackup = LineSize;
   1078 
   1079       //
   1080       // create a new line
   1081       //
   1082       Line = FileBufferCreateLine ();
   1083       if (Line == NULL) {
   1084         SHELL_FREE_NON_NULL (Buffer);
   1085         return EFI_OUT_OF_RESOURCES;
   1086       }
   1087       //
   1088       // calculate file length
   1089       //
   1090       LineSize -= LoopVar1;
   1091 
   1092       //
   1093       // Unicode and one CHAR_NULL
   1094       //
   1095       SHELL_FREE_NON_NULL (Line->Buffer);
   1096       Line->Buffer = AllocateZeroPool (LineSize * 2 + 2);
   1097 
   1098       if (Line->Buffer == NULL) {
   1099         RemoveEntryList (&Line->Link);
   1100         return EFI_OUT_OF_RESOURCES;
   1101       }
   1102       //
   1103       // copy this line to Line->Buffer
   1104       //
   1105       for (LoopVar2 = 0; LoopVar2 < LineSize; LoopVar2++) {
   1106         if (FileBuffer.FileType == FileTypeAscii) {
   1107           Line->Buffer[LoopVar2] = (CHAR16) AsciiBuffer[LoopVar1];
   1108         } else {
   1109           Line->Buffer[LoopVar2] = UnicodeBuffer[LoopVar1];
   1110         }
   1111 
   1112         LoopVar1++;
   1113       }
   1114       //
   1115       // LoopVar1 now points to where CHAR_CARRIAGE_RETURN or CHAR_LINEFEED;
   1116       //
   1117       Line->Buffer[LineSize]  = 0;
   1118 
   1119       Line->Size              = LineSize;
   1120       Line->TotalSize         = LineSize;
   1121       Line->Type              = Type;
   1122 
   1123       if (Type == NewLineTypeCarriageReturnLineFeed || Type == NewLineTypeLineFeedCarriageReturn) {
   1124         LoopVar1++;
   1125       }
   1126 
   1127       //
   1128       // last character is a return, SO create a new line
   1129       //
   1130       if (((Type == NewLineTypeCarriageReturnLineFeed || Type == NewLineTypeLineFeedCarriageReturn) && LineSizeBackup == FileSize - 2) ||
   1131           ((Type == NewLineTypeLineFeed || Type == NewLineTypeCarriageReturn) && LineSizeBackup == FileSize - 1)
   1132           ) {
   1133         Line = FileBufferCreateLine ();
   1134         if (Line == NULL) {
   1135           SHELL_FREE_NON_NULL (Buffer);
   1136           return EFI_OUT_OF_RESOURCES;
   1137         }
   1138       }
   1139       //
   1140       // end of if
   1141       //
   1142     }
   1143     //
   1144     // end of LoopVar1
   1145     //
   1146     SHELL_FREE_NON_NULL (Buffer);
   1147 
   1148   }
   1149   //
   1150   // end of if CreateFile
   1151   //
   1152 Done:
   1153 
   1154   FileBuffer.DisplayPosition.Row    = 2;
   1155   FileBuffer.DisplayPosition.Column = 1;
   1156   FileBuffer.LowVisibleRange.Row    = 1;
   1157   FileBuffer.LowVisibleRange.Column = 1;
   1158   FileBuffer.FilePosition.Row       = 1;
   1159   FileBuffer.FilePosition.Column    = 1;
   1160   FileBuffer.MousePosition.Row      = 2;
   1161   FileBuffer.MousePosition.Column   = 1;
   1162 
   1163   if (!Recover) {
   1164     UnicodeBuffer = CatSPrint (NULL, L"%d Lines Read", FileBuffer.NumLines);
   1165     if (UnicodeBuffer == NULL) {
   1166       return EFI_OUT_OF_RESOURCES;
   1167     }
   1168 
   1169     StatusBarSetStatusString (UnicodeBuffer);
   1170     FreePool (UnicodeBuffer);
   1171   }
   1172 /*
   1173     //
   1174     // check whether we have fs?: in filename
   1175     //
   1176     LoopVar1             = 0;
   1177     FSMappingPtr  = NULL;
   1178     while (FileName[LoopVar1] != 0) {
   1179       if (FileName[LoopVar1] == L':') {
   1180         FSMappingPtr = &FileName[LoopVar1];
   1181         break;
   1182       }
   1183 
   1184       LoopVar1++;
   1185     }
   1186 
   1187     if (FSMappingPtr == NULL) {
   1188       CurDir = ShellGetCurrentDir (NULL);
   1189     } else {
   1190       LoopVar1 = 0;
   1191       LoopVar2 = 0;
   1192       while (FileName[LoopVar1] != 0) {
   1193         if (FileName[LoopVar1] == L':') {
   1194           break;
   1195         }
   1196 
   1197         FSMapping[LoopVar2++] = FileName[LoopVar1];
   1198 
   1199         LoopVar1++;
   1200       }
   1201 
   1202       FSMapping[LoopVar2]  = 0;
   1203       CurDir        = ShellGetCurrentDir (FSMapping);
   1204     }
   1205 
   1206     if (CurDir != NULL) {
   1207       for (LoopVar1 = 0; LoopVar1 < StrLen (CurDir) && CurDir[LoopVar1] != ':'; LoopVar1++);
   1208 
   1209       CurDir[LoopVar1]   = 0;
   1210       DevicePath  = (EFI_DEVICE_PATH_PROTOCOL *) ShellGetMap (CurDir);
   1211       FreePool (CurDir);
   1212     } else {
   1213       return EFI_LOAD_ERROR;
   1214     }
   1215 
   1216     Status = LibDevicePathToInterface (
   1217               &gEfiSimpleFileSystemProtocolGuid,
   1218               DevicePath,
   1219               (VOID **) &Vol
   1220               );
   1221     if (EFI_ERROR (Status)) {
   1222       return EFI_LOAD_ERROR;
   1223     }
   1224 
   1225     Status = Vol->OpenVolume (Vol, &RootFs);
   1226     if (EFI_ERROR (Status)) {
   1227       return EFI_LOAD_ERROR;
   1228     }
   1229     //
   1230     // Get volume information of file system
   1231     //
   1232     Size        = SIZE_OF_EFI_FILE_SYSTEM_INFO + 100;
   1233     VolumeInfo  = (EFI_FILE_SYSTEM_INFO *) AllocateZeroPool (Size);
   1234     Status      = RootFs->GetInfo (RootFs, &gEfiFileSystemInfoGuid, &Size, VolumeInfo);
   1235     if (EFI_ERROR (Status)) {
   1236       RootFs->Close (RootFs);
   1237       return EFI_LOAD_ERROR;
   1238     }
   1239 
   1240     if (VolumeInfo->ReadOnly) {
   1241       StatusBarSetStatusString (L"WARNING: Volume Read Only");
   1242     }
   1243 
   1244     FreePool (VolumeInfo);
   1245     RootFs->Close (RootFs);
   1246   }
   1247 //
   1248 */
   1249   //
   1250   // has line
   1251   //
   1252   if (FileBuffer.Lines != 0) {
   1253     FileBuffer.CurrentLine = CR (FileBuffer.ListHead->ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
   1254   } else {
   1255     //
   1256     // create a dummy line
   1257     //
   1258     Line = FileBufferCreateLine ();
   1259     if (Line == NULL) {
   1260       return EFI_OUT_OF_RESOURCES;
   1261     }
   1262 
   1263     FileBuffer.CurrentLine = Line;
   1264   }
   1265 
   1266   FileBuffer.FileModified       = FALSE;
   1267   FileBufferNeedRefresh         = TRUE;
   1268   FileBufferOnlyLineNeedRefresh = FALSE;
   1269   FileBufferMouseNeedRefresh    = TRUE;
   1270 
   1271 
   1272   return EFI_SUCCESS;
   1273 }
   1274 
   1275 /**
   1276   According to FileBuffer.NewLineType & FileBuffer.FileType,
   1277   get the return buffer and size.
   1278 
   1279   @param[in] Type               The type of line.
   1280   @param[out] Buffer            The buffer to fill.
   1281   @param[out] Size              The amount of the buffer used on return.
   1282 **/
   1283 VOID
   1284 EFIAPI
   1285 GetNewLine (
   1286   IN CONST EE_NEWLINE_TYPE Type,
   1287   OUT CHAR8           *Buffer,
   1288   OUT UINT8           *Size
   1289   )
   1290 {
   1291   UINT8 NewLineSize;
   1292 
   1293   //
   1294   // give new line buffer,
   1295   // and will judge unicode or ascii
   1296   //
   1297   NewLineSize = 0;
   1298 
   1299   //
   1300   // not legal new line type
   1301   //
   1302   if (Type != NewLineTypeLineFeed && Type != NewLineTypeCarriageReturn && Type != NewLineTypeCarriageReturnLineFeed && Type != NewLineTypeLineFeedCarriageReturn) {
   1303     *Size = 0;
   1304     return ;
   1305   }
   1306   //
   1307   // use_cr: give 0x0d
   1308   //
   1309   if (Type == NewLineTypeCarriageReturn) {
   1310     if (MainEditor.FileBuffer->FileType == FileTypeUnicode) {
   1311       Buffer[0]   = 0x0d;
   1312       Buffer[1]   = 0;
   1313       NewLineSize = 2;
   1314     } else {
   1315       Buffer[0]   = 0x0d;
   1316       NewLineSize = 1;
   1317     }
   1318 
   1319     *Size = NewLineSize;
   1320     return ;
   1321   }
   1322   //
   1323   // use_lf: give 0x0a
   1324   //
   1325   if (Type == NewLineTypeLineFeed) {
   1326     if (MainEditor.FileBuffer->FileType == FileTypeUnicode) {
   1327       Buffer[0]   = 0x0a;
   1328       Buffer[1]   = 0;
   1329       NewLineSize = 2;
   1330     } else {
   1331       Buffer[0]   = 0x0a;
   1332       NewLineSize = 1;
   1333     }
   1334 
   1335     *Size = NewLineSize;
   1336     return ;
   1337   }
   1338   //
   1339   // use_crlf: give 0x0d 0x0a
   1340   //
   1341   if (Type == NewLineTypeCarriageReturnLineFeed) {
   1342     if (MainEditor.FileBuffer->FileType == FileTypeUnicode) {
   1343       Buffer[0]   = 0x0d;
   1344       Buffer[1]   = 0;
   1345       Buffer[2]   = 0x0a;
   1346       Buffer[3]   = 0;
   1347 
   1348       NewLineSize = 4;
   1349     } else {
   1350       Buffer[0]   = 0x0d;
   1351       Buffer[1]   = 0x0a;
   1352       NewLineSize = 2;
   1353     }
   1354 
   1355     *Size = NewLineSize;
   1356     return ;
   1357   }
   1358   //
   1359   // use_lfcr: give 0x0a 0x0d
   1360   //
   1361   if (Type == NewLineTypeLineFeedCarriageReturn) {
   1362     if (MainEditor.FileBuffer->FileType == FileTypeUnicode) {
   1363       Buffer[0]   = 0x0a;
   1364       Buffer[1]   = 0;
   1365       Buffer[2]   = 0x0d;
   1366       Buffer[3]   = 0;
   1367 
   1368       NewLineSize = 4;
   1369     } else {
   1370       Buffer[0]   = 0x0a;
   1371       Buffer[1]   = 0x0d;
   1372       NewLineSize = 2;
   1373     }
   1374 
   1375     *Size = NewLineSize;
   1376     return ;
   1377   }
   1378 
   1379 }
   1380 
   1381 /**
   1382   Change a Unicode string to an ASCII string.
   1383 
   1384   @param[in] UStr     The Unicode string.
   1385   @param[in] Length   The maximum size of AStr.
   1386   @param[out] AStr    ASCII string to pass out.
   1387 
   1388   @return The actuall length.
   1389 **/
   1390 UINTN
   1391 EFIAPI
   1392 UnicodeToAscii (
   1393   IN CONST CHAR16   *UStr,
   1394   IN CONST UINTN    Length,
   1395   OUT CHAR8         *AStr
   1396   )
   1397 {
   1398   UINTN Index;
   1399 
   1400   //
   1401   // just buffer copy, not character copy
   1402   //
   1403   for (Index = 0; Index < Length; Index++) {
   1404     *AStr++ = (CHAR8) *UStr++;
   1405   }
   1406 
   1407   return Index;
   1408 }
   1409 
   1410 /**
   1411   Save lines in FileBuffer to disk
   1412 
   1413   @param[in] FileName           The file name for writing.
   1414 
   1415   @retval EFI_SUCCESS           Data was written.
   1416   @retval EFI_LOAD_ERROR
   1417   @retval EFI_OUT_OF_RESOURCES  There were not enough resources to write the file.
   1418 **/
   1419 EFI_STATUS
   1420 EFIAPI
   1421 FileBufferSave (
   1422   IN CONST CHAR16 *FileName
   1423   )
   1424 {
   1425   SHELL_FILE_HANDLE FileHandle;
   1426   LIST_ENTRY        *Link;
   1427   EFI_EDITOR_LINE   *Line;
   1428   CHAR16            *Str;
   1429 
   1430   EFI_STATUS        Status;
   1431   UINTN             Length;
   1432   UINTN             NumLines;
   1433   CHAR8             NewLineBuffer[4];
   1434   UINT8             NewLineSize;
   1435 
   1436   EFI_FILE_INFO     *Info;
   1437 
   1438   UINT64            Attribute;
   1439 
   1440   EE_NEWLINE_TYPE   Type;
   1441 
   1442   UINTN             TotalSize;
   1443   //
   1444   // 2M
   1445   //
   1446   CHAR8             *Cache;
   1447   UINTN             LeftSize;
   1448   UINTN             Size;
   1449   CHAR8             *Ptr;
   1450 
   1451   Length    = 0;
   1452   //
   1453   // 2M
   1454   //
   1455   TotalSize = 0x200000;
   1456 
   1457   Attribute = 0;
   1458 
   1459 
   1460 
   1461   //
   1462   // if is the old file
   1463   //
   1464   if (FileBuffer.FileName != NULL && StrCmp (FileName, FileBuffer.FileName) == 0) {
   1465     //
   1466     // file has not been modified
   1467     //
   1468     if (!FileBuffer.FileModified) {
   1469       return EFI_SUCCESS;
   1470     }
   1471 
   1472     //
   1473     // if file is read-only, set error
   1474     //
   1475     if (FileBuffer.ReadOnly) {
   1476       StatusBarSetStatusString (L"Read Only File Can Not Be Saved");
   1477       return EFI_SUCCESS;
   1478     }
   1479   }
   1480 
   1481   Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0);
   1482 
   1483   if (!EFI_ERROR (Status)) {
   1484     Info = ShellGetFileInfo(FileHandle);
   1485 
   1486     if (Info != NULL && Info->Attribute & EFI_FILE_DIRECTORY) {
   1487       StatusBarSetStatusString (L"Directory Can Not Be Saved");
   1488       ShellCloseFile(FileHandle);
   1489       FreePool(Info);
   1490       return EFI_LOAD_ERROR;
   1491     }
   1492 
   1493     if (Info != NULL) {
   1494       Attribute = Info->Attribute & ~EFI_FILE_READ_ONLY;
   1495       FreePool(Info);
   1496     }
   1497 
   1498     //
   1499     // if file exits, so delete it
   1500     //
   1501     Status = ShellDeleteFile (&FileHandle);
   1502     if (EFI_ERROR (Status) || Status == EFI_WARN_DELETE_FAILURE) {
   1503       StatusBarSetStatusString (L"Write File Failed");
   1504       return EFI_LOAD_ERROR;
   1505     }
   1506  }
   1507 
   1508   Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, Attribute);
   1509 
   1510   if (EFI_ERROR (Status)) {
   1511     StatusBarSetStatusString (L"Create File Failed");
   1512     return EFI_LOAD_ERROR;
   1513   }
   1514 
   1515   //
   1516   // if file is Unicode file, write Unicode header to it.
   1517   //
   1518   if (FileBuffer.FileType == FileTypeUnicode) {
   1519     Length  = 2;
   1520     Status  = ShellWriteFile (FileHandle, &Length, (VOID*)&gUnicodeFileTag);
   1521     if (EFI_ERROR (Status)) {
   1522       ShellDeleteFile (&FileHandle);
   1523       return EFI_LOAD_ERROR;
   1524     }
   1525   }
   1526 
   1527   Cache = AllocateZeroPool (TotalSize);
   1528   if (Cache == NULL) {
   1529     ShellDeleteFile (&FileHandle);
   1530     return EFI_OUT_OF_RESOURCES;
   1531   }
   1532 
   1533   //
   1534   // write all the lines back to disk
   1535   //
   1536   NumLines  = 0;
   1537   Type      = NewLineTypeCarriageReturnLineFeed;
   1538 
   1539   Ptr       = Cache;
   1540   LeftSize  = TotalSize;
   1541 
   1542   for (Link = FileBuffer.ListHead->ForwardLink; Link != FileBuffer.ListHead; Link = Link->ForwardLink) {
   1543     Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
   1544 
   1545     if (Line->Type != NewLineTypeDefault) {
   1546       Type = Line->Type;
   1547     }
   1548     //
   1549     // newline character is at most 4 bytes ( two Unicode characters )
   1550     //
   1551     Length = 4;
   1552     if (Line->Buffer != NULL && Line->Size != 0) {
   1553       if (FileBuffer.FileType == FileTypeAscii) {
   1554         Length += Line->Size;
   1555       } else {
   1556         Length += (Line->Size * 2);
   1557       }
   1558       //
   1559       // end if FileTypeAscii
   1560       //
   1561     }
   1562 
   1563     //
   1564     // no cache room left, so write cache to disk
   1565     //
   1566     if (LeftSize < Length) {
   1567       Size    = TotalSize - LeftSize;
   1568       Status  = ShellWriteFile (FileHandle, &Size, Cache);
   1569       if (EFI_ERROR (Status)) {
   1570         ShellDeleteFile (&FileHandle);
   1571         FreePool (Cache);
   1572         return EFI_LOAD_ERROR;
   1573       }
   1574       Ptr       = Cache;
   1575       LeftSize  = TotalSize;
   1576     }
   1577 
   1578     if (Line->Buffer != NULL && Line->Size != 0) {
   1579       if (FileBuffer.FileType == FileTypeAscii) {
   1580         UnicodeToAscii (Line->Buffer, Line->Size, Ptr);
   1581         Length = Line->Size;
   1582       } else {
   1583         Length = (Line->Size * 2);
   1584         CopyMem (Ptr, (CHAR8 *) Line->Buffer, Length);
   1585       }
   1586       //
   1587       // end if FileTypeAscii
   1588       //
   1589       Ptr += Length;
   1590       LeftSize -= Length;
   1591 
   1592     }
   1593     //
   1594     // end of if Line -> Buffer != NULL && Line -> Size != 0
   1595     //
   1596     // if not the last line , write return buffer to disk
   1597     //
   1598     if (Link->ForwardLink != FileBuffer.ListHead) {
   1599       GetNewLine (Type, NewLineBuffer, &NewLineSize);
   1600       CopyMem (Ptr, (CHAR8 *) NewLineBuffer, NewLineSize);
   1601 
   1602       Ptr += NewLineSize;
   1603       LeftSize -= NewLineSize;
   1604     }
   1605 
   1606     NumLines++;
   1607   }
   1608 
   1609   if (TotalSize != LeftSize) {
   1610     Size    = TotalSize - LeftSize;
   1611     Status  = ShellWriteFile (FileHandle, &Size, Cache);
   1612     if (EFI_ERROR (Status)) {
   1613       ShellDeleteFile (&FileHandle);
   1614       FreePool (Cache);
   1615       return EFI_LOAD_ERROR;
   1616     }
   1617   }
   1618 
   1619   FreePool (Cache);
   1620 
   1621   ShellCloseFile(&FileHandle);
   1622 
   1623   FileBuffer.FileModified = FALSE;
   1624 
   1625   //
   1626   // set status string
   1627   //
   1628   Str = CatSPrint (NULL, L"%d Lines Wrote", NumLines);
   1629   if (Str == NULL) {
   1630     return EFI_OUT_OF_RESOURCES;
   1631   }
   1632 
   1633   StatusBarSetStatusString (Str);
   1634   SHELL_FREE_NON_NULL (Str);
   1635 
   1636   //
   1637   // now everything is ready , you can set the new file name to filebuffer
   1638   //
   1639   if (FileName != NULL && FileBuffer.FileName != NULL && StrCmp (FileName, FileBuffer.FileName) != 0) {
   1640     //
   1641     // not the same
   1642     //
   1643     FileBufferSetFileName (FileName);
   1644     if (FileBuffer.FileName == NULL) {
   1645       ShellDeleteFile (&FileHandle);
   1646       return EFI_OUT_OF_RESOURCES;
   1647     }
   1648   }
   1649 
   1650   FileBuffer.ReadOnly = FALSE;
   1651   return EFI_SUCCESS;
   1652 }
   1653 
   1654 /**
   1655   Scroll cursor to left 1 character position.
   1656 
   1657   @retval EFI_SUCCESS     The operation was successful.
   1658 **/
   1659 EFI_STATUS
   1660 EFIAPI
   1661 FileBufferScrollLeft (
   1662   VOID
   1663   )
   1664 {
   1665   EFI_EDITOR_LINE *Line;
   1666   UINTN           FRow;
   1667   UINTN           FCol;
   1668 
   1669   Line  = FileBuffer.CurrentLine;
   1670 
   1671   FRow  = FileBuffer.FilePosition.Row;
   1672   FCol  = FileBuffer.FilePosition.Column;
   1673 
   1674   //
   1675   // if already at start of this line, so move to the end of previous line
   1676   //
   1677   if (FCol <= 1) {
   1678     //
   1679     // has previous line
   1680     //
   1681     if (Line->Link.BackLink != FileBuffer.ListHead) {
   1682       FRow--;
   1683       Line  = CR (Line->Link.BackLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
   1684       FCol  = Line->Size + 1;
   1685     } else {
   1686       return EFI_SUCCESS;
   1687     }
   1688   } else {
   1689     //
   1690     // if not at start of this line, just move to previous column
   1691     //
   1692     FCol--;
   1693   }
   1694 
   1695   FileBufferMovePosition (FRow, FCol);
   1696 
   1697   return EFI_SUCCESS;
   1698 }
   1699 
   1700 /**
   1701   Delete a char in line
   1702 
   1703   @param[in, out] Line   The line to delete in.
   1704   @param[in] Pos         Position to delete the char at ( start from 0 ).
   1705 **/
   1706 VOID
   1707 EFIAPI
   1708 LineDeleteAt (
   1709   IN  OUT EFI_EDITOR_LINE       *Line,
   1710   IN      UINTN                 Pos
   1711   )
   1712 {
   1713   UINTN Index;
   1714 
   1715   //
   1716   // move the latter characters front
   1717   //
   1718   for (Index = Pos - 1; Index < Line->Size; Index++) {
   1719     Line->Buffer[Index] = Line->Buffer[Index + 1];
   1720   }
   1721 
   1722   Line->Size--;
   1723 }
   1724 
   1725 /**
   1726   Concatenate Src into Dest.
   1727 
   1728   @param[in, out] Dest   Destination string
   1729   @param[in] Src         Src String.
   1730 **/
   1731 VOID
   1732 EFIAPI
   1733 LineCat (
   1734   IN  OUT EFI_EDITOR_LINE *Dest,
   1735   IN      EFI_EDITOR_LINE *Src
   1736   )
   1737 {
   1738   CHAR16  *Str;
   1739   UINTN   Size;
   1740 
   1741   Size                = Dest->Size;
   1742 
   1743   Dest->Buffer[Size]  = 0;
   1744 
   1745   //
   1746   // concatenate the two strings
   1747   //
   1748   Str = CatSPrint (NULL, L"%s%s", Dest->Buffer, Src->Buffer);
   1749   if (Str == NULL) {
   1750     Dest->Buffer = NULL;
   1751     return ;
   1752   }
   1753 
   1754   Dest->Size      = Size + Src->Size;
   1755   Dest->TotalSize = Dest->Size;
   1756 
   1757   FreePool (Dest->Buffer);
   1758   FreePool (Src->Buffer);
   1759 
   1760   //
   1761   // put str to dest->buffer
   1762   //
   1763   Dest->Buffer = Str;
   1764 }
   1765 
   1766 /**
   1767   Delete the previous character.
   1768 
   1769   @retval EFI_SUCCESS           The delete was successful.
   1770   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
   1771 **/
   1772 EFI_STATUS
   1773 EFIAPI
   1774 FileBufferDoBackspace (
   1775   VOID
   1776   )
   1777 {
   1778   EFI_EDITOR_LINE *Line;
   1779   EFI_EDITOR_LINE *End;
   1780   LIST_ENTRY  *Link;
   1781   UINTN           FileColumn;
   1782 
   1783   FileColumn  = FileBuffer.FilePosition.Column;
   1784 
   1785   Line        = FileBuffer.CurrentLine;
   1786 
   1787   //
   1788   // the first column
   1789   //
   1790   if (FileColumn == 1) {
   1791     //
   1792     // the first row
   1793     //
   1794     if (FileBuffer.FilePosition.Row == 1) {
   1795       return EFI_SUCCESS;
   1796     }
   1797 
   1798     FileBufferScrollLeft ();
   1799 
   1800     Line  = FileBuffer.CurrentLine;
   1801     Link  = Line->Link.ForwardLink;
   1802     End   = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
   1803 
   1804     //
   1805     // concatenate this line with previous line
   1806     //
   1807     LineCat (Line, End);
   1808     if (Line->Buffer == NULL) {
   1809       return EFI_OUT_OF_RESOURCES;
   1810     }
   1811     //
   1812     // remove End from line list
   1813     //
   1814     RemoveEntryList (&End->Link);
   1815     FreePool (End);
   1816 
   1817     FileBuffer.NumLines--;
   1818 
   1819     FileBufferNeedRefresh         = TRUE;
   1820     FileBufferOnlyLineNeedRefresh = FALSE;
   1821 
   1822   } else {
   1823     //
   1824     // just delete the previous character
   1825     //
   1826     LineDeleteAt (Line, FileColumn - 1);
   1827     FileBufferScrollLeft ();
   1828     FileBufferOnlyLineNeedRefresh = TRUE;
   1829   }
   1830 
   1831   if (!FileBuffer.FileModified) {
   1832     FileBuffer.FileModified = TRUE;
   1833   }
   1834 
   1835   return EFI_SUCCESS;
   1836 }
   1837 
   1838 /**
   1839   Add a return into line at current position.
   1840 
   1841   @retval EFI_SUCCESS           The insetrion of the character was successful.
   1842   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
   1843 **/
   1844 EFI_STATUS
   1845 EFIAPI
   1846 FileBufferDoReturn (
   1847   VOID
   1848   )
   1849 {
   1850   EFI_EDITOR_LINE *Line;
   1851   EFI_EDITOR_LINE *NewLine;
   1852   UINTN           FileColumn;
   1853   UINTN           Index;
   1854   CHAR16          *Buffer;
   1855   UINTN           Row;
   1856   UINTN           Col;
   1857 
   1858   FileBufferNeedRefresh         = TRUE;
   1859   FileBufferOnlyLineNeedRefresh = FALSE;
   1860 
   1861   Line                          = FileBuffer.CurrentLine;
   1862 
   1863   FileColumn                    = FileBuffer.FilePosition.Column;
   1864 
   1865   NewLine                       = AllocateZeroPool (sizeof (EFI_EDITOR_LINE));
   1866   if (NewLine == NULL) {
   1867     return EFI_OUT_OF_RESOURCES;
   1868   }
   1869 
   1870   NewLine->Signature  = LINE_LIST_SIGNATURE;
   1871   NewLine->Size       = Line->Size - FileColumn + 1;
   1872   NewLine->TotalSize  = NewLine->Size;
   1873   NewLine->Buffer     = CatSPrint (NULL, L"\0");
   1874   if (NewLine->Buffer == NULL) {
   1875     return EFI_OUT_OF_RESOURCES;
   1876   }
   1877 
   1878   NewLine->Type = NewLineTypeDefault;
   1879 
   1880   if (NewLine->Size > 0) {
   1881     //
   1882     // UNICODE + CHAR_NULL
   1883     //
   1884     Buffer = AllocateZeroPool (2 * (NewLine->Size + 1));
   1885     if (Buffer == NULL) {
   1886       FreePool (NewLine->Buffer);
   1887       FreePool (NewLine);
   1888       return EFI_OUT_OF_RESOURCES;
   1889     }
   1890 
   1891     FreePool (NewLine->Buffer);
   1892 
   1893     NewLine->Buffer = Buffer;
   1894 
   1895     for (Index = 0; Index < NewLine->Size; Index++) {
   1896       NewLine->Buffer[Index] = Line->Buffer[Index + FileColumn - 1];
   1897     }
   1898 
   1899     NewLine->Buffer[NewLine->Size]  = CHAR_NULL;
   1900 
   1901     Line->Buffer[FileColumn - 1]    = CHAR_NULL;
   1902     Line->Size                      = FileColumn - 1;
   1903   }
   1904   //
   1905   // increase NumLines
   1906   //
   1907   FileBuffer.NumLines++;
   1908 
   1909   //
   1910   // insert it into the correct position of line list
   1911   //
   1912   NewLine->Link.BackLink     = &(Line->Link);
   1913   NewLine->Link.ForwardLink     = Line->Link.ForwardLink;
   1914   Line->Link.ForwardLink->BackLink = &(NewLine->Link);
   1915   Line->Link.ForwardLink        = &(NewLine->Link);
   1916 
   1917   //
   1918   // move cursor to the start of next line
   1919   //
   1920   Row = FileBuffer.FilePosition.Row + 1;
   1921   Col = 1;
   1922 
   1923   FileBufferMovePosition (Row, Col);
   1924 
   1925   //
   1926   // set file is modified
   1927   //
   1928   if (!FileBuffer.FileModified) {
   1929     FileBuffer.FileModified = TRUE;
   1930   }
   1931 
   1932   return EFI_SUCCESS;
   1933 }
   1934 
   1935 /**
   1936   Delete current character from current line.  This is the effect caused
   1937   by the 'del' key.
   1938 
   1939   @retval EFI_SUCCESS
   1940 **/
   1941 EFI_STATUS
   1942 EFIAPI
   1943 FileBufferDoDelete (
   1944   VOID
   1945   )
   1946 {
   1947   EFI_EDITOR_LINE *Line;
   1948   EFI_EDITOR_LINE *Next;
   1949   LIST_ENTRY  *Link;
   1950   UINTN           FileColumn;
   1951 
   1952   Line        = FileBuffer.CurrentLine;
   1953   FileColumn  = FileBuffer.FilePosition.Column;
   1954 
   1955   //
   1956   // the last column
   1957   //
   1958   if (FileColumn >= Line->Size + 1) {
   1959     //
   1960     // the last line
   1961     //
   1962     if (Line->Link.ForwardLink == FileBuffer.ListHead) {
   1963       return EFI_SUCCESS;
   1964     }
   1965     //
   1966     // since last character,
   1967     // so will add the next line to this line
   1968     //
   1969     Link  = Line->Link.ForwardLink;
   1970     Next  = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
   1971     LineCat (Line, Next);
   1972     if (Line->Buffer == NULL) {
   1973       return EFI_OUT_OF_RESOURCES;
   1974     }
   1975 
   1976     RemoveEntryList (&Next->Link);
   1977     FreePool (Next);
   1978 
   1979     FileBuffer.NumLines--;
   1980 
   1981     FileBufferNeedRefresh         = TRUE;
   1982     FileBufferOnlyLineNeedRefresh = FALSE;
   1983 
   1984   } else {
   1985     //
   1986     // just delete current character
   1987     //
   1988     LineDeleteAt (Line, FileColumn);
   1989     FileBufferOnlyLineNeedRefresh = TRUE;
   1990   }
   1991 
   1992   if (!FileBuffer.FileModified) {
   1993     FileBuffer.FileModified = TRUE;
   1994   }
   1995 
   1996   return EFI_SUCCESS;
   1997 }
   1998 
   1999 /**
   2000   Scroll cursor to right 1 character.
   2001 
   2002   @retval EFI_SUCCESS     The operation was successful.
   2003 **/
   2004 EFI_STATUS
   2005 EFIAPI
   2006 FileBufferScrollRight (
   2007   VOID
   2008   )
   2009 {
   2010   EFI_EDITOR_LINE *Line;
   2011   UINTN           FRow;
   2012   UINTN           FCol;
   2013 
   2014   Line = FileBuffer.CurrentLine;
   2015   if (Line->Buffer == NULL) {
   2016     return EFI_SUCCESS;
   2017   }
   2018 
   2019   FRow  = FileBuffer.FilePosition.Row;
   2020   FCol  = FileBuffer.FilePosition.Column;
   2021 
   2022   //
   2023   // if already at end of this line, scroll it to the start of next line
   2024   //
   2025   if (FCol > Line->Size) {
   2026     //
   2027     // has next line
   2028     //
   2029     if (Line->Link.ForwardLink != FileBuffer.ListHead) {
   2030       FRow++;
   2031       FCol = 1;
   2032     } else {
   2033       return EFI_SUCCESS;
   2034     }
   2035   } else {
   2036     //
   2037     // if not at end of this line, just move to next column
   2038     //
   2039     FCol++;
   2040   }
   2041 
   2042   FileBufferMovePosition (FRow, FCol);
   2043 
   2044   return EFI_SUCCESS;
   2045 }
   2046 
   2047 /**
   2048   Insert a char into line
   2049 
   2050 
   2051   @param[in] Line     The line to insert into.
   2052   @param[in] Char     The char to insert.
   2053   @param[in] Pos      The position to insert the char at ( start from 0 ).
   2054   @param[in] StrSize  The current string size ( include CHAR_NULL ),unit is Unicode character.
   2055 
   2056   @return The new string size ( include CHAR_NULL ) ( unit is Unicode character ).
   2057 **/
   2058 UINTN
   2059 EFIAPI
   2060 LineStrInsert (
   2061   IN      EFI_EDITOR_LINE  *Line,
   2062   IN      CHAR16           Char,
   2063   IN      UINTN            Pos,
   2064   IN      UINTN            StrSize
   2065   )
   2066 {
   2067   UINTN   Index;
   2068   CHAR16  *TempStringPtr;
   2069   CHAR16  *Str;
   2070 
   2071   Index = (StrSize) * 2;
   2072 
   2073   Str   = Line->Buffer;
   2074 
   2075   //
   2076   // do not have free space
   2077   //
   2078   if (Line->TotalSize <= Line->Size) {
   2079     Str = ReallocatePool (Index, Index + 16, Str);
   2080     if (Str == NULL) {
   2081       return 0;
   2082     }
   2083 
   2084     Line->TotalSize += 8;
   2085   }
   2086   //
   2087   // move the later part of the string one character right
   2088   //
   2089   TempStringPtr = Str;
   2090   for (Index = StrSize; Index > Pos; Index--) {
   2091     TempStringPtr[Index] = TempStringPtr[Index - 1];
   2092   }
   2093   //
   2094   // insert char into it.
   2095   //
   2096   TempStringPtr[Index]      = Char;
   2097 
   2098   Line->Buffer  = Str;
   2099   Line->Size++;
   2100 
   2101   return StrSize + 1;
   2102 }
   2103 
   2104 /**
   2105   Add a character to the current line.
   2106 
   2107   @param[in] Char               The Character to input.
   2108 
   2109   @retval EFI_SUCCESS           The input was succesful.
   2110 **/
   2111 EFI_STATUS
   2112 EFIAPI
   2113 FileBufferAddChar (
   2114   IN  CHAR16  Char
   2115   )
   2116 {
   2117   EFI_EDITOR_LINE *Line;
   2118   UINTN           FilePos;
   2119 
   2120   Line = FileBuffer.CurrentLine;
   2121 
   2122   //
   2123   // only needs to refresh current line
   2124   //
   2125   FileBufferOnlyLineNeedRefresh = TRUE;
   2126 
   2127   //
   2128   // when is insert mode, or cursor is at end of this line,
   2129   // so insert this character
   2130   // or replace the character.
   2131   //
   2132   FilePos = FileBuffer.FilePosition.Column - 1;
   2133   if (FileBuffer.ModeInsert || FilePos + 1 > Line->Size) {
   2134     LineStrInsert (Line, Char, FilePos, Line->Size + 1);
   2135   } else {
   2136     Line->Buffer[FilePos] = Char;
   2137   }
   2138   //
   2139   // move cursor to right
   2140   //
   2141   FileBufferScrollRight ();
   2142 
   2143   if (!FileBuffer.FileModified) {
   2144     FileBuffer.FileModified = TRUE;
   2145   }
   2146 
   2147   return EFI_SUCCESS;
   2148 }
   2149 
   2150 /**
   2151   Handles inputs from characters (ASCII key + Backspace + return)
   2152 
   2153   @param[in] Char               The input character.
   2154 
   2155   @retval EFI_SUCCESS           The operation was successful.
   2156   @retval EFI_LOAD_ERROR        There was an error.
   2157   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
   2158 **/
   2159 EFI_STATUS
   2160 EFIAPI
   2161 FileBufferDoCharInput (
   2162   IN CONST CHAR16 Char
   2163   )
   2164 {
   2165   EFI_STATUS  Status;
   2166 
   2167   Status = EFI_SUCCESS;
   2168 
   2169   switch (Char) {
   2170   case CHAR_NULL:
   2171     break;
   2172 
   2173   case CHAR_BACKSPACE:
   2174     Status = FileBufferDoBackspace ();
   2175     break;
   2176 
   2177   case CHAR_TAB:
   2178     //
   2179     // Tabs are ignored
   2180     //
   2181     break;
   2182 
   2183   case CHAR_LINEFEED:
   2184   case CHAR_CARRIAGE_RETURN:
   2185     Status = FileBufferDoReturn ();
   2186     break;
   2187 
   2188   default:
   2189     //
   2190     // DEAL WITH ASCII CHAR, filter out thing like ctrl+f
   2191     //
   2192     if (Char > 127 || Char < 32) {
   2193       Status = StatusBarSetStatusString (L"Unknown Command");
   2194     } else {
   2195       Status = FileBufferAddChar (Char);
   2196     }
   2197 
   2198     break;
   2199 
   2200   }
   2201 
   2202   return Status;
   2203 }
   2204 
   2205 /**
   2206   Scroll cursor to the next line.
   2207 
   2208   @retval EFI_SUCCESS     The operation was successful.
   2209 **/
   2210 EFI_STATUS
   2211 EFIAPI
   2212 FileBufferScrollDown (
   2213   VOID
   2214   )
   2215 {
   2216   EFI_EDITOR_LINE *Line;
   2217   UINTN           FRow;
   2218   UINTN           FCol;
   2219 
   2220   Line = FileBuffer.CurrentLine;
   2221   if (Line->Buffer == NULL) {
   2222     return EFI_SUCCESS;
   2223   }
   2224 
   2225   FRow  = FileBuffer.FilePosition.Row;
   2226   FCol  = FileBuffer.FilePosition.Column;
   2227 
   2228   //
   2229   // has next line
   2230   //
   2231   if (Line->Link.ForwardLink != FileBuffer.ListHead) {
   2232     FRow++;
   2233     Line = CR (Line->Link.ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
   2234 
   2235     //
   2236     // if the next line is not that long, so move to end of next line
   2237     //
   2238     if (FCol > Line->Size) {
   2239       FCol = Line->Size + 1;
   2240     }
   2241 
   2242   } else {
   2243     return EFI_SUCCESS;
   2244   }
   2245 
   2246   FileBufferMovePosition (FRow, FCol);
   2247 
   2248   return EFI_SUCCESS;
   2249 }
   2250 
   2251 /**
   2252   Scroll the cursor to previous line.
   2253 
   2254   @retval EFI_SUCCESS     The operation was successful.
   2255 **/
   2256 EFI_STATUS
   2257 EFIAPI
   2258 FileBufferScrollUp (
   2259   VOID
   2260   )
   2261 {
   2262   EFI_EDITOR_LINE *Line;
   2263   UINTN           FRow;
   2264   UINTN           FCol;
   2265 
   2266   Line  = FileBuffer.CurrentLine;
   2267 
   2268   FRow  = FileBuffer.FilePosition.Row;
   2269   FCol  = FileBuffer.FilePosition.Column;
   2270 
   2271   //
   2272   // has previous line
   2273   //
   2274   if (Line->Link.BackLink != FileBuffer.ListHead) {
   2275     FRow--;
   2276     Line = CR (Line->Link.BackLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
   2277 
   2278     //
   2279     // if previous line is not that long, so move to the end of previous line
   2280     //
   2281     if (FCol > Line->Size) {
   2282       FCol = Line->Size + 1;
   2283     }
   2284 
   2285   } else {
   2286     return EFI_SUCCESS;
   2287   }
   2288 
   2289   FileBufferMovePosition (FRow, FCol);
   2290 
   2291   return EFI_SUCCESS;
   2292 }
   2293 
   2294 /**
   2295   Scroll cursor to next page.
   2296 
   2297   @retval EFI_SUCCESS     The operation wa successful.
   2298 **/
   2299 EFI_STATUS
   2300 EFIAPI
   2301 FileBufferPageDown (
   2302   VOID
   2303   )
   2304 {
   2305   EFI_EDITOR_LINE *Line;
   2306   UINTN           FRow;
   2307   UINTN           FCol;
   2308   UINTN           Gap;
   2309 
   2310   Line  = FileBuffer.CurrentLine;
   2311 
   2312   FRow  = FileBuffer.FilePosition.Row;
   2313   FCol  = FileBuffer.FilePosition.Column;
   2314 
   2315   //
   2316   // has next page
   2317   //
   2318   if (FileBuffer.NumLines >= FRow + (MainEditor.ScreenSize.Row - 2)) {
   2319     Gap = (MainEditor.ScreenSize.Row - 2);
   2320   } else {
   2321     //
   2322     // MOVE CURSOR TO LAST LINE
   2323     //
   2324     Gap = FileBuffer.NumLines - FRow;
   2325   }
   2326   //
   2327   // get correct line
   2328   //
   2329   Line = MoveLine (Gap);
   2330 
   2331   //
   2332   // if that line, is not that long, so move to the end of that line
   2333   //
   2334   if (Line != NULL && FCol > Line->Size) {
   2335     FCol = Line->Size + 1;
   2336   }
   2337 
   2338   FRow += Gap;
   2339 
   2340   FileBufferMovePosition (FRow, FCol);
   2341 
   2342   return EFI_SUCCESS;
   2343 }
   2344 
   2345 /**
   2346   Scroll cursor to previous screen.
   2347 
   2348   @retval EFI_SUCCESS     The operation was successful.
   2349 **/
   2350 EFI_STATUS
   2351 EFIAPI
   2352 FileBufferPageUp (
   2353   VOID
   2354   )
   2355 {
   2356   EFI_EDITOR_LINE *Line;
   2357   UINTN           FRow;
   2358   UINTN           FCol;
   2359   UINTN           Gap;
   2360   INTN            Retreat;
   2361 
   2362   Line  = FileBuffer.CurrentLine;
   2363 
   2364   FRow  = FileBuffer.FilePosition.Row;
   2365   FCol  = FileBuffer.FilePosition.Column;
   2366 
   2367   //
   2368   // has previous page
   2369   //
   2370   if (FRow > (MainEditor.ScreenSize.Row - 2)) {
   2371     Gap = (MainEditor.ScreenSize.Row - 2);
   2372   } else {
   2373     //
   2374     // the first line of file will displayed on the first line of screen
   2375     //
   2376     Gap = FRow - 1;
   2377   }
   2378 
   2379   Retreat = Gap;
   2380   Retreat = -Retreat;
   2381 
   2382   //
   2383   // get correct line
   2384   //
   2385   Line = MoveLine (Retreat);
   2386 
   2387   //
   2388   // if that line is not that long, so move to the end of that line
   2389   //
   2390   if (Line != NULL && FCol > Line->Size) {
   2391     FCol = Line->Size + 1;
   2392   }
   2393 
   2394   FRow -= Gap;
   2395 
   2396   FileBufferMovePosition (FRow, FCol);
   2397 
   2398   return EFI_SUCCESS;
   2399 }
   2400 
   2401 /**
   2402   Scroll cursor to end of the current line.
   2403 
   2404   @retval EFI_SUCCESS       The operation was successful.
   2405 **/
   2406 EFI_STATUS
   2407 EFIAPI
   2408 FileBufferEnd (
   2409   VOID
   2410   )
   2411 {
   2412   EFI_EDITOR_LINE *Line;
   2413   UINTN           FRow;
   2414   UINTN           FCol;
   2415 
   2416   Line  = FileBuffer.CurrentLine;
   2417 
   2418   FRow  = FileBuffer.FilePosition.Row;
   2419 
   2420   //
   2421   // goto the last column of the line
   2422   //
   2423   FCol = Line->Size + 1;
   2424 
   2425   FileBufferMovePosition (FRow, FCol);
   2426 
   2427   return EFI_SUCCESS;
   2428 }
   2429 
   2430 /**
   2431   Dispatch input to different handler
   2432   @param[in] Key                The input key.  One of:
   2433                                     ASCII KEY
   2434                                     Backspace/Delete
   2435                                     Return
   2436                                     Direction key: up/down/left/right/pgup/pgdn
   2437                                     Home/End
   2438                                     INS
   2439 
   2440   @retval EFI_SUCCESS           The dispatch was done successfully.
   2441   @retval EFI_LOAD_ERROR        The dispatch was not successful.
   2442   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
   2443 **/
   2444 EFI_STATUS
   2445 EFIAPI
   2446 FileBufferHandleInput (
   2447   IN CONST EFI_INPUT_KEY *Key
   2448   )
   2449 {
   2450   EFI_STATUS  Status;
   2451 
   2452   Status = EFI_SUCCESS;
   2453 
   2454   switch (Key->ScanCode) {
   2455   //
   2456   // ordinary key input
   2457   //
   2458   case SCAN_NULL:
   2459     if (!FileBuffer.ReadOnly) {
   2460       Status = FileBufferDoCharInput (Key->UnicodeChar);
   2461     } else {
   2462       Status = StatusBarSetStatusString (L"Read Only File Can Not Be Modified");
   2463     }
   2464 
   2465     break;
   2466 
   2467   //
   2468   // up arrow
   2469   //
   2470   case SCAN_UP:
   2471     Status = FileBufferScrollUp ();
   2472     break;
   2473 
   2474   //
   2475   // down arrow
   2476   //
   2477   case SCAN_DOWN:
   2478     Status = FileBufferScrollDown ();
   2479     break;
   2480 
   2481   //
   2482   // right arrow
   2483   //
   2484   case SCAN_RIGHT:
   2485     Status = FileBufferScrollRight ();
   2486     break;
   2487 
   2488   //
   2489   // left arrow
   2490   //
   2491   case SCAN_LEFT:
   2492     Status = FileBufferScrollLeft ();
   2493     break;
   2494 
   2495   //
   2496   // page up
   2497   //
   2498   case SCAN_PAGE_UP:
   2499     Status = FileBufferPageUp ();
   2500     break;
   2501 
   2502   //
   2503   // page down
   2504   //
   2505   case SCAN_PAGE_DOWN:
   2506     Status = FileBufferPageDown ();
   2507     break;
   2508 
   2509   //
   2510   // delete
   2511   //
   2512   case SCAN_DELETE:
   2513     if (!FileBuffer.ReadOnly) {
   2514       Status = FileBufferDoDelete ();
   2515     } else {
   2516       Status = StatusBarSetStatusString (L"Read Only File Can Not Be Modified");
   2517     }
   2518 
   2519     break;
   2520 
   2521   //
   2522   // home
   2523   //
   2524   case SCAN_HOME:
   2525     FileBufferMovePosition (FileBuffer.FilePosition.Row, 1);
   2526     Status = EFI_SUCCESS;
   2527     break;
   2528 
   2529   //
   2530   // end
   2531   //
   2532   case SCAN_END:
   2533     Status = FileBufferEnd ();
   2534     break;
   2535 
   2536   //
   2537   // insert
   2538   //
   2539   case SCAN_INSERT:
   2540     FileBuffer.ModeInsert = (BOOLEAN)!FileBuffer.ModeInsert;
   2541     Status = EFI_SUCCESS;
   2542     break;
   2543 
   2544   default:
   2545     Status = StatusBarSetStatusString (L"Unknown Command");
   2546     break;
   2547   }
   2548 
   2549   return Status;
   2550 }
   2551 
   2552 /**
   2553   Check user specified FileRow is above current screen.
   2554 
   2555   @param[in] FileRow    The row of file position ( start from 1 ).
   2556 
   2557   @retval TRUE    It is above the current screen.
   2558   @retval FALSE   It is not above the current screen.
   2559 **/
   2560 BOOLEAN
   2561 EFIAPI
   2562 AboveCurrentScreen (
   2563   IN UINTN FileRow
   2564   )
   2565 {
   2566   //
   2567   // if is to the above of the screen
   2568   //
   2569   if (FileRow < FileBuffer.LowVisibleRange.Row) {
   2570     return TRUE;
   2571   }
   2572 
   2573   return FALSE;
   2574 }
   2575 
   2576 /**
   2577   Check user specified FileRow is under current screen.
   2578 
   2579   @param[in] FileRow    The row of file position ( start from 1 ).
   2580 
   2581   @retval TRUE      It is under the current screen.
   2582   @retval FALSE     It is not under the current screen.
   2583 **/
   2584 BOOLEAN
   2585 EFIAPI
   2586 UnderCurrentScreen (
   2587   IN UINTN FileRow
   2588   )
   2589 {
   2590   //
   2591   // if is to the under of the screen
   2592   //
   2593   if (FileRow > FileBuffer.LowVisibleRange.Row + (MainEditor.ScreenSize.Row - 2) - 1) {
   2594     return TRUE;
   2595   }
   2596 
   2597   return FALSE;
   2598 }
   2599 
   2600 /**
   2601   Check user specified FileCol is left to current screen.
   2602 
   2603   @param[in] FileCol    The column of file position ( start from 1 ).
   2604 
   2605   @retval TRUE    It is to the left.
   2606   @retval FALSE   It is not to the left.
   2607 **/
   2608 BOOLEAN
   2609 EFIAPI
   2610 LeftCurrentScreen (
   2611   IN UINTN FileCol
   2612   )
   2613 {
   2614   //
   2615   // if is to the left of the screen
   2616   //
   2617   if (FileCol < FileBuffer.LowVisibleRange.Column) {
   2618     return TRUE;
   2619   }
   2620 
   2621   return FALSE;
   2622 }
   2623 
   2624 /**
   2625   Check user specified FileCol is right to current screen.
   2626 
   2627   @param[in] FileCol    The column of file position ( start from 1 ).
   2628 
   2629   @retval TRUE    It is to the right.
   2630   @retval FALSE   It is not to the right.
   2631 **/
   2632 BOOLEAN
   2633 EFIAPI
   2634 RightCurrentScreen (
   2635   IN UINTN FileCol
   2636   )
   2637 {
   2638   //
   2639   // if is to the right of the screen
   2640   //
   2641   if (FileCol > FileBuffer.LowVisibleRange.Column + MainEditor.ScreenSize.Column - 1) {
   2642     return TRUE;
   2643   }
   2644 
   2645   return FALSE;
   2646 }
   2647 
   2648 /**
   2649   Advance/Retreat lines and set CurrentLine in FileBuffer to it
   2650 
   2651   @param[in] Count The line number to advance/retreat
   2652                      >0 : advance
   2653                      <0: retreat
   2654 
   2655   @retval NULL An error occured.
   2656   @return The line after advance/retreat.
   2657 **/
   2658 EFI_EDITOR_LINE *
   2659 EFIAPI
   2660 MoveCurrentLine (
   2661   IN  INTN Count
   2662   )
   2663 {
   2664   EFI_EDITOR_LINE *Line;
   2665   UINTN           AbsCount;
   2666 
   2667   if (Count <= 0) {
   2668     AbsCount  = (UINTN)ABS(Count);
   2669     Line      = InternalEditorMiscLineRetreat (AbsCount,MainEditor.FileBuffer->CurrentLine,MainEditor.FileBuffer->ListHead);
   2670   } else {
   2671     Line = InternalEditorMiscLineAdvance ((UINTN)Count,MainEditor.FileBuffer->CurrentLine,MainEditor.FileBuffer->ListHead);
   2672   }
   2673 
   2674   if (Line == NULL) {
   2675     return NULL;
   2676   }
   2677 
   2678   MainEditor.FileBuffer->CurrentLine = Line;
   2679 
   2680   return Line;
   2681 }
   2682 
   2683 /**
   2684   According to cursor's file position, adjust screen display
   2685 
   2686   @param[in] NewFilePosRow    The row of file position ( start from 1 ).
   2687   @param[in] NewFilePosCol    The column of file position ( start from 1 ).
   2688 **/
   2689 VOID
   2690 EFIAPI
   2691 FileBufferMovePosition (
   2692   IN CONST UINTN NewFilePosRow,
   2693   IN CONST UINTN NewFilePosCol
   2694   )
   2695 {
   2696   INTN    RowGap;
   2697   INTN    ColGap;
   2698   UINTN   Abs;
   2699   BOOLEAN Above;
   2700   BOOLEAN Under;
   2701   BOOLEAN Right;
   2702   BOOLEAN Left;
   2703 
   2704   //
   2705   // CALCULATE gap between current file position and new file position
   2706   //
   2707   RowGap  = NewFilePosRow - FileBuffer.FilePosition.Row;
   2708   ColGap  = NewFilePosCol - FileBuffer.FilePosition.Column;
   2709 
   2710   Under   = UnderCurrentScreen (NewFilePosRow);
   2711   Above   = AboveCurrentScreen (NewFilePosRow);
   2712   //
   2713   // if is below current screen
   2714   //
   2715   if (Under) {
   2716     //
   2717     // display row will be unchanged
   2718     //
   2719     FileBuffer.FilePosition.Row = NewFilePosRow;
   2720   } else {
   2721     if (Above) {
   2722       //
   2723       // has enough above line, so display row unchanged
   2724       // not has enough above lines, so the first line is at the
   2725       // first display line
   2726       //
   2727       if (NewFilePosRow < (FileBuffer.DisplayPosition.Row - 1)) {
   2728         FileBuffer.DisplayPosition.Row = NewFilePosRow + 1;
   2729       }
   2730 
   2731       FileBuffer.FilePosition.Row = NewFilePosRow;
   2732     } else {
   2733       //
   2734       // in current screen
   2735       //
   2736       FileBuffer.FilePosition.Row = NewFilePosRow;
   2737       if (RowGap < 0) {
   2738         Abs = (UINTN)ABS(RowGap);
   2739         FileBuffer.DisplayPosition.Row -= Abs;
   2740       } else {
   2741         FileBuffer.DisplayPosition.Row += RowGap;
   2742       }
   2743     }
   2744   }
   2745 
   2746   FileBuffer.LowVisibleRange.Row  = FileBuffer.FilePosition.Row - (FileBuffer.DisplayPosition.Row - 2);
   2747 
   2748   Right = RightCurrentScreen (NewFilePosCol);
   2749   Left = LeftCurrentScreen (NewFilePosCol);
   2750 
   2751   //
   2752   // if right to current screen
   2753   //
   2754   if (Right) {
   2755     //
   2756     // display column will be changed to end
   2757     //
   2758     FileBuffer.DisplayPosition.Column = MainEditor.ScreenSize.Column;
   2759     FileBuffer.FilePosition.Column    = NewFilePosCol;
   2760   } else {
   2761     if (Left) {
   2762       //
   2763       // has enough left characters , so display row unchanged
   2764       // not has enough left characters,
   2765       // so the first character is at the first display column
   2766       //
   2767       if (NewFilePosCol < (FileBuffer.DisplayPosition.Column)) {
   2768         FileBuffer.DisplayPosition.Column = NewFilePosCol;
   2769       }
   2770 
   2771       FileBuffer.FilePosition.Column = NewFilePosCol;
   2772     } else {
   2773       //
   2774       // in current screen
   2775       //
   2776       FileBuffer.FilePosition.Column = NewFilePosCol;
   2777       if (ColGap < 0) {
   2778         Abs = (UINTN)(-ColGap);
   2779         FileBuffer.DisplayPosition.Column -= Abs;
   2780       } else {
   2781         FileBuffer.DisplayPosition.Column += ColGap;
   2782       }
   2783     }
   2784   }
   2785 
   2786   FileBuffer.LowVisibleRange.Column = FileBuffer.FilePosition.Column - (FileBuffer.DisplayPosition.Column - 1);
   2787 
   2788   //
   2789   // let CurrentLine point to correct line;
   2790   //
   2791   FileBuffer.CurrentLine = MoveCurrentLine (RowGap);
   2792 
   2793 }
   2794 
   2795 /**
   2796   Cut current line out and return a pointer to it.
   2797 
   2798   @param[out] CutLine    Upon a successful return pointer to the pointer to
   2799                         the allocated cut line.
   2800 
   2801   @retval EFI_SUCCESS             The cut was successful.
   2802   @retval EFI_NOT_FOUND           There was no selection to cut.
   2803   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
   2804 **/
   2805 EFI_STATUS
   2806 EFIAPI
   2807 FileBufferCutLine (
   2808   OUT EFI_EDITOR_LINE **CutLine
   2809   )
   2810 {
   2811   EFI_EDITOR_LINE *Line;
   2812   EFI_EDITOR_LINE *NewLine;
   2813   UINTN           Row;
   2814   UINTN           Col;
   2815 
   2816   if (FileBuffer.ReadOnly) {
   2817     StatusBarSetStatusString (L"Read Only File Can Not Be Modified");
   2818     return EFI_SUCCESS;
   2819   }
   2820 
   2821   Line = FileBuffer.CurrentLine;
   2822 
   2823   //
   2824   // if is the last dummy line, SO CAN not cut
   2825   //
   2826   if (StrCmp (Line->Buffer, L"\0") == 0 && Line->Link.ForwardLink == FileBuffer.ListHead
   2827   //
   2828   // last line
   2829   //
   2830   ) {
   2831     //
   2832     // LAST LINE AND NOTHING ON THIS LINE, SO CUT NOTHING
   2833     //
   2834     StatusBarSetStatusString (L"Nothing to Cut");
   2835     return EFI_NOT_FOUND;
   2836   }
   2837   //
   2838   // if is the last line, so create a dummy line
   2839   //
   2840   if (Line->Link.ForwardLink == FileBuffer.ListHead) {
   2841     //
   2842     // last line
   2843     // create a new line
   2844     //
   2845     NewLine = FileBufferCreateLine ();
   2846     if (NewLine == NULL) {
   2847       return EFI_OUT_OF_RESOURCES;
   2848     }
   2849   }
   2850 
   2851   FileBuffer.NumLines--;
   2852   Row = FileBuffer.FilePosition.Row;
   2853   Col = 1;
   2854   //
   2855   // move home
   2856   //
   2857   FileBuffer.CurrentLine = CR (
   2858                             FileBuffer.CurrentLine->Link.ForwardLink,
   2859                             EFI_EDITOR_LINE,
   2860                             Link,
   2861                             LINE_LIST_SIGNATURE
   2862                             );
   2863 
   2864   RemoveEntryList (&Line->Link);
   2865 
   2866   FileBuffer.Lines = CR (FileBuffer.ListHead->ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
   2867 
   2868   FileBufferMovePosition (Row, Col);
   2869 
   2870   FileBuffer.FileModified       = TRUE;
   2871   FileBufferNeedRefresh         = TRUE;
   2872   FileBufferOnlyLineNeedRefresh = FALSE;
   2873 
   2874   *CutLine                      = Line;
   2875 
   2876   return EFI_SUCCESS;
   2877 }
   2878 
   2879 /**
   2880   Paste a line into line list.
   2881 
   2882   @retval EFI_SUCCESS             The paste was successful.
   2883   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
   2884 **/
   2885 EFI_STATUS
   2886 EFIAPI
   2887 FileBufferPasteLine (
   2888   VOID
   2889   )
   2890 {
   2891   EFI_EDITOR_LINE *Line;
   2892   EFI_EDITOR_LINE *NewLine;
   2893   UINTN           Row;
   2894   UINTN           Col;
   2895 
   2896   //
   2897   // if nothing is on clip board
   2898   // then do nothing
   2899   //
   2900   if (MainEditor.CutLine == NULL) {
   2901     return EFI_SUCCESS;
   2902   }
   2903   //
   2904   // read only file can not be pasted on
   2905   //
   2906   if (FileBuffer.ReadOnly) {
   2907     StatusBarSetStatusString (L"Read Only File Can Not Be Modified");
   2908     return EFI_SUCCESS;
   2909   }
   2910 
   2911   NewLine = LineDup (MainEditor.CutLine);
   2912   if (NewLine == NULL) {
   2913     return EFI_OUT_OF_RESOURCES;
   2914   }
   2915   //
   2916   // insert it above current line
   2917   //
   2918   Line                    = FileBuffer.CurrentLine;
   2919   NewLine->Link.BackLink     = Line->Link.BackLink;
   2920   NewLine->Link.ForwardLink     = &Line->Link;
   2921 
   2922   Line->Link.BackLink->ForwardLink = &NewLine->Link;
   2923   Line->Link.BackLink        = &NewLine->Link;
   2924 
   2925   FileBuffer.NumLines++;
   2926   FileBuffer.CurrentLine  = NewLine;
   2927 
   2928   FileBuffer.Lines        = CR (FileBuffer.ListHead->ForwardLink, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
   2929 
   2930   Col                     = 1;
   2931   //
   2932   // move home
   2933   //
   2934   Row = FileBuffer.FilePosition.Row;
   2935 
   2936   FileBufferMovePosition (Row, Col);
   2937 
   2938   //
   2939   // after paste, set some value so that refresh knows to do something
   2940   //
   2941   FileBuffer.FileModified       = TRUE;
   2942   FileBufferNeedRefresh         = TRUE;
   2943   FileBufferOnlyLineNeedRefresh = FALSE;
   2944 
   2945   return EFI_SUCCESS;
   2946 }
   2947 
   2948 /**
   2949   Search string from current position on in file
   2950 
   2951   @param[in] Str    The search string.
   2952   @param[in] Offset The offset from current position.
   2953 
   2954   @retval EFI_SUCCESS       The operation was successful.
   2955   @retval EFI_NOT_FOUND     The string Str was not found.
   2956 **/
   2957 EFI_STATUS
   2958 EFIAPI
   2959 FileBufferSearch (
   2960   IN CONST CHAR16  *Str,
   2961   IN CONST UINTN Offset
   2962   )
   2963 {
   2964   CHAR16          *Current;
   2965   UINTN           Position;
   2966   UINTN           Row;
   2967   UINTN           Column;
   2968   EFI_EDITOR_LINE *Line;
   2969   CHAR16          *CharPos;
   2970   LIST_ENTRY      *Link;
   2971   BOOLEAN         Found;
   2972 
   2973   Column = 0;
   2974   Position = 0;
   2975 
   2976   //
   2977   // search if in current line
   2978   //
   2979   Current = FileBuffer.CurrentLine->Buffer + FileBuffer.FilePosition.Column - 1 + Offset;
   2980 
   2981   if (Current >= (FileBuffer.CurrentLine->Buffer + FileBuffer.CurrentLine->Size)) {
   2982     //
   2983     // the end
   2984     //
   2985     Current = FileBuffer.CurrentLine->Buffer + FileBuffer.CurrentLine->Size;
   2986   }
   2987 
   2988   Found = FALSE;
   2989 
   2990   CharPos  =  StrStr (Current, Str);
   2991   if (CharPos != NULL) {
   2992     Position = CharPos - Current + 1;
   2993     Found   = TRUE;
   2994   }
   2995 
   2996   //
   2997   // found
   2998   //
   2999   if (Found) {
   3000     Column  = (Position - 1) + FileBuffer.FilePosition.Column + Offset;
   3001     Row     = FileBuffer.FilePosition.Row;
   3002   } else {
   3003     //
   3004     // not found so find through next lines
   3005     //
   3006     Link  = FileBuffer.CurrentLine->Link.ForwardLink;
   3007 
   3008     Row   = FileBuffer.FilePosition.Row + 1;
   3009     while (Link != FileBuffer.ListHead) {
   3010       Line      = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
   3011 //      Position  = StrStr (Line->Buffer, Str);
   3012       CharPos  =  StrStr (Line->Buffer, Str);
   3013       if (CharPos != NULL) {
   3014         Position = CharPos - Line->Buffer + 1;
   3015         Found   = TRUE;
   3016       }
   3017 
   3018       if (Found) {
   3019         //
   3020         // found
   3021         //
   3022         Column = Position;
   3023         break;
   3024       }
   3025 
   3026       Row++;
   3027       Link = Link->ForwardLink;
   3028     }
   3029 
   3030     if (Link == FileBuffer.ListHead) {
   3031       Found = FALSE;
   3032     } else {
   3033       Found = TRUE;
   3034     }
   3035   }
   3036 
   3037   if (!Found) {
   3038     return EFI_NOT_FOUND;
   3039   }
   3040 
   3041   FileBufferMovePosition (Row, Column);
   3042 
   3043   //
   3044   // call refresh to fresh edit area,
   3045   // because the outer may loop to find multiply occurrence of this string
   3046   //
   3047   FileBufferRefresh ();
   3048 
   3049   return EFI_SUCCESS;
   3050 }
   3051 
   3052 /**
   3053   Replace SearchLen characters from current position on with Replace.
   3054 
   3055   This will modify the current buffer at the current position.
   3056 
   3057   @param[in] Replace    The string to replace.
   3058   @param[in] SearchLen  Search string's length.
   3059 
   3060   @retval EFI_SUCCESS             The operation was successful.
   3061   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
   3062 **/
   3063 EFI_STATUS
   3064 EFIAPI
   3065 FileBufferReplace (
   3066   IN CONST CHAR16   *Replace,
   3067   IN CONST UINTN    SearchLen
   3068   )
   3069 {
   3070   UINTN   ReplaceLen;
   3071   UINTN   Index;
   3072   CHAR16  *Buffer;
   3073   UINTN   NewSize;
   3074   UINTN   OldSize;
   3075   UINTN   Gap;
   3076 
   3077   ReplaceLen  = StrLen (Replace);
   3078 
   3079   OldSize     = FileBuffer.CurrentLine->Size + 1;
   3080   //
   3081   // include CHAR_NULL
   3082   //
   3083   NewSize = OldSize + (ReplaceLen - SearchLen);
   3084 
   3085   if (ReplaceLen > SearchLen) {
   3086     //
   3087     // do not have the enough space
   3088     //
   3089     if (FileBuffer.CurrentLine->TotalSize + 1 <= NewSize) {
   3090       FileBuffer.CurrentLine->Buffer = ReallocatePool (
   3091                                         2 * OldSize,
   3092                                         2 * NewSize,
   3093                                         FileBuffer.CurrentLine->Buffer
   3094                                         );
   3095       FileBuffer.CurrentLine->TotalSize = NewSize - 1;
   3096     }
   3097 
   3098     if (FileBuffer.CurrentLine->Buffer == NULL) {
   3099       return EFI_OUT_OF_RESOURCES;
   3100     }
   3101     //
   3102     // the end CHAR_NULL character;
   3103     //
   3104     Buffer  = FileBuffer.CurrentLine->Buffer + (NewSize - 1);
   3105     Gap     = ReplaceLen - SearchLen;
   3106 
   3107     //
   3108     // keep the latter part
   3109     //
   3110     for (Index = 0; Index < (FileBuffer.CurrentLine->Size - FileBuffer.FilePosition.Column - SearchLen + 2); Index++) {
   3111       *Buffer = *(Buffer - Gap);
   3112       Buffer--;
   3113     }
   3114     //
   3115     // set replace into it
   3116     //
   3117     Buffer = FileBuffer.CurrentLine->Buffer + FileBuffer.FilePosition.Column - 1;
   3118     for (Index = 0; Index < ReplaceLen; Index++) {
   3119       Buffer[Index] = Replace[Index];
   3120     }
   3121   }
   3122 
   3123   if (ReplaceLen < SearchLen) {
   3124     Buffer = FileBuffer.CurrentLine->Buffer + FileBuffer.FilePosition.Column - 1;
   3125 
   3126     for (Index = 0; Index < ReplaceLen; Index++) {
   3127       Buffer[Index] = Replace[Index];
   3128     }
   3129 
   3130     Buffer += ReplaceLen;
   3131     Gap = SearchLen - ReplaceLen;
   3132 
   3133     //
   3134     // set replace into it
   3135     //
   3136     for (Index = 0; Index < (FileBuffer.CurrentLine->Size - FileBuffer.FilePosition.Column - ReplaceLen + 2); Index++) {
   3137       *Buffer = *(Buffer + Gap);
   3138       Buffer++;
   3139     }
   3140   }
   3141 
   3142   if (ReplaceLen == SearchLen) {
   3143     Buffer = FileBuffer.CurrentLine->Buffer + FileBuffer.FilePosition.Column - 1;
   3144     for (Index = 0; Index < ReplaceLen; Index++) {
   3145       Buffer[Index] = Replace[Index];
   3146     }
   3147   }
   3148 
   3149   FileBuffer.CurrentLine->Size += (ReplaceLen - SearchLen);
   3150 
   3151   FileBufferOnlyLineNeedRefresh = TRUE;
   3152 
   3153   FileBuffer.FileModified       = TRUE;
   3154 
   3155   MainTitleBarRefresh (MainEditor.FileBuffer->FileName, MainEditor.FileBuffer->FileType, MainEditor.FileBuffer->ReadOnly, MainEditor.FileBuffer->FileModified, MainEditor.ScreenSize.Column, MainEditor.ScreenSize.Row, 0, 0);
   3156   FileBufferRestorePosition ();
   3157   FileBufferRefresh ();
   3158 
   3159   return EFI_SUCCESS;
   3160 }
   3161 
   3162 /**
   3163   Move the mouse cursor position.
   3164 
   3165   @param[in] TextX      The new x-coordinate.
   3166   @param[in] TextY      The new y-coordinate.
   3167 **/
   3168 VOID
   3169 EFIAPI
   3170 FileBufferAdjustMousePosition (
   3171   IN CONST INT32 TextX,
   3172   IN CONST INT32 TextY
   3173   )
   3174 {
   3175   UINTN CoordinateX;
   3176   UINTN CoordinateY;
   3177   UINTN AbsX;
   3178   UINTN AbsY;
   3179 
   3180   //
   3181   // TextX and TextY is mouse movement data returned by mouse driver
   3182   // This function will change it to MousePosition
   3183   //
   3184   //
   3185   // get absolute value
   3186   //
   3187 
   3188   AbsX = ABS(TextX);
   3189   AbsY = ABS(TextY);
   3190 
   3191   CoordinateX = FileBuffer.MousePosition.Column;
   3192   CoordinateY = FileBuffer.MousePosition.Row;
   3193 
   3194   if (TextX >= 0) {
   3195     CoordinateX += TextX;
   3196   } else {
   3197     if (CoordinateX >= AbsX) {
   3198       CoordinateX -= AbsX;
   3199     } else {
   3200       CoordinateX = 0;
   3201     }
   3202   }
   3203 
   3204   if (TextY >= 0) {
   3205     CoordinateY += TextY;
   3206   } else {
   3207     if (CoordinateY >= AbsY) {
   3208       CoordinateY -= AbsY;
   3209     } else {
   3210       CoordinateY = 0;
   3211     }
   3212   }
   3213   //
   3214   // check whether new mouse column position is beyond screen
   3215   // if not, adjust it
   3216   //
   3217   if (CoordinateX >= 1 && CoordinateX <= MainEditor.ScreenSize.Column) {
   3218     FileBuffer.MousePosition.Column = CoordinateX;
   3219   } else if (CoordinateX < 1) {
   3220     FileBuffer.MousePosition.Column = 1;
   3221   } else if (CoordinateX > MainEditor.ScreenSize.Column) {
   3222     FileBuffer.MousePosition.Column = MainEditor.ScreenSize.Column;
   3223   }
   3224   //
   3225   // check whether new mouse row position is beyond screen
   3226   // if not, adjust it
   3227   //
   3228   if (CoordinateY >= 2 && CoordinateY <= (MainEditor.ScreenSize.Row - 1)) {
   3229     FileBuffer.MousePosition.Row = CoordinateY;
   3230   } else if (CoordinateY < 2) {
   3231     FileBuffer.MousePosition.Row = 2;
   3232   } else if (CoordinateY > (MainEditor.ScreenSize.Row - 1)) {
   3233     FileBuffer.MousePosition.Row = (MainEditor.ScreenSize.Row - 1);
   3234   }
   3235 
   3236 }
   3237 
   3238 /**
   3239   Search and replace operation.
   3240 
   3241   @param[in] SearchStr    The string to search for.
   3242   @param[in] ReplaceStr   The string to replace with.
   3243   @param[in] Offset       The column to start at.
   3244 **/
   3245 EFI_STATUS
   3246 EFIAPI
   3247 FileBufferReplaceAll (
   3248   IN CHAR16 *SearchStr,
   3249   IN CHAR16 *ReplaceStr,
   3250   IN UINTN  Offset
   3251   )
   3252 {
   3253   CHAR16          *Buffer;
   3254   UINTN           Position;
   3255   UINTN           Column;
   3256   UINTN           ReplaceLen;
   3257   UINTN           SearchLen;
   3258   UINTN           Index;
   3259   UINTN           NewSize;
   3260   UINTN           OldSize;
   3261   UINTN           Gap;
   3262   EFI_EDITOR_LINE *Line;
   3263   LIST_ENTRY      *Link;
   3264   CHAR16          *CharPos;
   3265 
   3266   SearchLen   = StrLen (SearchStr);
   3267   ReplaceLen  = StrLen (ReplaceStr);
   3268 
   3269   Column      = FileBuffer.FilePosition.Column + Offset - 1;
   3270 
   3271   if (Column > FileBuffer.CurrentLine->Size) {
   3272     Column = FileBuffer.CurrentLine->Size;
   3273   }
   3274 
   3275   Link = &(FileBuffer.CurrentLine->Link);
   3276 
   3277   while (Link != FileBuffer.ListHead) {
   3278     Line      = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
   3279     CharPos  =  StrStr (Line->Buffer + Column, SearchStr);
   3280     if (CharPos != NULL) {
   3281       Position = CharPos - Line->Buffer;// + Column;
   3282       //
   3283       // found
   3284       //
   3285       if (ReplaceLen > SearchLen) {
   3286         OldSize = Line->Size + 1;
   3287         //
   3288         // include CHAR_NULL
   3289         //
   3290         NewSize = OldSize + (ReplaceLen - SearchLen);
   3291 
   3292         //
   3293         // do not have the enough space
   3294         //
   3295         if (Line->TotalSize + 1 <= NewSize) {
   3296           Line->Buffer = ReallocatePool (
   3297                           2 * OldSize,
   3298                           2 * NewSize,
   3299                           Line->Buffer
   3300                           );
   3301           Line->TotalSize = NewSize - 1;
   3302         }
   3303 
   3304         if (Line->Buffer == NULL) {
   3305           return EFI_OUT_OF_RESOURCES;
   3306         }
   3307         //
   3308         // the end CHAR_NULL character;
   3309         //
   3310         Buffer  = Line->Buffer + (NewSize - 1);
   3311         Gap     = ReplaceLen - SearchLen;
   3312 
   3313         //
   3314         // keep the latter part
   3315         //
   3316         for (Index = 0; Index < (Line->Size - Position - SearchLen + 1); Index++) {
   3317           *Buffer = *(Buffer - Gap);
   3318           Buffer--;
   3319         }
   3320 
   3321       } else if (ReplaceLen < SearchLen){
   3322         Buffer  = Line->Buffer + Position + ReplaceLen;
   3323         Gap     = SearchLen - ReplaceLen;
   3324 
   3325         for (Index = 0; Index < (Line->Size - Position - ReplaceLen + 1); Index++) {
   3326           *Buffer = *(Buffer + Gap);
   3327           Buffer++;
   3328         }
   3329       } else {
   3330         ASSERT(ReplaceLen == SearchLen);
   3331       }
   3332       //
   3333       // set replace into it
   3334       //
   3335       Buffer = Line->Buffer + Position;
   3336       for (Index = 0; Index < ReplaceLen; Index++) {
   3337         Buffer[Index] = ReplaceStr[Index];
   3338       }
   3339 
   3340       Line->Size += (ReplaceLen - SearchLen);
   3341       Column += ReplaceLen;
   3342     } else {
   3343       //
   3344       // not found
   3345       //
   3346       Column  = 0;
   3347       Link    = Link->ForwardLink;
   3348     }
   3349   }
   3350   //
   3351   // call refresh to fresh edit area
   3352   //
   3353   FileBuffer.FileModified = TRUE;
   3354   FileBufferNeedRefresh   = TRUE;
   3355   FileBufferRefresh ();
   3356 
   3357   return EFI_SUCCESS;
   3358 }
   3359 
   3360 /**
   3361   Set the modified state to TRUE.
   3362 **/
   3363 VOID
   3364 EFIAPI
   3365 FileBufferSetModified (
   3366   VOID
   3367   )
   3368 {
   3369   FileBuffer.FileModified = TRUE;
   3370 }
   3371 
   3372