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