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