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