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