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