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