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