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