1 /** @file
2   Implements editor 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 "EditStatusBar.h"
17 #include "EditInputBar.h"
18 #include "EditMenuBar.h"
19 
20 //
21 // the first time editor launch
22 //
23 BOOLEAN                       EditorFirst;
24 
25 //
26 // it's time editor should exit
27 //
28 BOOLEAN                       EditorExit;
29 
30 BOOLEAN                       EditorMouseAction;
31 
32 extern EFI_EDITOR_FILE_BUFFER FileBuffer;
33 
34 extern BOOLEAN                FileBufferNeedRefresh;
35 
36 extern BOOLEAN                FileBufferOnlyLineNeedRefresh;
37 
38 extern BOOLEAN                FileBufferMouseNeedRefresh;
39 
40 extern EFI_EDITOR_FILE_BUFFER FileBufferBackupVar;
41 
42 EFI_EDITOR_GLOBAL_EDITOR      MainEditor;
43 
44 
45 /**
46   Load a file from disk to editor
47 
48   @retval EFI_SUCCESS             The operation was successful.
49   @retval EFI_LOAD_ERROR          A load error occured.
50   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
51 **/
52 EFI_STATUS
53 MainCommandOpenFile (
54   VOID
55   );
56 
57 /**
58   Switch a file from ASCII to UNICODE or vise-versa.
59 
60   @retval EFI_SUCCESS           The switch was ok or a warning was presented.
61 **/
62 EFI_STATUS
63 MainCommandSwitchFileType (
64   VOID
65   );
66 
67 /**
68   move cursor to specified lines
69 
70   @retval EFI_SUCCESS             The operation was successful.
71 **/
72 EFI_STATUS
73 MainCommandGotoLine (
74   VOID
75   );
76 
77 /**
78   Save current file to disk, you can save to current file name or
79   save to another file name.
80 
81   @retval EFI_SUCCESS           The file was saved correctly.
82   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
83   @retval EFI_LOAD_ERROR          A file access error occured.
84 **/
85 EFI_STATUS
86 MainCommandSaveFile (
87   VOID
88   );
89 
90 /**
91   Show help information for the editor.
92 
93   @retval EFI_SUCCESS             The operation was successful.
94 **/
95 EFI_STATUS
96 MainCommandDisplayHelp (
97   VOID
98   );
99 
100 /**
101   exit editor
102 
103   @retval EFI_SUCCESS             The operation was successful.
104   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
105   @retval EFI_LOAD_ERROR          A load error occured.
106 **/
107 EFI_STATUS
108 MainCommandExit (
109   VOID
110   );
111 
112 /**
113   search string in file buffer
114 
115   @retval EFI_SUCCESS             The operation was successful.
116   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
117   @retval EFI_LOAD_ERROR          A load error occured.
118 **/
119 EFI_STATUS
120 MainCommandSearch (
121   VOID
122   );
123 
124 /**
125   search string in file buffer, and replace it with another str
126 
127   @retval EFI_SUCCESS             The operation was successful.
128   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
129   @retval EFI_LOAD_ERROR          A load error occured.
130 **/
131 EFI_STATUS
132 MainCommandSearchReplace (
133   VOID
134   );
135 
136 /**
137   cut current line to clipboard
138 
139   @retval EFI_SUCCESS             The operation was successful.
140   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
141   @retval EFI_LOAD_ERROR          A load error occured.
142 **/
143 EFI_STATUS
144 MainCommandCutLine (
145   VOID
146   );
147 
148 /**
149   paste line to file buffer.
150 
151   @retval EFI_SUCCESS             The operation was successful.
152   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
153   @retval EFI_LOAD_ERROR          A load error occured.
154 **/
155 EFI_STATUS
156 MainCommandPasteLine (
157   VOID
158   );
159 
160 /**
161   Help info that will be displayed.
162 **/
163 EFI_STRING_ID  MainMenuHelpInfo[] = {
164   STRING_TOKEN(STR_EDIT_HELP_TITLE),
165   STRING_TOKEN(STR_EDIT_HELP_BLANK),
166   STRING_TOKEN(STR_EDIT_HELP_LIST_TITLE),
167   STRING_TOKEN(STR_EDIT_HELP_DIV),
168   STRING_TOKEN(STR_EDIT_HELP_GO_TO_LINE),
169   STRING_TOKEN(STR_EDIT_HELP_SAVE_FILE),
170   STRING_TOKEN(STR_EDIT_HELP_EXIT),
171   STRING_TOKEN(STR_EDIT_HELP_SEARCH),
172   STRING_TOKEN(STR_EDIT_HELP_SEARCH_REPLACE),
173   STRING_TOKEN(STR_EDIT_HELP_CUT_LINE),
174   STRING_TOKEN(STR_EDIT_HELP_PASTE_LINE),
175   STRING_TOKEN(STR_EDIT_HELP_OPEN_FILE),
176   STRING_TOKEN(STR_EDIT_HELP_FILE_TYPE),
177   STRING_TOKEN(STR_EDIT_HELP_BLANK),
178   STRING_TOKEN(STR_EDIT_HELP_EXIT_HELP),
179   STRING_TOKEN(STR_EDIT_HELP_BLANK),
180   STRING_TOKEN(STR_EDIT_HELP_BLANK),
181   STRING_TOKEN(STR_EDIT_HELP_BLANK),
182   STRING_TOKEN(STR_EDIT_HELP_BLANK),
183   STRING_TOKEN(STR_EDIT_HELP_BLANK),
184   STRING_TOKEN(STR_EDIT_HELP_BLANK),
185   STRING_TOKEN(STR_EDIT_HELP_BLANK),
186   STRING_TOKEN(STR_EDIT_HELP_DIV),
187 0
188 };
189 
190 MENU_ITEM_FUNCTION MainControlBasedMenuFunctions[] = {
191   NULL,
192   NULL,                      /* Ctrl - A */
193   NULL,                      /* Ctrl - B */
194   NULL,                      /* Ctrl - C */
195   NULL,                      /* Ctrl - D */
196   MainCommandDisplayHelp,    /* Ctrl - E */
197   MainCommandSearch,         /* Ctrl - F */
198   MainCommandGotoLine,       /* Ctrl - G */
199   NULL,                      /* Ctrl - H */
200   NULL,                      /* Ctrl - I */
201   NULL,                      /* Ctrl - J */
202   MainCommandCutLine,        /* Ctrl - K */
203   NULL,                      /* Ctrl - L */
204   NULL,                      /* Ctrl - M */
205   NULL,                      /* Ctrl - N */
206   MainCommandOpenFile,       /* Ctrl - O */
207   NULL,                      /* Ctrl - P */
208   MainCommandExit,           /* Ctrl - Q */
209   MainCommandSearchReplace,  /* Ctrl - R */
210   MainCommandSaveFile,       /* Ctrl - S */
211   MainCommandSwitchFileType, /* Ctrl - T */
212   MainCommandPasteLine,      /* Ctrl - U */
213   NULL,                      /* Ctrl - V */
214   NULL,                      /* Ctrl - W */
215   NULL,                      /* Ctrl - X */
216   NULL,                      /* Ctrl - Y */
217   NULL,                      /* Ctrl - Z */
218 };
219 
220 EDITOR_MENU_ITEM  MainMenuItems[] = {
221   {
222     STRING_TOKEN(STR_EDIT_LIBMENUBAR_GO_TO_LINE),
223     STRING_TOKEN(STR_EDIT_LIBMENUBAR_F1),
224     MainCommandGotoLine
225   },
226   {
227     STRING_TOKEN(STR_EDIT_LIBMENUBAR_SAVE_FILE),
228     STRING_TOKEN(STR_EDIT_LIBMENUBAR_F2),
229     MainCommandSaveFile
230   },
231   {
232     STRING_TOKEN(STR_EDIT_LIBMENUBAR_EXIT),
233     STRING_TOKEN(STR_EDIT_LIBMENUBAR_F3),
234     MainCommandExit
235   },
236 
237   {
238     STRING_TOKEN(STR_EDIT_LIBMENUBAR_SEARCH),
239     STRING_TOKEN(STR_EDIT_LIBMENUBAR_F4),
240     MainCommandSearch
241   },
242   {
243     STRING_TOKEN(STR_EDIT_LIBMENUBAR_SEARCH_REPLACE),
244     STRING_TOKEN(STR_EDIT_LIBMENUBAR_F5),
245     MainCommandSearchReplace
246   },
247   {
248     STRING_TOKEN(STR_EDIT_LIBMENUBAR_CUT_LINE),
249     STRING_TOKEN(STR_EDIT_LIBMENUBAR_F6),
250     MainCommandCutLine
251   },
252   {
253     STRING_TOKEN(STR_EDIT_LIBMENUBAR_PASTE_LINE),
254     STRING_TOKEN(STR_EDIT_LIBMENUBAR_F7),
255     MainCommandPasteLine
256   },
257 
258   {
259     STRING_TOKEN(STR_EDIT_LIBMENUBAR_OPEN_FILE),
260     STRING_TOKEN(STR_EDIT_LIBMENUBAR_F8),
261     MainCommandOpenFile
262   },
263   {
264     STRING_TOKEN(STR_EDIT_LIBMENUBAR_FILE_TYPE),
265     STRING_TOKEN(STR_EDIT_LIBMENUBAR_F9),
266     MainCommandSwitchFileType
267   },
268   {
269     STRING_TOKEN(STR_EDIT_LIBMENUBAR_FILE_TYPE),
270     STRING_TOKEN(STR_EDIT_LIBMENUBAR_F11),
271     MainCommandSwitchFileType
272   },
273 
274   {
275     0,
276     0,
277     NULL
278   }
279 };
280 
281 
282 /**
283   Load a file from disk to editor
284 
285   @retval EFI_SUCCESS             The operation was successful.
286   @retval EFI_LOAD_ERROR          A load error occured.
287   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
288 **/
289 EFI_STATUS
MainCommandOpenFile(VOID)290 MainCommandOpenFile (
291   VOID
292   )
293 {
294   BOOLEAN     Done;
295   EFI_STATUS  Status;
296 
297   //
298   //  This command will open a file from current working directory.
299   //   Read-only file can also be opened. But it can not be modified.
300   // Below is the scenario of Open File command:
301   // 1.IF currently opened file has not been modIFied, directly go to step .
302   //   IF currently opened file has been modified,
303   //     an Input Bar will be prompted as :
304   //       "File Modified. Save ( Yes/No/Cancel) ?"
305   //           IF user press 'y' or 'Y', currently opened file will be saved.
306   //           IF user press 'n' or 'N', currently opened file will
307   //              not be saved.
308   //           IF user press 'c' or 'C' or ESC, Open File command ends and
309   //              currently opened file is still opened.
310   //
311   // 2.  An Input Bar will be prompted as :  "File Name to Open: "
312   //       IF user press ESC, Open File command ends and
313   //          currently opened file is still opened.
314   //       Any other inputs with a Return will
315   //          cause currently opened file close.
316   //
317   // 3.  IF user input file name is an existing file , this file will be read
318   //        and opened.
319   //    IF user input file name is a new file, this file will be created
320   //        and opened. This file's type ( UNICODE or ASCII ) is the same
321   //        with the old file.
322   // if current file is modified, so you need to choose
323   // whether to save it first.
324   //
325   if (MainEditor.FileBuffer->FileModified) {
326 
327     Status = InputBarSetPrompt (L"File modified. Save (Yes/No/Cancel) ? ");
328     if (EFI_ERROR (Status)) {
329       return Status;
330     }
331     //
332     // the answer is just one character
333     //
334     Status = InputBarSetStringSize (1);
335     if (EFI_ERROR (Status)) {
336       return Status;
337     }
338     //
339     // loop for user's answer
340     // valid answer is just 'y' 'Y', 'n' 'N', 'c' 'C'
341     //
342     Done = FALSE;
343     while (!Done) {
344       Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column);
345       StatusBarSetRefresh();
346 
347       //
348       // ESC pressed
349       //
350       if (Status == EFI_NOT_READY) {
351         return EFI_SUCCESS;
352       }
353 
354       switch (InputBarGetString()[0]) {
355       case L'y':
356       case L'Y':
357         //
358         // want to save this file first
359         //
360         Status = FileBufferSave (MainEditor.FileBuffer->FileName);
361         if (EFI_ERROR (Status)) {
362           return Status;
363         }
364 
365         MainTitleBarRefresh (MainEditor.FileBuffer->FileName, MainEditor.FileBuffer->FileType, MainEditor.FileBuffer->ReadOnly, MainEditor.FileBuffer->FileModified, MainEditor.ScreenSize.Column, MainEditor.ScreenSize.Row, 0, 0);
366         FileBufferRestorePosition ();
367         Done = TRUE;
368         break;
369 
370       case L'n':
371       case L'N':
372         //
373         // the file won't be saved
374         //
375         Done = TRUE;
376         break;
377 
378       case L'c':
379       case L'C':
380         return EFI_SUCCESS;
381       }
382     }
383   }
384   //
385   // TO get the open file name
386   //
387   Status = InputBarSetPrompt (L"File Name to Open: ");
388   if (EFI_ERROR (Status)) {
389     FileBufferRead (MainEditor.FileBuffer->FileName, TRUE);
390     return Status;
391   }
392 
393   Status = InputBarSetStringSize (100);
394   if (EFI_ERROR (Status)) {
395     FileBufferRead (MainEditor.FileBuffer->FileName, TRUE);
396     return Status;
397   }
398 
399   while (1) {
400     Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column);
401     StatusBarSetRefresh();
402 
403     //
404     // ESC pressed
405     //
406     if (Status == EFI_NOT_READY) {
407       return EFI_SUCCESS;
408     }
409     //
410     // The input string length should > 0
411     //
412     if (StrLen (InputBarGetString()) > 0) {
413       //
414       // CHECK if filename is valid
415       //
416       if (!IsValidFileName (InputBarGetString())) {
417         FileBufferRead (MainEditor.FileBuffer->FileName, TRUE);
418         StatusBarSetStatusString (L"Invalid File Name");
419         return EFI_SUCCESS;
420       }
421 
422       break;
423     }
424   }
425   //
426   // read from disk
427   //
428   Status = FileBufferRead (InputBarGetString(), FALSE);
429 
430   if (EFI_ERROR (Status)) {
431     FileBufferRead (MainEditor.FileBuffer->FileName, TRUE);
432     return EFI_LOAD_ERROR;
433   }
434 
435   return EFI_SUCCESS;
436 }
437 
438 /**
439   Switch a file from ASCII to UNICODE or vise-versa.
440 
441   @retval EFI_SUCCESS           The switch was ok or a warning was presented.
442 **/
443 EFI_STATUS
MainCommandSwitchFileType(VOID)444 MainCommandSwitchFileType (
445   VOID
446   )
447 {
448   //
449   // Below is the scenario of File Type command:
450   // After File Type is executed, file type will be changed to another type
451   // if file is read-only, can not be modified
452   //
453   if (MainEditor.FileBuffer->ReadOnly) {
454     StatusBarSetStatusString (L"Read Only File Can Not Be Modified");
455     return EFI_SUCCESS;
456   }
457 
458   if (MainEditor.FileBuffer->FileType == FileTypeUnicode) {
459     MainEditor.FileBuffer->FileType = FileTypeAscii;
460   } else {
461     MainEditor.FileBuffer->FileType = FileTypeUnicode;
462   }
463 
464   MainEditor.FileBuffer->FileModified = TRUE;
465 
466   return EFI_SUCCESS;
467 }
468 
469 /**
470   cut current line to clipboard
471 
472   @retval EFI_SUCCESS             The operation was successful.
473   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
474   @retval EFI_LOAD_ERROR          A load error occured.
475 **/
476 EFI_STATUS
MainCommandCutLine(VOID)477 MainCommandCutLine (
478   VOID
479   )
480 {
481   EFI_STATUS      Status;
482   EFI_EDITOR_LINE *Line;
483 
484   //
485   // This command will cut current line ( where cursor is on ) to clip board.
486   //      And cursor will move to the beginning of next line.
487   // Below is the scenario of Cut Line command:
488   // 1.  IF cursor is on valid line, current line will be cut to clip board.
489   //     IF cursor is not on valid line, an Status String will be prompted :
490   //        "Nothing to Cut".
491   //
492   Line = NULL;
493   Status = FileBufferCutLine (&Line);
494   if (Status == EFI_NOT_FOUND) {
495     return EFI_SUCCESS;
496   }
497 
498   if (EFI_ERROR (Status)) {
499     return Status;
500   }
501 
502   MainEditor.CutLine = Line;
503 
504   return EFI_SUCCESS;
505 }
506 
507 /**
508   paste line to file buffer.
509 
510   @retval EFI_SUCCESS             The operation was successful.
511   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
512   @retval EFI_LOAD_ERROR          A load error occured.
513 **/
514 EFI_STATUS
MainCommandPasteLine(VOID)515 MainCommandPasteLine (
516   VOID
517   )
518 {
519   EFI_STATUS  Status;
520 
521   //
522   // Below is the scenario of Paste Line command:
523   // 1.  IF nothing is on clipboard, a Status String will be prompted :
524   //        "No Line to Paste" and Paste Line command ends.
525   //     IF something is on clipboard, insert it above current line.
526   // nothing on clipboard
527   //
528   if (MainEditor.CutLine == NULL) {
529     StatusBarSetStatusString (L"No Line to Paste");
530     return EFI_SUCCESS;
531   }
532 
533   Status = FileBufferPasteLine ();
534 
535   return Status;
536 }
537 
538 
539 /**
540   search string in file buffer
541 
542   @retval EFI_SUCCESS             The operation was successful.
543   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
544   @retval EFI_LOAD_ERROR          A load error occured.
545 **/
546 EFI_STATUS
MainCommandSearch(VOID)547 MainCommandSearch (
548   VOID
549   )
550 {
551   EFI_STATUS  Status;
552   CHAR16      *Buffer;
553   BOOLEAN     Done;
554   UINTN       Offset;
555 
556   //
557   // Below is the scenario of Search command:
558   // 1.  An Input Bar will be prompted : "Enter Search String:".
559   //       IF user press ESC, Search command ends.
560   //       IF user just press Enter, Search command ends.
561   //       IF user inputs the search string,  do Step 2.
562   //
563   // 2.  IF input search string is found, cursor will move to the first
564   //        occurrence and do Step 3.
565   //     IF input search string is not found, a Status String
566   //        "Search String Not Found" will be prompted and Search command ends.
567   //
568   // 3.  An Input Bar will be prompted: "Find Next (Yes/No/Cancel ) ?".
569   //      IF user press ESC, Search command ends.
570   //       IF user press 'y' or 'Y', do Step 2.
571   //       IF user press 'n' or 'N', Search command ends.
572   //       IF user press 'c' or 'C', Search command ends.
573   //
574   Status = InputBarSetPrompt (L"Enter Search String: ");
575   if (EFI_ERROR (Status)) {
576     return Status;
577   }
578 
579   Status = InputBarSetStringSize (40);
580   if (EFI_ERROR (Status)) {
581     return Status;
582   }
583 
584   Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column);
585   StatusBarSetRefresh();
586 
587   //
588   // ESC
589   //
590   if (Status == EFI_NOT_READY) {
591     return EFI_SUCCESS;
592   }
593   //
594   // just enter pressed
595   //
596   if (StrLen (InputBarGetString()) == 0) {
597     return EFI_SUCCESS;
598   }
599 
600   Buffer = CatSPrint (NULL, L"%s", InputBarGetString());
601   if (Buffer == NULL) {
602     return EFI_OUT_OF_RESOURCES;
603   }
604   //
605   // the first time , search from current position
606   //
607   Offset = 0;
608   do {
609     //
610     // since search may be continued to search multiple times
611     // so we need to backup editor each time
612     //
613     MainEditorBackup ();
614 
615     Status = FileBufferSearch (Buffer, Offset);
616 
617     if (Status == EFI_NOT_FOUND) {
618       break;
619     }
620     //
621     // Find next
622     //
623     Status = InputBarSetPrompt (L"Find Next (Yes/No) ?");
624     if (EFI_ERROR (Status)) {
625       FreePool (Buffer);
626       return Status;
627     }
628 
629     Status = InputBarSetStringSize (1);
630     if (EFI_ERROR (Status)) {
631       FreePool (Buffer);
632       return Status;
633     }
634 
635     Done = FALSE;
636     while (!Done) {
637       Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column);
638       StatusBarSetRefresh();
639 
640       //
641       // ESC pressed
642       //
643       if (Status == EFI_NOT_READY) {
644         FreePool (Buffer);
645         return EFI_SUCCESS;
646       }
647 
648       switch (InputBarGetString()[0]) {
649       case L'y':
650       case L'Y':
651         Done = TRUE;
652         break;
653 
654       case L'n':
655       case L'N':
656         FreePool (Buffer);
657         return EFI_SUCCESS;
658 
659       }
660       //
661       // end of which
662       //
663     }
664     //
665     // end of while !Done
666     // for search second, third time, search from current position + strlen
667     //
668     Offset = StrLen (Buffer);
669 
670   } while (1);
671   //
672   // end of do
673   //
674   FreePool (Buffer);
675   StatusBarSetStatusString (L"Search String Not Found");
676 
677   return EFI_SUCCESS;
678 }
679 
680 /**
681   Search string in file buffer, and replace it with another str.
682 
683   @retval EFI_SUCCESS             The operation was successful.
684   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
685   @retval EFI_LOAD_ERROR          A load error occured.
686 **/
687 EFI_STATUS
MainCommandSearchReplace(VOID)688 MainCommandSearchReplace (
689   VOID
690   )
691 {
692   EFI_STATUS  Status;
693   CHAR16      *Search;
694   CHAR16      *Replace;
695   BOOLEAN     Done;
696   BOOLEAN     First;
697   BOOLEAN     ReplaceOption;
698   UINTN       SearchLen;
699   UINTN       ReplaceLen;
700   BOOLEAN     ReplaceAll;
701 
702   ReplaceOption = FALSE;
703 
704   //
705   // Below is the scenario of Search/Replace command:
706   // 1.  An Input Bar is prompted : "Enter Search String:".
707   //       IF user press ESC, Search/Replace command ends.
708   //       IF user just press Enter, Search/Replace command ends.
709   //       IF user inputs the search string S, do Step 2.
710   //
711   // 2.  An Input Bar is prompted: "Replace With:".
712   //       IF user press ESC, Search/Replace command ends.
713   //      IF user inputs the replace string R, do Step 3.
714   //
715   // 3.  IF input search string is not found, an Status String
716   //        "Search String Not Found" will be prompted
717   //        and Search/Replace command ends
718   //     IF input search string is found, do Step 4.
719   //
720   // 4.  An Input Bar will be prompted: "Replace ( Yes/No/All/Cancel )?"
721   //       IF user press 'y' or 'Y', S will be replaced with R and do Step 5
722   //       IF user press 'n' or 'N', S will not be replaced and do Step 5.
723   //       IF user press 'a' or 'A', all the S from file current position on
724   //          will be replaced with R and Search/Replace command ends.
725   //       IF user press 'c' or 'C' or ESC, Search/Replace command ends.
726   //
727   // 5.  An Input Bar will be prompted: "Find Next (Yes/No/Cancel) ?".
728   //       IF user press ESC, Search/Replace command ends.
729   //       IF user press 'y' or 'Y', do Step 3.
730   //       IF user press 'n' or 'N', Search/Replace command ends.
731   //       IF user press 'c' or 'C', Search/Replace command ends.
732   // input search string
733   //
734   Status = InputBarSetPrompt (L"Enter Search String: ");
735   if (EFI_ERROR (Status)) {
736     return Status;
737   }
738 
739   Status = InputBarSetStringSize (40);
740   if (EFI_ERROR (Status)) {
741     return Status;
742   }
743 
744   Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column);
745   StatusBarSetRefresh();
746 
747   //
748   // ESC
749   //
750   if (Status == EFI_NOT_READY) {
751     return EFI_SUCCESS;
752   }
753   //
754   // if just pressed enter
755   //
756   if (StrLen (InputBarGetString()) == 0) {
757     return EFI_SUCCESS;
758   }
759 
760   Search = CatSPrint (NULL, L"%s", InputBarGetString());
761   if (Search == NULL) {
762     return EFI_OUT_OF_RESOURCES;
763   }
764 
765   SearchLen = StrLen (Search);
766 
767   //
768   // input replace string
769   //
770   Status = InputBarSetPrompt (L"Replace With: ");
771   if (EFI_ERROR (Status)) {
772     return Status;
773   }
774 
775   Status = InputBarSetStringSize (40);
776   if (EFI_ERROR (Status)) {
777     return Status;
778   }
779 
780   Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column);
781   StatusBarSetRefresh();
782 
783   //
784   // ESC
785   //
786   if (Status == EFI_NOT_READY) {
787     return EFI_SUCCESS;
788   }
789 
790   Replace = CatSPrint (NULL, L"%s", InputBarGetString());
791   if (Replace == NULL) {
792     FreePool (Search);
793     return EFI_OUT_OF_RESOURCES;
794   }
795 
796   ReplaceLen  = StrLen (Replace);
797 
798   First       = TRUE;
799   ReplaceAll  = FALSE;
800   do {
801     //
802     // since search may be continued to search multiple times
803     // so we need to backup editor each time
804     //
805     MainEditorBackup ();
806 
807     if (First) {
808       Status = FileBufferSearch (Search, 0);
809     } else {
810       //
811       // if just replace, so skip this replace string
812       // if replace string is an empty string, so skip to next character
813       //
814       if (ReplaceOption) {
815         Status = FileBufferSearch (Search, (ReplaceLen == 0) ? 1 : ReplaceLen);
816       } else {
817         Status = FileBufferSearch (Search, SearchLen);
818       }
819     }
820 
821     if (Status == EFI_NOT_FOUND) {
822       break;
823     }
824     //
825     // replace or not?
826     //
827     Status = InputBarSetPrompt (L"Replace (Yes/No/All/Cancel) ?");
828 
829     if (EFI_ERROR (Status)) {
830       FreePool (Search);
831       FreePool (Replace);
832       return Status;
833     }
834 
835     Status = InputBarSetStringSize (1);
836     if (EFI_ERROR (Status)) {
837       FreePool (Search);
838       FreePool (Replace);
839       return Status;
840     }
841 
842     Done = FALSE;
843     while (!Done) {
844       Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column);
845       StatusBarSetRefresh();
846 
847       //
848       // ESC pressed
849       //
850       if (Status == EFI_NOT_READY) {
851         FreePool (Search);
852         FreePool (Replace);
853         return EFI_SUCCESS;
854       }
855 
856       switch (InputBarGetString()[0]) {
857       case L'y':
858       case L'Y':
859         Done          = TRUE;
860         ReplaceOption = TRUE;
861         break;
862 
863       case L'n':
864       case L'N':
865         Done          = TRUE;
866         ReplaceOption = FALSE;
867         break;
868 
869       case L'a':
870       case L'A':
871         Done          = TRUE;
872         ReplaceOption = TRUE;
873         ReplaceAll    = TRUE;
874         break;
875 
876       case L'c':
877       case L'C':
878         FreePool (Search);
879         FreePool (Replace);
880         return EFI_SUCCESS;
881 
882       }
883       //
884       // end of which
885       //
886     }
887     //
888     // end of while !Done
889     // Decide to Replace
890     //
891     if (ReplaceOption) {
892       //
893       // file is read-only
894       //
895       if (MainEditor.FileBuffer->ReadOnly) {
896         StatusBarSetStatusString (L"Read Only File Can Not Be Modified");
897         return EFI_SUCCESS;
898       }
899       //
900       // replace all
901       //
902       if (ReplaceAll) {
903         Status = FileBufferReplaceAll (Search, Replace, 0);
904         FreePool (Search);
905         FreePool (Replace);
906         return Status;
907       }
908       //
909       // replace
910       //
911       Status = FileBufferReplace (Replace, SearchLen);
912       if (EFI_ERROR (Status)) {
913         FreePool (Search);
914         FreePool (Replace);
915         return Status;
916       }
917     }
918     //
919     // Find next
920     //
921     Status = InputBarSetPrompt (L"Find Next (Yes/No) ?");
922     if (EFI_ERROR (Status)) {
923       FreePool (Search);
924       FreePool (Replace);
925       return Status;
926     }
927 
928     Status = InputBarSetStringSize (1);
929     if (EFI_ERROR (Status)) {
930       FreePool (Search);
931       FreePool (Replace);
932       return Status;
933     }
934 
935     Done = FALSE;
936     while (!Done) {
937       Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column);
938       StatusBarSetRefresh();
939 
940       //
941       // ESC pressed
942       //
943       if (Status == EFI_NOT_READY) {
944         FreePool (Search);
945         FreePool (Replace);
946         return EFI_SUCCESS;
947       }
948 
949       switch (InputBarGetString()[0]) {
950       case L'y':
951       case L'Y':
952         Done = TRUE;
953         break;
954 
955       case L'n':
956       case L'N':
957         FreePool (Search);
958         FreePool (Replace);
959         return EFI_SUCCESS;
960 
961       }
962       //
963       // end of which
964       //
965     }
966     //
967     // end of while !Done
968     //
969     First = FALSE;
970 
971   } while (1);
972   //
973   // end of do
974   //
975   FreePool (Search);
976   FreePool (Replace);
977 
978   StatusBarSetStatusString (L"Search String Not Found");
979 
980   return EFI_SUCCESS;
981 }
982 
983 /**
984   exit editor
985 
986   @retval EFI_SUCCESS             The operation was successful.
987   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
988   @retval EFI_LOAD_ERROR          A load error occured.
989 **/
990 EFI_STATUS
MainCommandExit(VOID)991 MainCommandExit (
992   VOID
993   )
994 {
995   EFI_STATUS  Status;
996 
997   //
998   // Below is the scenario of Exit command:
999   // 1.  IF currently opened file is not modified, exit the editor and
1000   //        Exit command ends.
1001   //     IF currently opened file is modified, do Step 2
1002   //
1003   // 2.  An Input Bar will be prompted:
1004   //        "File modified. Save ( Yes/No/Cancel )?"
1005   //       IF user press 'y' or 'Y', currently opened file will be saved
1006   //          and Editor exits
1007   //       IF user press 'n' or 'N', currently opened file will not be saved
1008   //          and Editor exits.
1009   //       IF user press 'c' or 'C' or ESC, Exit command ends.
1010   // if file has been modified, so will prompt user whether to save the changes
1011   //
1012   if (MainEditor.FileBuffer->FileModified) {
1013 
1014     Status = InputBarSetPrompt (L"File modified. Save (Yes/No/Cancel) ? ");
1015     if (EFI_ERROR (Status)) {
1016       return Status;
1017     }
1018 
1019     Status = InputBarSetStringSize (1);
1020     if (EFI_ERROR (Status)) {
1021       return Status;
1022     }
1023 
1024     while (1) {
1025       Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column);
1026       StatusBarSetRefresh();
1027 
1028       //
1029       // ESC pressed
1030       //
1031       if (Status == EFI_NOT_READY) {
1032         return EFI_SUCCESS;
1033       }
1034 
1035       switch (InputBarGetString()[0]) {
1036       case L'y':
1037       case L'Y':
1038         //
1039         // write file back to disk
1040         //
1041         Status = FileBufferSave (MainEditor.FileBuffer->FileName);
1042         if (!EFI_ERROR (Status)) {
1043           EditorExit = TRUE;
1044         }
1045 
1046         return Status;
1047 
1048       case L'n':
1049       case L'N':
1050         EditorExit = TRUE;
1051         return EFI_SUCCESS;
1052 
1053       case L'c':
1054       case L'C':
1055         return EFI_SUCCESS;
1056 
1057       }
1058     }
1059   }
1060 
1061   EditorExit = TRUE;
1062   return EFI_SUCCESS;
1063 
1064 }
1065 
1066 /**
1067   move cursor to specified lines
1068 
1069   @retval EFI_SUCCESS             The operation was successful.
1070 **/
1071 EFI_STATUS
MainCommandGotoLine(VOID)1072 MainCommandGotoLine (
1073   VOID
1074   )
1075 {
1076   EFI_STATUS  Status;
1077   UINTN       Row;
1078 
1079   //
1080   // Below is the scenario of Go To Line command:
1081   // 1.  An Input Bar will be prompted : "Go To Line:".
1082   //       IF user press ESC, Go To Line command ends.
1083   //       IF user just press Enter, cursor remains unchanged.
1084   //       IF user inputs line number, do Step 2.
1085   //
1086   // 2.  IF input line number is valid, move cursor to the beginning
1087   //        of specified line and Go To Line command ends.
1088   //    IF input line number is invalid, a Status String will be prompted:
1089   //        "No Such Line" and Go To Line command ends.
1090   //
1091   Status = InputBarSetPrompt (L"Go To Line: ");
1092   if (EFI_ERROR (Status)) {
1093     return Status;
1094   }
1095   //
1096   // line number's digit <= 6
1097   //
1098   Status = InputBarSetStringSize (6);
1099   if (EFI_ERROR (Status)) {
1100     return Status;
1101   }
1102 
1103   Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column);
1104   StatusBarSetRefresh();
1105 
1106   //
1107   // press ESC
1108   //
1109   if (Status == EFI_NOT_READY) {
1110     return EFI_SUCCESS;
1111   }
1112   //
1113   // if JUST press enter
1114   //
1115   if (StrLen (InputBarGetString()) == 0) {
1116     return EFI_SUCCESS;
1117   }
1118 
1119   Row = ShellStrToUintn (InputBarGetString());
1120 
1121   //
1122   // invalid line number
1123   //
1124   if (Row > MainEditor.FileBuffer->NumLines || Row <= 0) {
1125     StatusBarSetStatusString (L"No Such Line");
1126     return EFI_SUCCESS;
1127   }
1128   //
1129   // move cursor to that line's start
1130   //
1131   FileBufferMovePosition (Row, 1);
1132 
1133   return EFI_SUCCESS;
1134 }
1135 
1136 /**
1137   Save current file to disk, you can save to current file name or
1138   save to another file name.
1139 
1140   @retval EFI_SUCCESS           The file was saved correctly.
1141   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
1142   @retval EFI_LOAD_ERROR          A file access error occured.
1143 **/
1144 EFI_STATUS
MainCommandSaveFile(VOID)1145 MainCommandSaveFile (
1146   VOID
1147   )
1148 {
1149   EFI_STATUS        Status;
1150   CHAR16            *FileName;
1151   BOOLEAN           OldFile;
1152   CHAR16            *Str;
1153   SHELL_FILE_HANDLE FileHandle;
1154   EFI_FILE_INFO     *Info;
1155 
1156   //
1157   // This command will save currently opened file to disk.
1158   // You can choose save to another file name or just save to
1159   //    current file name.
1160   // Below is the scenario of Save File command:
1161   //    ( Suppose the old file name is A )
1162   // 1.  An Input Bar will be prompted:    "File To Save: [ old file name]"
1163   //     IF user press ESC, Save File command ends .
1164   //     IF user press Enter, input file name will be A.
1165   //     IF user inputs a new file name B,  input file name will be B.
1166   //
1167   // 2.  IF input file name is A, go to do Step 3.
1168   //     IF input file name is B, go to do Step 4.
1169   //
1170   // 3.  IF A is read only, Status Bar will show "Access Denied" and
1171   //       Save File commands ends.
1172   //     IF A is not read only, save file buffer to disk and remove modified
1173   //       flag in Title Bar , then Save File command ends.
1174   //
1175   // 4.  IF B does not exist, create this file and save file buffer to it.
1176   //       Go to do Step 7.
1177   //     IF B exits, do Step 5.
1178   //
1179   // 5.An Input Bar will be prompted:
1180   //      "File Exists. Overwrite ( Yes/No/Cancel )?"
1181   //       IF user press 'y' or 'Y', do Step 6.
1182   //       IF user press 'n' or 'N', Save File commands ends.
1183   //       IF user press 'c' or 'C' or ESC, Save File commands ends.
1184   //
1185   // 6. IF B is a read-only file, Status Bar will show "Access Denied" and
1186   //       Save File commands ends.
1187   //    IF B can be read and write, save file buffer to B.
1188   //
1189   // 7.  Update File Name field in Title Bar to B and remove the modified
1190   //       flag in Title Bar.
1191   //
1192   Str = CatSPrint (NULL, L"File to Save: [%s]", MainEditor.FileBuffer->FileName);
1193   if (Str == NULL) {
1194     return EFI_OUT_OF_RESOURCES;
1195   }
1196 
1197   if (StrLen (Str) >= 50) {
1198     //
1199     // replace the long file name with "..."
1200     //
1201     Str[46] = L'.';
1202     Str[47] = L'.';
1203     Str[48] = L'.';
1204     Str[49] = L']';
1205     Str[50] = CHAR_NULL;
1206   }
1207 
1208   Status = InputBarSetPrompt (Str);
1209   FreePool(Str);
1210 
1211   if (EFI_ERROR (Status)) {
1212     return Status;
1213   }
1214 
1215 
1216   Status = InputBarSetStringSize (100);
1217   if (EFI_ERROR (Status)) {
1218     return Status;
1219   }
1220   //
1221   // get new file name
1222   //
1223   Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column);
1224   StatusBarSetRefresh();
1225 
1226   //
1227   // if user pressed ESC
1228   //
1229   if (Status == EFI_NOT_READY) {
1230     return EFI_SUCCESS;
1231   }
1232 
1233   //
1234   // if just enter pressed, so think save to current file name
1235   //
1236   if (StrLen (InputBarGetString()) == 0) {
1237     FileName = CatSPrint (NULL, L"%s", MainEditor.FileBuffer->FileName);
1238   } else {
1239     FileName = CatSPrint (NULL, L"%s", InputBarGetString());
1240   }
1241 
1242   if (FileName == NULL) {
1243     return EFI_OUT_OF_RESOURCES;
1244   }
1245 
1246   if (!IsValidFileName (FileName)) {
1247     StatusBarSetStatusString (L"Invalid File Name");
1248     FreePool (FileName);
1249     return EFI_SUCCESS;
1250   }
1251 
1252   OldFile = FALSE;
1253 
1254   //
1255   // save to the old file
1256   //
1257   if (StringNoCaseCompare (&FileName, &MainEditor.FileBuffer->FileName) == 0) {
1258     OldFile = TRUE;
1259   }
1260 
1261   if (OldFile) {
1262     //
1263     // if the file is read only, so can not write back to it.
1264     //
1265     if (MainEditor.FileBuffer->ReadOnly == TRUE) {
1266       StatusBarSetStatusString (L"Access Denied");
1267       FreePool (FileName);
1268       return EFI_SUCCESS;
1269     }
1270   } else {
1271     //
1272     // if the file exists
1273     //
1274     if (ShellFileExists(FileName) != EFI_NOT_FOUND) {
1275       //
1276       // check for read only
1277       //
1278       Status = ShellOpenFileByName(FileName, &FileHandle, EFI_FILE_MODE_READ, 0);
1279       if (EFI_ERROR(Status)) {
1280         StatusBarSetStatusString (L"Open Failed");
1281         FreePool (FileName);
1282         return EFI_SUCCESS;
1283       }
1284 
1285       Info = ShellGetFileInfo(FileHandle);
1286       if (Info == NULL) {
1287         StatusBarSetStatusString (L"Access Denied");
1288         FreePool (FileName);
1289         return (EFI_SUCCESS);
1290       }
1291 
1292       if (Info->Attribute & EFI_FILE_READ_ONLY) {
1293         StatusBarSetStatusString (L"Access Denied - Read Only");
1294         FreePool (Info);
1295         FreePool (FileName);
1296         return (EFI_SUCCESS);
1297       }
1298       FreePool (Info);
1299 
1300       //
1301       // ask user whether to overwrite this file
1302       //
1303       Status = InputBarSetPrompt (L"File exists. Overwrite (Yes/No/Cancel) ? ");
1304       if (EFI_ERROR (Status)) {
1305         SHELL_FREE_NON_NULL (FileName);
1306         return Status;
1307       }
1308 
1309       Status = InputBarSetStringSize (1);
1310       if (EFI_ERROR (Status)) {
1311         SHELL_FREE_NON_NULL (FileName);
1312         return Status;
1313       }
1314 
1315       while (TRUE) {
1316         Status = InputBarRefresh (MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column);
1317         StatusBarSetRefresh();
1318 
1319         //
1320         // ESC pressed
1321         //
1322         if (Status == EFI_NOT_READY) {
1323           SHELL_FREE_NON_NULL (FileName);
1324           return EFI_SUCCESS;
1325         }
1326 
1327         switch (InputBarGetString()[0]) {
1328         case L'y':
1329         case L'Y':
1330           break;
1331 
1332         case L'n':
1333         case L'N':
1334         case L'c':
1335         case L'C':
1336           SHELL_FREE_NON_NULL (FileName);
1337           return EFI_SUCCESS;
1338         } // end switch
1339       } // while (!done)
1340     } // file does exist
1341   } // if old file name same
1342 
1343   //
1344   // save file to disk with specified name
1345   //
1346   FileBufferSetModified();
1347   Status = FileBufferSave (FileName);
1348   SHELL_FREE_NON_NULL (FileName);
1349 
1350   return Status;
1351 }
1352 
1353 /**
1354   Show help information for the editor.
1355 
1356   @retval EFI_SUCCESS             The operation was successful.
1357 **/
1358 EFI_STATUS
MainCommandDisplayHelp(VOID)1359 MainCommandDisplayHelp (
1360   VOID
1361   )
1362 {
1363   INT32           CurrentLine;
1364   CHAR16          *InfoString;
1365   EFI_INPUT_KEY   Key;
1366 
1367   //
1368   // print helpInfo
1369   //
1370   for (CurrentLine = 0; 0 != MainMenuHelpInfo[CurrentLine]; CurrentLine++) {
1371     InfoString = HiiGetString(gShellDebug1HiiHandle, MainMenuHelpInfo[CurrentLine], NULL);
1372     ShellPrintEx (0, CurrentLine+1, L"%E%s%N", InfoString);
1373   }
1374 
1375   //
1376   // scan for ctrl+w
1377   //
1378   do {
1379     gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
1380   } while(SCAN_CONTROL_W != Key.UnicodeChar);
1381 
1382   //
1383   // update screen with file buffer's info
1384   //
1385   FileBufferRestorePosition ();
1386   FileBufferNeedRefresh = TRUE;
1387   FileBufferOnlyLineNeedRefresh = FALSE;
1388   FileBufferRefresh ();
1389 
1390   return EFI_SUCCESS;
1391 }
1392 
1393 EFI_EDITOR_COLOR_ATTRIBUTES   OriginalColors;
1394 INTN                          OriginalMode;
1395 
1396 
1397 //
1398 // basic initialization for MainEditor
1399 //
1400 EFI_EDITOR_GLOBAL_EDITOR      MainEditorConst = {
1401   &FileBuffer,
1402   {
1403     {0, 0}
1404   },
1405   {
1406     0,
1407     0
1408   },
1409   NULL,
1410   FALSE,
1411   NULL
1412 };
1413 
1414 /**
1415   The initialization function for MainEditor.
1416 
1417   @retval EFI_SUCCESS             The operation was successful.
1418   @retval EFI_LOAD_ERROR          A load error occured.
1419 **/
1420 EFI_STATUS
MainEditorInit(VOID)1421 MainEditorInit (
1422   VOID
1423   )
1424 {
1425   EFI_STATUS  Status;
1426   EFI_HANDLE  *HandleBuffer;
1427   UINTN       HandleCount;
1428   UINTN       Index;
1429 
1430   //
1431   // basic initialization
1432   //
1433   CopyMem (&MainEditor, &MainEditorConst, sizeof (MainEditor));
1434 
1435   //
1436   // set screen attributes
1437   //
1438   MainEditor.ColorAttributes.Colors.Foreground  = gST->ConOut->Mode->Attribute & 0x000000ff;
1439 
1440   MainEditor.ColorAttributes.Colors.Background  = (UINT8) (gST->ConOut->Mode->Attribute >> 4);
1441   OriginalColors = MainEditor.ColorAttributes.Colors;
1442 
1443   OriginalMode = gST->ConOut->Mode->Mode;
1444 
1445   //
1446   // query screen size
1447   //
1448   gST->ConOut->QueryMode (
1449         gST->ConOut,
1450         gST->ConOut->Mode->Mode,
1451         &(MainEditor.ScreenSize.Column),
1452         &(MainEditor.ScreenSize.Row)
1453         );
1454 
1455   //
1456   // Find mouse in System Table ConsoleInHandle
1457   //
1458   Status = gBS->HandleProtocol (
1459                 gST->ConIn,
1460                 &gEfiSimplePointerProtocolGuid,
1461                 (VOID**)&MainEditor.MouseInterface
1462                 );
1463   if (EFI_ERROR (Status)) {
1464     //
1465     // If there is no Simple Pointer Protocol on System Table
1466     //
1467     HandleBuffer = NULL;
1468     MainEditor.MouseInterface = NULL;
1469     Status = gBS->LocateHandleBuffer (
1470                   ByProtocol,
1471                   &gEfiSimplePointerProtocolGuid,
1472                   NULL,
1473                   &HandleCount,
1474                   &HandleBuffer
1475                   );
1476     if (!EFI_ERROR (Status) && HandleCount > 0) {
1477       //
1478       // Try to find the first available mouse device
1479       //
1480       for (Index = 0; Index < HandleCount; Index++) {
1481         Status = gBS->HandleProtocol (
1482                       HandleBuffer[Index],
1483                       &gEfiSimplePointerProtocolGuid,
1484                       (VOID**)&MainEditor.MouseInterface
1485                       );
1486         if (!EFI_ERROR (Status)) {
1487           break;
1488         }
1489       }
1490     }
1491     if (HandleBuffer != NULL) {
1492       FreePool (HandleBuffer);
1493     }
1494   }
1495 
1496   if (!EFI_ERROR (Status) && MainEditor.MouseInterface != NULL) {
1497     MainEditor.MouseAccumulatorX  = 0;
1498     MainEditor.MouseAccumulatorY  = 0;
1499     MainEditor.MouseSupported     = TRUE;
1500   }
1501 
1502   //
1503   // below will call the five components' init function
1504   //
1505   Status = MainTitleBarInit (L"UEFI EDIT");
1506   if (EFI_ERROR (Status)) {
1507     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_TITLEBAR), gShellDebug1HiiHandle);
1508     return EFI_LOAD_ERROR;
1509   }
1510 
1511   Status = ControlHotKeyInit (MainControlBasedMenuFunctions);
1512   Status = MenuBarInit (MainMenuItems);
1513   if (EFI_ERROR (Status)) {
1514     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_MAINMENU), gShellDebug1HiiHandle);
1515     return EFI_LOAD_ERROR;
1516   }
1517 
1518   Status = StatusBarInit ();
1519   if (EFI_ERROR (Status)) {
1520     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_STATUSBAR), gShellDebug1HiiHandle);
1521     return EFI_LOAD_ERROR;
1522   }
1523 
1524   InputBarInit ();
1525 
1526   Status = FileBufferInit ();
1527   if (EFI_ERROR (Status)) {
1528     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_FILEBUFFER), gShellDebug1HiiHandle);
1529     return EFI_LOAD_ERROR;
1530   }
1531   //
1532   // clear whole screen and enable cursor
1533   //
1534   gST->ConOut->ClearScreen (gST->ConOut);
1535   gST->ConOut->EnableCursor (gST->ConOut, TRUE);
1536 
1537   //
1538   // initialize EditorFirst and EditorExit
1539   //
1540   EditorFirst       = TRUE;
1541   EditorExit        = FALSE;
1542   EditorMouseAction = FALSE;
1543 
1544   return EFI_SUCCESS;
1545 }
1546 
1547 /**
1548   The cleanup function for MainEditor.
1549 
1550   @retval EFI_SUCCESS             The operation was successful.
1551   @retval EFI_LOAD_ERROR          A load error occured.
1552 **/
1553 EFI_STATUS
MainEditorCleanup(VOID)1554 MainEditorCleanup (
1555   VOID
1556   )
1557 {
1558   EFI_STATUS  Status;
1559 
1560   //
1561   // call the five components' cleanup function
1562   // if error, do not exit
1563   // just print some warning
1564   //
1565   MainTitleBarCleanup();
1566   StatusBarCleanup();
1567   InputBarCleanup();
1568   MenuBarCleanup ();
1569 
1570   Status = FileBufferCleanup ();
1571   if (EFI_ERROR (Status)) {
1572     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_FILEBUFFER_CLEANUP), gShellDebug1HiiHandle);
1573   }
1574   //
1575   // restore old mode
1576   //
1577   if (OriginalMode != gST->ConOut->Mode->Mode) {
1578     gST->ConOut->SetMode (gST->ConOut, OriginalMode);
1579   }
1580   //
1581   // restore old screen color
1582   //
1583   gST->ConOut->SetAttribute (
1584         gST->ConOut,
1585         EFI_TEXT_ATTR (OriginalColors.Foreground, OriginalColors.Background)
1586         );
1587 
1588   gST->ConOut->ClearScreen (gST->ConOut);
1589 
1590   return EFI_SUCCESS;
1591 }
1592 
1593 /**
1594   Refresh the main editor component.
1595 **/
1596 VOID
MainEditorRefresh(VOID)1597 MainEditorRefresh (
1598   VOID
1599   )
1600 {
1601   //
1602   // The Stall value is from experience. NOT from spec.  avoids 'flicker'
1603   //
1604   gBS->Stall (50);
1605 
1606   //
1607   // call the components refresh function
1608   //
1609   if (EditorFirst
1610     || StrCmp (FileBufferBackupVar.FileName, FileBuffer.FileName) != 0
1611     || FileBufferBackupVar.FileType != FileBuffer.FileType
1612     || FileBufferBackupVar.FileModified != FileBuffer.FileModified
1613     || FileBufferBackupVar.ReadOnly != FileBuffer.ReadOnly) {
1614 
1615     MainTitleBarRefresh (MainEditor.FileBuffer->FileName, MainEditor.FileBuffer->FileType, MainEditor.FileBuffer->ReadOnly, MainEditor.FileBuffer->FileModified, MainEditor.ScreenSize.Column, MainEditor.ScreenSize.Row, 0, 0);
1616     FileBufferRestorePosition ();
1617   }
1618 
1619   if (EditorFirst
1620     || FileBufferBackupVar.FilePosition.Row != FileBuffer.FilePosition.Row
1621     || FileBufferBackupVar.FilePosition.Column != FileBuffer.FilePosition.Column
1622     || FileBufferBackupVar.ModeInsert != FileBuffer.ModeInsert
1623     || StatusBarGetRefresh()) {
1624 
1625     StatusBarRefresh (EditorFirst, MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column, MainEditor.FileBuffer->FilePosition.Row, MainEditor.FileBuffer->FilePosition.Column, MainEditor.FileBuffer->ModeInsert);
1626     FileBufferRestorePosition ();
1627   }
1628 
1629   if (EditorFirst) {
1630     FileBufferRestorePosition ();
1631   }
1632 
1633   FileBufferRefresh ();
1634 
1635   //
1636   // EditorFirst is now set to FALSE
1637   //
1638   EditorFirst = FALSE;
1639 }
1640 
1641 /**
1642   Get's the resultant location of the cursor based on the relative movement of the Mouse.
1643 
1644   @param[in] GuidX    The relative mouse movement.
1645 
1646   @return The X location of the mouse.
1647 **/
1648 INT32
GetTextX(IN INT32 GuidX)1649 GetTextX (
1650   IN INT32 GuidX
1651   )
1652 {
1653   INT32 Gap;
1654 
1655   MainEditor.MouseAccumulatorX += GuidX;
1656   Gap = (MainEditor.MouseAccumulatorX * (INT32) MainEditor.ScreenSize.Column) / (INT32) (50 * (INT32) MainEditor.MouseInterface->Mode->ResolutionX);
1657   MainEditor.MouseAccumulatorX = (MainEditor.MouseAccumulatorX * (INT32) MainEditor.ScreenSize.Column) % (INT32) (50 * (INT32) MainEditor.MouseInterface->Mode->ResolutionX);
1658   MainEditor.MouseAccumulatorX = MainEditor.MouseAccumulatorX / (INT32) MainEditor.ScreenSize.Column;
1659   return Gap;
1660 }
1661 
1662 /**
1663   Get's the resultant location of the cursor based on the relative movement of the Mouse.
1664 
1665   @param[in] GuidY    The relative mouse movement.
1666 
1667   @return The Y location of the mouse.
1668 **/
1669 INT32
GetTextY(IN INT32 GuidY)1670 GetTextY (
1671   IN INT32 GuidY
1672   )
1673 {
1674   INT32 Gap;
1675 
1676   MainEditor.MouseAccumulatorY += GuidY;
1677   Gap = (MainEditor.MouseAccumulatorY * (INT32) MainEditor.ScreenSize.Row) / (INT32) (50 * (INT32) MainEditor.MouseInterface->Mode->ResolutionY);
1678   MainEditor.MouseAccumulatorY = (MainEditor.MouseAccumulatorY * (INT32) MainEditor.ScreenSize.Row) % (INT32) (50 * (INT32) MainEditor.MouseInterface->Mode->ResolutionY);
1679   MainEditor.MouseAccumulatorY = MainEditor.MouseAccumulatorY / (INT32) MainEditor.ScreenSize.Row;
1680 
1681   return Gap;
1682 }
1683 
1684 /**
1685   Support mouse movement.  Move the cursor.
1686 
1687   @param[in] MouseState     The current mouse state.
1688 
1689   @retval EFI_SUCCESS       The operation was successful.
1690   @retval EFI_NOT_FOUND     There was no mouse support found.
1691 **/
1692 EFI_STATUS
MainEditorHandleMouseInput(IN EFI_SIMPLE_POINTER_STATE MouseState)1693 MainEditorHandleMouseInput (
1694   IN EFI_SIMPLE_POINTER_STATE       MouseState
1695   )
1696 {
1697 
1698   INT32           TextX;
1699   INT32           TextY;
1700   UINTN           FRow;
1701   UINTN           FCol;
1702 
1703   LIST_ENTRY  *Link;
1704   EFI_EDITOR_LINE *Line;
1705 
1706   UINTN           Index;
1707   BOOLEAN         Action;
1708 
1709   //
1710   // mouse action means:
1711   //    mouse movement
1712   //    mouse left button
1713   //
1714   Action = FALSE;
1715 
1716   //
1717   // have mouse movement
1718   //
1719   if (MouseState.RelativeMovementX || MouseState.RelativeMovementY) {
1720     //
1721     // handle
1722     //
1723     TextX = GetTextX (MouseState.RelativeMovementX);
1724     TextY = GetTextY (MouseState.RelativeMovementY);
1725 
1726     FileBufferAdjustMousePosition (TextX, TextY);
1727 
1728     Action = TRUE;
1729 
1730   }
1731 
1732   //
1733   // if left button pushed down
1734   //
1735   if (MouseState.LeftButton) {
1736 
1737     FCol = MainEditor.FileBuffer->MousePosition.Column - 1 + 1;
1738 
1739     FRow = MainEditor.FileBuffer->FilePosition.Row +
1740       MainEditor.FileBuffer->MousePosition.Row -
1741       MainEditor.FileBuffer->DisplayPosition.Row;
1742 
1743     //
1744     // beyond the file line length
1745     //
1746     if (MainEditor.FileBuffer->NumLines < FRow) {
1747       FRow = MainEditor.FileBuffer->NumLines;
1748     }
1749 
1750     Link = MainEditor.FileBuffer->ListHead->ForwardLink;
1751     for (Index = 0; Index < FRow - 1; Index++) {
1752       Link = Link->ForwardLink;
1753     }
1754 
1755     Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
1756 
1757     //
1758     // beyond the line's column length
1759     //
1760     if (FCol > Line->Size + 1) {
1761       FCol = Line->Size + 1;
1762     }
1763 
1764     FileBufferMovePosition (FRow, FCol);
1765 
1766     MainEditor.FileBuffer->MousePosition.Row    = MainEditor.FileBuffer->DisplayPosition.Row;
1767 
1768     MainEditor.FileBuffer->MousePosition.Column = MainEditor.FileBuffer->DisplayPosition.Column;
1769 
1770     Action = TRUE;
1771   }
1772   //
1773   // mouse has action
1774   //
1775   if (Action) {
1776     return EFI_SUCCESS;
1777   }
1778 
1779   //
1780   // no mouse action
1781   //
1782   return EFI_NOT_FOUND;
1783 }
1784 
1785 /**
1786   Handle user key input. This routes to other functions for the actions.
1787 
1788   @retval EFI_SUCCESS             The operation was successful.
1789   @retval EFI_LOAD_ERROR          A load error occured.
1790   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
1791 **/
1792 EFI_STATUS
MainEditorKeyInput(VOID)1793 MainEditorKeyInput (
1794   VOID
1795   )
1796 {
1797   EFI_INPUT_KEY             Key;
1798   EFI_STATUS                Status;
1799   EFI_SIMPLE_POINTER_STATE  MouseState;
1800 
1801   do {
1802 
1803     Status            = EFI_SUCCESS;
1804     EditorMouseAction = FALSE;
1805 
1806     //
1807     // backup some key elements, so that can aVOID some refresh work
1808     //
1809     MainEditorBackup ();
1810 
1811     //
1812     // change priority of checking mouse/keyboard activity dynamically
1813     // so prevent starvation of keyboard.
1814     // if last time, mouse moves then this time check keyboard
1815     //
1816     if (MainEditor.MouseSupported) {
1817       Status = MainEditor.MouseInterface->GetState (
1818                                             MainEditor.MouseInterface,
1819                                             &MouseState
1820                                             );
1821       if (!EFI_ERROR (Status)) {
1822 
1823         Status = MainEditorHandleMouseInput (MouseState);
1824 
1825         if (!EFI_ERROR (Status)) {
1826           EditorMouseAction           = TRUE;
1827           FileBufferMouseNeedRefresh  = TRUE;
1828         } else if (Status == EFI_LOAD_ERROR) {
1829           StatusBarSetStatusString (L"Invalid Mouse Movement ");
1830         }
1831       }
1832     }
1833 
1834     Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
1835     if (!EFI_ERROR (Status)) {
1836       //
1837       // dispatch to different components' key handling function
1838       // so not everywhere has to set this variable
1839       //
1840       FileBufferMouseNeedRefresh = TRUE;
1841       //
1842       // clear previous status string
1843       //
1844       StatusBarSetRefresh();
1845 
1846       //
1847       // dispatch to different components' key handling function
1848       //
1849       if (EFI_NOT_FOUND != MenuBarDispatchControlHotKey(&Key)) {
1850         Status = EFI_SUCCESS;
1851       } else if ((Key.ScanCode == SCAN_NULL) || ((Key.ScanCode >= SCAN_UP) && (Key.ScanCode <= SCAN_PAGE_DOWN))) {
1852         Status = FileBufferHandleInput (&Key);
1853       } else if ((Key.ScanCode >= SCAN_F1) && (Key.ScanCode <= SCAN_F12)) {
1854         Status = MenuBarDispatchFunctionKey (&Key);
1855       } else {
1856         StatusBarSetStatusString (L"Unknown Command");
1857         FileBufferMouseNeedRefresh = FALSE;
1858       }
1859 
1860       if (Status != EFI_SUCCESS && Status != EFI_OUT_OF_RESOURCES) {
1861         //
1862         // not already has some error status
1863         //
1864         if (StatusBarGetString() != NULL && StrCmp (L"", StatusBarGetString()) == 0) {
1865           StatusBarSetStatusString (L"Disk Error. Try Again");
1866         }
1867       }
1868 
1869     }
1870     //
1871     // after handling, refresh editor
1872     //
1873     MainEditorRefresh ();
1874 
1875   } while (Status != EFI_OUT_OF_RESOURCES && !EditorExit);
1876 
1877   return Status;
1878 }
1879 
1880 /**
1881   Set clipboard
1882 
1883   @param[in] Line   A pointer to the line to be set to clipboard
1884 
1885   @retval EFI_SUCCESS             The operation was successful.
1886   @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1887 **/
1888 EFI_STATUS
MainEditorSetCutLine(EFI_EDITOR_LINE * Line)1889 MainEditorSetCutLine (
1890   EFI_EDITOR_LINE *Line
1891   )
1892 {
1893   if (Line == NULL) {
1894     return EFI_SUCCESS;
1895   }
1896 
1897   if (MainEditor.CutLine != NULL) {
1898     //
1899     // free the old clipboard
1900     //
1901     LineFree (MainEditor.CutLine);
1902   }
1903   //
1904   // duplicate the line to clipboard
1905   //
1906   MainEditor.CutLine = LineDup (Line);
1907   if (MainEditor.CutLine == NULL) {
1908     return EFI_OUT_OF_RESOURCES;
1909   }
1910 
1911   return EFI_SUCCESS;
1912 }
1913 
1914 /**
1915   Backup function for MainEditor
1916 
1917   @retval EFI_SUCCESS The operation was successful.
1918 **/
1919 EFI_STATUS
MainEditorBackup(VOID)1920 MainEditorBackup (
1921   VOID
1922   )
1923 {
1924   FileBufferBackup ();
1925 
1926   return EFI_SUCCESS;
1927 }
1928