1 /** @file
2   Implements editor interface functions.
3 
4   Copyright (c) 2005 - 2014, 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
1421 EFIAPI
MainEditorInit(VOID)1422 MainEditorInit (
1423   VOID
1424   )
1425 {
1426   EFI_STATUS  Status;
1427   EFI_HANDLE  *HandleBuffer;
1428   UINTN       HandleCount;
1429   UINTN       Index;
1430 
1431   //
1432   // basic initialization
1433   //
1434   CopyMem (&MainEditor, &MainEditorConst, sizeof (MainEditor));
1435 
1436   //
1437   // set screen attributes
1438   //
1439   MainEditor.ColorAttributes.Colors.Foreground  = gST->ConOut->Mode->Attribute & 0x000000ff;
1440 
1441   MainEditor.ColorAttributes.Colors.Background  = (UINT8) (gST->ConOut->Mode->Attribute >> 4);
1442   OriginalColors = MainEditor.ColorAttributes.Colors;
1443 
1444   OriginalMode = gST->ConOut->Mode->Mode;
1445 
1446   //
1447   // query screen size
1448   //
1449   gST->ConOut->QueryMode (
1450         gST->ConOut,
1451         gST->ConOut->Mode->Mode,
1452         &(MainEditor.ScreenSize.Column),
1453         &(MainEditor.ScreenSize.Row)
1454         );
1455 
1456   //
1457   // Find mouse in System Table ConsoleInHandle
1458   //
1459   Status = gBS->HandleProtocol (
1460                 gST->ConIn,
1461                 &gEfiSimplePointerProtocolGuid,
1462                 (VOID**)&MainEditor.MouseInterface
1463                 );
1464   if (EFI_ERROR (Status)) {
1465     //
1466     // If there is no Simple Pointer Protocol on System Table
1467     //
1468     HandleBuffer = NULL;
1469     MainEditor.MouseInterface = NULL;
1470     Status = gBS->LocateHandleBuffer (
1471                   ByProtocol,
1472                   &gEfiSimplePointerProtocolGuid,
1473                   NULL,
1474                   &HandleCount,
1475                   &HandleBuffer
1476                   );
1477     if (!EFI_ERROR (Status) && HandleCount > 0) {
1478       //
1479       // Try to find the first available mouse device
1480       //
1481       for (Index = 0; Index < HandleCount; Index++) {
1482         Status = gBS->HandleProtocol (
1483                       HandleBuffer[Index],
1484                       &gEfiSimplePointerProtocolGuid,
1485                       (VOID**)&MainEditor.MouseInterface
1486                       );
1487         if (!EFI_ERROR (Status)) {
1488           break;
1489         }
1490       }
1491     }
1492     if (HandleBuffer != NULL) {
1493       FreePool (HandleBuffer);
1494     }
1495   }
1496 
1497   if (!EFI_ERROR (Status) && MainEditor.MouseInterface != NULL) {
1498     MainEditor.MouseAccumulatorX  = 0;
1499     MainEditor.MouseAccumulatorY  = 0;
1500     MainEditor.MouseSupported     = TRUE;
1501   }
1502 
1503   //
1504   // below will call the five components' init function
1505   //
1506   Status = MainTitleBarInit (L"UEFI EDIT");
1507   if (EFI_ERROR (Status)) {
1508     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_TITLEBAR), gShellDebug1HiiHandle);
1509     return EFI_LOAD_ERROR;
1510   }
1511 
1512   Status = ControlHotKeyInit (MainControlBasedMenuFunctions);
1513   Status = MenuBarInit (MainMenuItems);
1514   if (EFI_ERROR (Status)) {
1515     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_MAINMENU), gShellDebug1HiiHandle);
1516     return EFI_LOAD_ERROR;
1517   }
1518 
1519   Status = StatusBarInit ();
1520   if (EFI_ERROR (Status)) {
1521     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_STATUSBAR), gShellDebug1HiiHandle);
1522     return EFI_LOAD_ERROR;
1523   }
1524 
1525   InputBarInit ();
1526 
1527   Status = FileBufferInit ();
1528   if (EFI_ERROR (Status)) {
1529     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_FILEBUFFER), gShellDebug1HiiHandle);
1530     return EFI_LOAD_ERROR;
1531   }
1532   //
1533   // clear whole screen and enable cursor
1534   //
1535   gST->ConOut->ClearScreen (gST->ConOut);
1536   gST->ConOut->EnableCursor (gST->ConOut, TRUE);
1537 
1538   //
1539   // initialize EditorFirst and EditorExit
1540   //
1541   EditorFirst       = TRUE;
1542   EditorExit        = FALSE;
1543   EditorMouseAction = FALSE;
1544 
1545   return EFI_SUCCESS;
1546 }
1547 
1548 /**
1549   The cleanup function for MainEditor.
1550 
1551   @retval EFI_SUCCESS             The operation was successful.
1552   @retval EFI_LOAD_ERROR          A load error occured.
1553 **/
1554 EFI_STATUS
1555 EFIAPI
MainEditorCleanup(VOID)1556 MainEditorCleanup (
1557   VOID
1558   )
1559 {
1560   EFI_STATUS  Status;
1561 
1562   //
1563   // call the five components' cleanup function
1564   // if error, do not exit
1565   // just print some warning
1566   //
1567   MainTitleBarCleanup();
1568   StatusBarCleanup();
1569   InputBarCleanup();
1570   MenuBarCleanup ();
1571 
1572   Status = FileBufferCleanup ();
1573   if (EFI_ERROR (Status)) {
1574     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_EDIT_LIBEDITOR_FILEBUFFER_CLEANUP), gShellDebug1HiiHandle);
1575   }
1576   //
1577   // restore old mode
1578   //
1579   if (OriginalMode != gST->ConOut->Mode->Mode) {
1580     gST->ConOut->SetMode (gST->ConOut, OriginalMode);
1581   }
1582   //
1583   // restore old screen color
1584   //
1585   gST->ConOut->SetAttribute (
1586         gST->ConOut,
1587         EFI_TEXT_ATTR (OriginalColors.Foreground, OriginalColors.Background)
1588         );
1589 
1590   gST->ConOut->ClearScreen (gST->ConOut);
1591 
1592   return EFI_SUCCESS;
1593 }
1594 
1595 /**
1596   Refresh the main editor component.
1597 **/
1598 VOID
1599 EFIAPI
MainEditorRefresh(VOID)1600 MainEditorRefresh (
1601   VOID
1602   )
1603 {
1604   //
1605   // The Stall value is from experience. NOT from spec.  avoids 'flicker'
1606   //
1607   gBS->Stall (50);
1608 
1609   //
1610   // call the components refresh function
1611   //
1612   if (EditorFirst
1613     || StrCmp (FileBufferBackupVar.FileName, FileBuffer.FileName) != 0
1614     || FileBufferBackupVar.FileType != FileBuffer.FileType
1615     || FileBufferBackupVar.FileModified != FileBuffer.FileModified
1616     || FileBufferBackupVar.ReadOnly != FileBuffer.ReadOnly) {
1617 
1618     MainTitleBarRefresh (MainEditor.FileBuffer->FileName, MainEditor.FileBuffer->FileType, MainEditor.FileBuffer->ReadOnly, MainEditor.FileBuffer->FileModified, MainEditor.ScreenSize.Column, MainEditor.ScreenSize.Row, 0, 0);
1619     FileBufferRestorePosition ();
1620     FileBufferRefresh ();
1621   }
1622   if (EditorFirst
1623     || FileBufferBackupVar.FilePosition.Row != FileBuffer.FilePosition.Row
1624     || FileBufferBackupVar.FilePosition.Column != FileBuffer.FilePosition.Column
1625     || FileBufferBackupVar.ModeInsert != FileBuffer.ModeInsert
1626     || StatusBarGetRefresh()) {
1627 
1628     StatusBarRefresh (EditorFirst, MainEditor.ScreenSize.Row, MainEditor.ScreenSize.Column, MainEditor.FileBuffer->FilePosition.Row, MainEditor.FileBuffer->FilePosition.Column, MainEditor.FileBuffer->ModeInsert);
1629     FileBufferRestorePosition ();
1630     FileBufferRefresh ();
1631   }
1632 
1633   if (EditorFirst) {
1634     FileBufferRestorePosition ();
1635   }
1636 
1637   //
1638   // EditorFirst is now set to FALSE
1639   //
1640   EditorFirst = FALSE;
1641 }
1642 
1643 /**
1644   Get's the resultant location of the cursor based on the relative movement of the Mouse.
1645 
1646   @param[in] GuidX    The relative mouse movement.
1647 
1648   @return The X location of the mouse.
1649 **/
1650 INT32
1651 EFIAPI
GetTextX(IN INT32 GuidX)1652 GetTextX (
1653   IN INT32 GuidX
1654   )
1655 {
1656   INT32 Gap;
1657 
1658   MainEditor.MouseAccumulatorX += GuidX;
1659   Gap = (MainEditor.MouseAccumulatorX * (INT32) MainEditor.ScreenSize.Column) / (INT32) (50 * (INT32) MainEditor.MouseInterface->Mode->ResolutionX);
1660   MainEditor.MouseAccumulatorX = (MainEditor.MouseAccumulatorX * (INT32) MainEditor.ScreenSize.Column) % (INT32) (50 * (INT32) MainEditor.MouseInterface->Mode->ResolutionX);
1661   MainEditor.MouseAccumulatorX = MainEditor.MouseAccumulatorX / (INT32) MainEditor.ScreenSize.Column;
1662   return Gap;
1663 }
1664 
1665 /**
1666   Get's the resultant location of the cursor based on the relative movement of the Mouse.
1667 
1668   @param[in] GuidY    The relative mouse movement.
1669 
1670   @return The Y location of the mouse.
1671 **/
1672 INT32
1673 EFIAPI
GetTextY(IN INT32 GuidY)1674 GetTextY (
1675   IN INT32 GuidY
1676   )
1677 {
1678   INT32 Gap;
1679 
1680   MainEditor.MouseAccumulatorY += GuidY;
1681   Gap = (MainEditor.MouseAccumulatorY * (INT32) MainEditor.ScreenSize.Row) / (INT32) (50 * (INT32) MainEditor.MouseInterface->Mode->ResolutionY);
1682   MainEditor.MouseAccumulatorY = (MainEditor.MouseAccumulatorY * (INT32) MainEditor.ScreenSize.Row) % (INT32) (50 * (INT32) MainEditor.MouseInterface->Mode->ResolutionY);
1683   MainEditor.MouseAccumulatorY = MainEditor.MouseAccumulatorY / (INT32) MainEditor.ScreenSize.Row;
1684 
1685   return Gap;
1686 }
1687 
1688 /**
1689   Support mouse movement.  Move the cursor.
1690 
1691   @param[in] MouseState     The current mouse state.
1692 
1693   @retval EFI_SUCCESS       The operation was successful.
1694   @retval EFI_NOT_FOUND     There was no mouse support found.
1695 **/
1696 EFI_STATUS
1697 EFIAPI
MainEditorHandleMouseInput(IN EFI_SIMPLE_POINTER_STATE MouseState)1698 MainEditorHandleMouseInput (
1699   IN EFI_SIMPLE_POINTER_STATE       MouseState
1700   )
1701 {
1702 
1703   INT32           TextX;
1704   INT32           TextY;
1705   UINTN           FRow;
1706   UINTN           FCol;
1707 
1708   LIST_ENTRY  *Link;
1709   EFI_EDITOR_LINE *Line;
1710 
1711   UINTN           Index;
1712   BOOLEAN         Action;
1713 
1714   //
1715   // mouse action means:
1716   //    mouse movement
1717   //    mouse left button
1718   //
1719   Action = FALSE;
1720 
1721   //
1722   // have mouse movement
1723   //
1724   if (MouseState.RelativeMovementX || MouseState.RelativeMovementY) {
1725     //
1726     // handle
1727     //
1728     TextX = GetTextX (MouseState.RelativeMovementX);
1729     TextY = GetTextY (MouseState.RelativeMovementY);
1730 
1731     FileBufferAdjustMousePosition (TextX, TextY);
1732 
1733     Action = TRUE;
1734 
1735   }
1736 
1737   //
1738   // if left button pushed down
1739   //
1740   if (MouseState.LeftButton) {
1741 
1742     FCol = MainEditor.FileBuffer->MousePosition.Column - 1 + 1;
1743 
1744     FRow = MainEditor.FileBuffer->FilePosition.Row +
1745       MainEditor.FileBuffer->MousePosition.Row -
1746       MainEditor.FileBuffer->DisplayPosition.Row;
1747 
1748     //
1749     // beyond the file line length
1750     //
1751     if (MainEditor.FileBuffer->NumLines < FRow) {
1752       FRow = MainEditor.FileBuffer->NumLines;
1753     }
1754 
1755     Link = MainEditor.FileBuffer->ListHead->ForwardLink;
1756     for (Index = 0; Index < FRow - 1; Index++) {
1757       Link = Link->ForwardLink;
1758     }
1759 
1760     Line = CR (Link, EFI_EDITOR_LINE, Link, LINE_LIST_SIGNATURE);
1761 
1762     //
1763     // beyond the line's column length
1764     //
1765     if (FCol > Line->Size + 1) {
1766       FCol = Line->Size + 1;
1767     }
1768 
1769     FileBufferMovePosition (FRow, FCol);
1770 
1771     MainEditor.FileBuffer->MousePosition.Row    = MainEditor.FileBuffer->DisplayPosition.Row;
1772 
1773     MainEditor.FileBuffer->MousePosition.Column = MainEditor.FileBuffer->DisplayPosition.Column;
1774 
1775     Action = TRUE;
1776   }
1777   //
1778   // mouse has action
1779   //
1780   if (Action) {
1781     return EFI_SUCCESS;
1782   }
1783 
1784   //
1785   // no mouse action
1786   //
1787   return EFI_NOT_FOUND;
1788 }
1789 
1790 /**
1791   Handle user key input. This routes to other functions for the actions.
1792 
1793   @retval EFI_SUCCESS             The operation was successful.
1794   @retval EFI_LOAD_ERROR          A load error occured.
1795   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
1796 **/
1797 EFI_STATUS
1798 EFIAPI
MainEditorKeyInput(VOID)1799 MainEditorKeyInput (
1800   VOID
1801   )
1802 {
1803   EFI_INPUT_KEY             Key;
1804   EFI_STATUS                Status;
1805   EFI_SIMPLE_POINTER_STATE  MouseState;
1806 
1807   do {
1808 
1809     Status            = EFI_SUCCESS;
1810     EditorMouseAction = FALSE;
1811 
1812     //
1813     // backup some key elements, so that can aVOID some refresh work
1814     //
1815     MainEditorBackup ();
1816 
1817     //
1818     // change priority of checking mouse/keyboard activity dynamically
1819     // so prevent starvation of keyboard.
1820     // if last time, mouse moves then this time check keyboard
1821     //
1822     if (MainEditor.MouseSupported) {
1823       Status = MainEditor.MouseInterface->GetState (
1824                                             MainEditor.MouseInterface,
1825                                             &MouseState
1826                                             );
1827       if (!EFI_ERROR (Status)) {
1828 
1829         Status = MainEditorHandleMouseInput (MouseState);
1830 
1831         if (!EFI_ERROR (Status)) {
1832           EditorMouseAction           = TRUE;
1833           FileBufferMouseNeedRefresh  = TRUE;
1834         } else if (Status == EFI_LOAD_ERROR) {
1835           StatusBarSetStatusString (L"Invalid Mouse Movement ");
1836         }
1837       }
1838     }
1839 
1840     Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
1841     if (!EFI_ERROR (Status)) {
1842       //
1843       // dispatch to different components' key handling function
1844       // so not everywhere has to set this variable
1845       //
1846       FileBufferMouseNeedRefresh = TRUE;
1847       //
1848       // clear previous status string
1849       //
1850       StatusBarSetRefresh();
1851 
1852       //
1853       // dispatch to different components' key handling function
1854       //
1855       if (EFI_NOT_FOUND != MenuBarDispatchControlHotKey(&Key)) {
1856         Status = EFI_SUCCESS;
1857       } else if ((Key.ScanCode == SCAN_NULL) || ((Key.ScanCode >= SCAN_UP) && (Key.ScanCode <= SCAN_PAGE_DOWN))) {
1858         Status = FileBufferHandleInput (&Key);
1859       } else if ((Key.ScanCode >= SCAN_F1) && (Key.ScanCode <= SCAN_F12)) {
1860         Status = MenuBarDispatchFunctionKey (&Key);
1861       } else {
1862         StatusBarSetStatusString (L"Unknown Command");
1863         FileBufferMouseNeedRefresh = FALSE;
1864       }
1865 
1866       if (Status != EFI_SUCCESS && Status != EFI_OUT_OF_RESOURCES) {
1867         //
1868         // not already has some error status
1869         //
1870         if (StatusBarGetString() != NULL && StrCmp (L"", StatusBarGetString()) == 0) {
1871           StatusBarSetStatusString (L"Disk Error. Try Again");
1872         }
1873       }
1874 
1875     }
1876     //
1877     // after handling, refresh editor
1878     //
1879     MainEditorRefresh ();
1880 
1881   } while (Status != EFI_OUT_OF_RESOURCES && !EditorExit);
1882 
1883   return Status;
1884 }
1885 
1886 /**
1887   Set clipboard
1888 
1889   @param[in] Line   A pointer to the line to be set to clipboard
1890 
1891   @retval EFI_SUCCESS             The operation was successful.
1892   @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1893 **/
1894 EFI_STATUS
1895 EFIAPI
MainEditorSetCutLine(EFI_EDITOR_LINE * Line)1896 MainEditorSetCutLine (
1897   EFI_EDITOR_LINE *Line
1898   )
1899 {
1900   if (Line == NULL) {
1901     return EFI_SUCCESS;
1902   }
1903 
1904   if (MainEditor.CutLine != NULL) {
1905     //
1906     // free the old clipboard
1907     //
1908     LineFree (MainEditor.CutLine);
1909   }
1910   //
1911   // duplicate the line to clipboard
1912   //
1913   MainEditor.CutLine = LineDup (Line);
1914   if (MainEditor.CutLine == NULL) {
1915     return EFI_OUT_OF_RESOURCES;
1916   }
1917 
1918   return EFI_SUCCESS;
1919 }
1920 
1921 /**
1922   Backup function for MainEditor
1923 
1924   @retval EFI_SUCCESS The operation was successful.
1925 **/
1926 EFI_STATUS
1927 EFIAPI
MainEditorBackup(VOID)1928 MainEditorBackup (
1929   VOID
1930   )
1931 {
1932   FileBufferBackup ();
1933 
1934   return EFI_SUCCESS;
1935 }
1936