ec87f9c8bc4689f28cc6633b98867a06d1086425
[efi/shell/.git] / shellenv / conio.c
1 /*++
2
3 Copyright (c) 2005, Intel Corporation                                                         
4 All rights reserved. This program and the accompanying materials                          
5 are licensed and made available under the terms and conditions of the BSD License         
6 which accompanies this distribution. The full text of the license may be found at         
7 http://opensource.org/licenses/bsd-license.php                                            
8                                                                                           
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             
11
12 Module Name:
13
14   conio.c
15   
16 Abstract: 
17
18   Shell Environment driver
19
20 Revision History
21
22 --*/
23
24 #include "shelle.h"
25 #include "shellenvguid.h"
26
27 //
28 //
29 //
30 #define MAX_HISTORY           20
31
32 #define MODE_MIN_COLUMN       80
33 #define MODE_MIN_ROW          25
34
35 #define INPUT_LINE_SIGNATURE  EFI_SIGNATURE_32 ('i', 's', 'i', 'g')
36
37 //
38 // add this line to aVOID record keyboard history as memory leak
39 //
40 #undef AllocateZeroPool
41
42 typedef struct {
43   UINTN           Signature;
44   EFI_LIST_ENTRY  Link;
45   CHAR16          *Buffer;
46 } INPUT_LINE;
47
48 //
49 // Globals
50 //
51 STATIC BOOLEAN        SEnvInsertMode;
52 STATIC EFI_LIST_ENTRY SEnvLineHistory;
53 STATIC UINTN          SEnvNoHistory;
54
55 //
56 //
57 //
58 VOID
59 SEnvConIoInitDosKey (
60   VOID
61   )
62 /*++
63
64 Routine Description:
65
66 Arguments:
67
68 Returns:
69
70 --*/
71 {
72   InitializeListHead (&SEnvLineHistory);
73   SEnvInsertMode  = TRUE;
74   SEnvNoHistory   = 0;
75 }
76
77 EFI_STATUS
78 SEnvConIoOpen (
79   IN EFI_FILE                   *File,
80   OUT EFI_FILE                  **NewHandle,
81   IN CHAR16                     *FileName,
82   IN UINT64                     OpenMode,
83   IN UINT64                     Attributes
84   )
85 /*++
86
87 Routine Description:
88
89   Functions used to access the console interface via a file handle
90   Used if the console is not being redirected to a file
91
92 Arguments:
93
94   File       - The file
95   NewHandle  - The new handle
96   FileName   - The file name
97   OpenMode   - The open mode
98   Attributes - Attributes
99
100 Returns:
101
102   EFI_NOT_FOUND - Not found
103 --*/
104 {
105   return EFI_NOT_FOUND;
106 }
107
108 EFI_STATUS
109 SEnvConIoNop (
110   IN EFI_FILE  *File
111   )
112 /*++
113
114 Routine Description:
115
116 Arguments:
117
118   File - The file
119
120 Returns:
121
122   EFI_SUCCESS - Success
123
124 --*/
125 {
126   return EFI_SUCCESS;
127 }
128
129 EFI_STATUS
130 SEnvConIoGetPosition (
131   IN EFI_FILE                   *File,
132   OUT UINT64                    *Position
133   )
134 /*++
135
136 Routine Description:
137
138 Arguments:
139
140   File     - The file
141   Position - Position
142   
143 Returns:
144
145   EFI_UNSUPPORTED - Unsupported
146
147 --*/
148 {
149   return EFI_UNSUPPORTED;
150 }
151
152 EFI_STATUS
153 SEnvConIoSetPosition (
154   IN EFI_FILE                   *File,
155   OUT UINT64                    Position
156   )
157 /*++
158
159 Routine Description:
160
161 Arguments:
162
163   File     - The file
164   Position - THe position
165
166 Returns:
167
168   EFI_UNSUPPORTED - add return value to function comment
169
170 --*/
171 {
172   return EFI_UNSUPPORTED;
173 }
174
175 EFI_STATUS
176 SEnvConIoGetInfo (
177   IN EFI_FILE                   *File,
178   IN EFI_GUID                   *InformationType,
179   IN OUT UINTN                  *BufferSize,
180   OUT VOID                      *Buffer
181   )
182 /*++
183
184 Routine Description:
185
186 Arguments:
187
188   File            - The file
189   InformationType - The infomation type
190   BufferSize      - The buffer size
191   Buffer          - The buffer
192
193 Returns:
194
195   EFI_UNSUPPORTED - Unsupported
196   
197 --*/
198 {
199   return EFI_UNSUPPORTED;
200 }
201
202 EFI_STATUS
203 SEnvConIoSetInfo (
204   IN EFI_FILE                   *File,
205   IN EFI_GUID                   *InformationType,
206   IN UINTN                      BufferSize,
207   OUT VOID                      *Buffer
208   )
209 /*++
210
211 Routine Description:
212
213 Arguments:
214
215   File            - The file
216   InformationType - The infomation type
217   BufferSize      - The buffer size
218   Buffer          - The buffer
219
220 Returns:
221
222   EFI_UNSUPPORTED - Unsupported
223
224 --*/
225 {
226   return EFI_UNSUPPORTED;
227 }
228
229 EFI_STATUS
230 SEnvConIoWrite (
231   IN EFI_FILE                   *File,
232   IN OUT UINTN                  *BufferSize,
233   IN VOID                       *Buffer
234   )
235 /*++
236
237 Routine Description:
238
239 Arguments:
240
241   File       - The file
242   BufferSize - The buffer size
243   Buffer     - The buffer
244
245 Returns:
246   EFI_SUCCESS - Success
247
248 --*/
249 {
250   PrintToken (STRING_TOKEN (STR_SHELLENV_CONIO_ONE_VAR_S_PTR), HiiEnvHandle, *BufferSize, Buffer);
251   return EFI_SUCCESS;
252 }
253
254 EFI_STATUS
255 SEnvErrIoWrite (
256   IN EFI_FILE                   *File,
257   IN OUT UINTN                  *BufferSize,
258   IN VOID                       *Buffer
259   )
260 /*++
261
262 Routine Description:
263
264 Arguments:
265
266   File       - The file
267   BufferSize - The buffer size
268   Buffer     - The buffer
269
270 Returns:
271   EFI_SUCCESS - Success
272
273 --*/
274 {
275   IPrint (ST->StdErr, L"%.*s", *BufferSize, Buffer);
276   return EFI_SUCCESS;
277 }
278
279 EFI_STATUS
280 SEnvErrIoRead (
281   IN EFI_FILE                   *File,
282   IN OUT UINTN                  *BufferSize,
283   IN VOID                       *Buffer
284   )
285 /*++
286
287 Routine Description:
288
289 Arguments:
290
291   File       - The file
292   BufferSize - The buffer size
293   Buffer     - The buffer
294
295 Returns:
296   EFI_UNSUPPORTED - Unsupported
297
298 --*/
299 {
300   return EFI_UNSUPPORTED;
301 }
302
303 VOID
304 SEnvPrintHistory (
305   VOID
306   )
307 /*++
308
309 Routine Description:
310
311 Arguments:
312
313 Returns:
314
315 --*/
316 {
317   EFI_LIST_ENTRY  *Link;
318   INPUT_LINE      *Line;
319   UINTN           Index;
320   UINTN           LineNumber;
321   UINTN           StartColumn;
322   UINTN           LineLength;
323   UINTN           TotalRow;
324   UINTN           LineCount;
325   CHAR16          InputStr[1];
326
327   Print (L"\n");
328   Index       = 0;
329   LineNumber  = 0;
330   StartColumn = 4;
331   ST->ConOut->QueryMode (
332                 ST->ConOut,
333                 ST->ConOut->Mode->Mode,
334                 &LineLength,
335                 &TotalRow
336                 );
337
338   //
339   // Print history
340   //
341   for (Link = SEnvLineHistory.Flink; Link != &SEnvLineHistory; Link = Link->Flink) {
342     Index += 1;
343     Line      = CR (Link, INPUT_LINE, Link, INPUT_LINE_SIGNATURE);
344     LineCount = (StrLen (Line->Buffer) + StartColumn + 1) / LineLength + 1;
345
346     if (LineNumber + LineCount >= TotalRow) {
347       PrintToken (STRING_TOKEN (STR_SHELLENV_CONIO_ENTER_CONTINUE), HiiEnvHandle);
348       Input (NULL, InputStr, 1);
349       Print (L"\n");
350       LineNumber = 0;
351     }
352
353     PrintToken (STRING_TOKEN (STR_SHELLENV_CONIO_TWO_VARS_D_S), HiiEnvHandle, Index, Line->Buffer);
354     LineNumber += LineCount;
355   }
356 }
357
358 VOID
359 MoveCursorBackward (
360   IN     UINTN                   LineLength,
361   IN OUT UINTN                   *Column,
362   IN OUT UINTN                   *Row
363   )
364 /*++
365
366 Routine Description:
367   Move the cursor position one character backward.
368
369 Arguments:
370   LineLength       Length of a line. Get it by calling QueryMode
371   Column           Current column of the cursor position
372   Row              Current row of the cursor position
373
374 Returns:
375
376 --*/
377 {
378   //
379   // If current column is 0, move to the last column of the previous line,
380   // otherwise, just decrement column.
381   //
382   if (*Column == 0) {
383     (*Column) = LineLength - 1;
384     if (*Row > 0) {
385       (*Row)--;
386     }
387   } else {
388     (*Column)--;
389   }
390 }
391
392 VOID
393 MoveCursorForward (
394   IN     UINTN                   LineLength,
395   IN     UINTN                   TotalRow,
396   IN OUT UINTN                   *Column,
397   IN OUT UINTN                   *Row
398   )
399 /*++
400
401 Routine Description:
402   Move the cursor position one character backward.
403
404 Arguments:
405   LineLength       Length of a line. Get it by calling QueryMode
406   TotalRow         Total row of a screen, get by calling QueryMode
407   Column           Current column of the cursor position
408   Row              Current row of the cursor position
409
410 Returns:
411
412 --*/
413 {
414   //
415   // If current column is at line end, move to the first column of the nest
416   // line, otherwise, just increment column.
417   //
418   (*Column)++;
419   if (*Column >= LineLength) {
420     (*Column) = 0;
421     if ((*Row) < TotalRow - 1) {
422       (*Row)++;
423     }
424   }
425 }
426
427 EFI_STATUS
428 SEnvConIoRead (
429   IN EFI_FILE                       *File,
430   IN OUT UINTN                      *BufferSize,
431   IN VOID                           *Buffer
432   )
433 /*++
434
435 Routine Description:
436   Get a line from the console.
437
438 Arguments:
439   File             File handle, actually not used in this function
440   BufferSize       Size of the buffer
441   Buffer           Buffer to hold the line from console
442
443 Returns:
444   EFI_SUCCESS      The function finished successfully
445   EFI_OUT_OF_RESOURCES Out of resources
446   
447 --*/
448 {
449   CHAR16                        *Str;
450   BOOLEAN                       Done;
451   UINTN                         Column;
452   UINTN                         Row;
453   UINTN                         StartColumn;
454   UINTN                         Update;
455   UINTN                         Delete;
456   UINTN                         Len;
457   UINTN                         StrPos;
458   UINTN                         MaxStr;
459   UINTN                         Index;
460   UINTN                         LineLength;
461   UINTN                         TotalRow;
462   UINTN                         SkipLength;
463   UINTN                         OutputLength;
464   UINTN                         TailRow;
465   UINTN                         TailColumn;
466   EFI_INPUT_KEY                 Key;
467   EFI_SIMPLE_TEXT_OUT_PROTOCOL  *ConOut;
468   EFI_SIMPLE_TEXT_IN_PROTOCOL   *ConIn;
469   INPUT_LINE                    *NewLine;
470   INPUT_LINE                    *LineCmd;
471   EFI_LIST_ENTRY                *LinePos;
472   EFI_LIST_ENTRY                *NewPos;
473   BOOLEAN                       InScrolling;
474
475   ConOut            = ST->ConOut;
476   ConIn             = ST->ConIn;
477   Str               = Buffer;
478   Len               = 0;
479   StrPos            = 0;
480   OutputLength      = 0;
481   Update            = 0;
482   Delete            = 0;
483   LinePos           = NewPos = &SEnvLineHistory;
484
485   InScrolling       = FALSE;
486
487   //
488   // If buffer is not large enough to hold a CHAR16, do nothing.
489   //
490   if (*BufferSize < sizeof (CHAR16) * 2) {
491     *BufferSize = 0;
492     return EFI_SUCCESS;
493   }
494   //
495   // Get the screen setting and the current cursor location
496   //
497   StartColumn = ConOut->Mode->CursorColumn;
498   Column      = StartColumn;
499   Row         = ConOut->Mode->CursorRow;
500   ConOut->QueryMode (ConOut, ConOut->Mode->Mode, &LineLength, &TotalRow);
501
502   //
503   // Limit the line length to the buffer size or the minimun size of the
504   // screen. (The smaller takes effect)
505   //
506   MaxStr = LineLength * (TotalRow - 1) - StartColumn;
507   if (MaxStr > *BufferSize / sizeof (CHAR16)) {
508     MaxStr = *BufferSize / sizeof (CHAR16);
509   }
510
511   ZeroMem (Str, MaxStr * sizeof (CHAR16));
512   Done = FALSE;
513   do {
514     //
515     // Read a key
516     //
517     WaitForSingleEvent (ConIn->WaitForKey, 0);
518     ConIn->ReadKeyStroke (ConIn, &Key);
519
520     //
521     // Press PageUp or PageDown to scroll the history screen up or down.
522     // Press any other key to quit scrolling.
523     //
524     if (Key.UnicodeChar == 0 && (Key.ScanCode == SCAN_PAGE_UP || Key.ScanCode == SCAN_PAGE_DOWN)) {
525       if (Key.ScanCode == SCAN_PAGE_UP) {
526         SEnvConOutHistoryScrollBack (NULL);
527       } else if (Key.ScanCode == SCAN_PAGE_DOWN) {
528         SEnvConOutHistoryScrollForward (NULL);
529       }
530
531       InScrolling = TRUE;
532     } else {
533       if (InScrolling) {
534         SEnvConOutHistoryQuitScroll ();
535         InScrolling = FALSE;
536       }
537     }
538
539     switch (Key.UnicodeChar) {
540     case CHAR_CARRIAGE_RETURN:
541       //
542       // All done, print a newline at the end of the string
543       //
544       TailRow     = Row + (Len - StrPos + Column) / LineLength;
545       TailColumn  = (Len - StrPos + Column) % LineLength;
546       PrintAt (TailColumn, TailRow, L"%N\n");
547       Done = TRUE;
548       break;
549
550     case CHAR_BACKSPACE:
551       if (StrPos) {
552         //
553         // If not move back beyond string beginning, move all characters behind
554         // the current position one character forward
555         //
556         StrPos -= 1;
557         Update  = StrPos;
558         Delete  = 1;
559         CopyMem (Str + StrPos, Str + StrPos + 1, sizeof (CHAR16) * (Len - StrPos));
560
561         //
562         // Adjust the current column and row
563         //
564         MoveCursorBackward (LineLength, &Column, &Row);
565       }
566       break;
567
568     default:
569       if (Key.UnicodeChar >= ' ') {
570         //
571         // If we are at the buffer's end, drop the key
572         //
573         if (Len == MaxStr - 1 && (SEnvInsertMode || StrPos == Len)) {
574           break;
575         }
576         //
577         // If in insert mode, move all characters behind the current position
578         // one character backward to make space for this character. Then store
579         // the character.
580         //
581         if (SEnvInsertMode) {
582           for (Index = Len; Index > StrPos; Index -= 1) {
583             Str[Index] = Str[Index - 1];
584           }
585         }
586
587         Str[StrPos] = Key.UnicodeChar;
588         Update      = StrPos;
589
590         StrPos += 1;
591         OutputLength = 1;
592       }
593       break;
594
595     case 0:
596       switch (Key.ScanCode) {
597       case SCAN_DELETE:
598         //
599         // Move characters behind current position one character forward
600         //
601         if (Len) {
602           Update  = StrPos;
603           Delete  = 1;
604           CopyMem (Str + StrPos, Str + StrPos + 1, sizeof (CHAR16) * (Len - StrPos));
605         }
606         break;
607
608       case SCAN_UP:
609         //
610         // Prepare to print the previous command
611         //
612         NewPos = LinePos->Blink;
613         if (NewPos == &SEnvLineHistory) {
614           NewPos = NewPos->Blink;
615         }
616         break;
617
618       case SCAN_DOWN:
619         //
620         // Prepare to print the next command
621         //
622         NewPos = LinePos->Flink;
623         if (NewPos == &SEnvLineHistory) {
624           NewPos = NewPos->Flink;
625         }
626         break;
627
628       case SCAN_LEFT:
629         //
630         // Adjust current cursor position
631         //
632         if (StrPos) {
633           StrPos -= 1;
634           MoveCursorBackward (LineLength, &Column, &Row);
635         }
636         break;
637
638       case SCAN_RIGHT:
639         //
640         // Adjust current cursor position
641         //
642         if (StrPos < Len) {
643           StrPos += 1;
644           MoveCursorForward (LineLength, TotalRow, &Column, &Row);
645         }
646         break;
647
648       case SCAN_HOME:
649         //
650         // Move current cursor position to the beginning of the command line
651         //
652         Row -= (StrPos + StartColumn) / LineLength;
653         Column  = StartColumn;
654         StrPos  = 0;
655         break;
656
657       case SCAN_END:
658         //
659         // Move current cursor position to the end of the command line
660         //
661         TailRow     = Row + (Len - StrPos + Column) / LineLength;
662         TailColumn  = (Len - StrPos + Column) % LineLength;
663         Row         = TailRow;
664         Column      = TailColumn;
665         StrPos      = Len;
666         break;
667
668       case SCAN_ESC:
669         //
670         // Prepare to clear the current command line
671         //
672         Str[0]  = 0;
673         Update  = 0;
674         Delete  = Len;
675         Row -= (StrPos + StartColumn) / LineLength;
676         Column        = StartColumn;
677         OutputLength  = 0;
678         break;
679
680       case SCAN_INSERT:
681         //
682         // Toggle the SEnvInsertMode flag
683         //
684         SEnvInsertMode = (BOOLEAN)!SEnvInsertMode;
685         break;
686
687       case SCAN_F7:
688         //
689         // Print command history
690         //
691         SEnvPrintHistory ();
692         *Str  = 0;
693         Done  = TRUE;
694         break;
695       }
696     }
697
698     if (Done) {
699       break;
700     }
701     //
702     // If we have a new position, we are preparing to print a previous or next
703     // command.
704     //
705     if (NewPos != &SEnvLineHistory) {
706       Column = StartColumn;
707       Row -= (StrPos + StartColumn) / LineLength;
708
709       LineCmd       = CR (NewPos, INPUT_LINE, Link, INPUT_LINE_SIGNATURE);
710       LinePos       = NewPos;
711       NewPos        = &SEnvLineHistory;
712
713       OutputLength  = StrLen (LineCmd->Buffer) < MaxStr - 1 ? StrLen (LineCmd->Buffer) : MaxStr - 1;
714       CopyMem (Str, LineCmd->Buffer, OutputLength * sizeof (CHAR16));
715       Str[OutputLength] = 0;
716
717       StrPos            = OutputLength;
718
719       //
720       // Draw new input string
721       //
722       Update = 0;
723       if (Len > OutputLength) {
724         //
725         // If old string was longer, blank its tail
726         //
727         Delete = Len - OutputLength;
728       }
729     }
730     //
731     // If we need to update the output do so now
732     //
733     if (Update != -1) {
734       PrintAt (Column, Row, L"%s%.*s", Str + Update, Delete, L"");
735       Len = StrLen (Str);
736
737       if (Delete) {
738         SetMem (Str + Len, Delete * sizeof (CHAR16), 0x00);
739       }
740
741       if (StrPos > Len) {
742         StrPos = Len;
743       }
744
745       Update = (UINTN) -1;
746
747       //
748       // After using print to reflect newly updates, if we're not using
749       // BACKSPACE and DELETE, we need to move the cursor position forward,
750       // so adjust row and column here.
751       //
752       if (Key.UnicodeChar != CHAR_BACKSPACE && !(Key.UnicodeChar == 0 && Key.ScanCode == SCAN_DELETE)) {
753         //
754         // Calulate row and column of the tail of current string
755         //
756         TailRow     = Row + (Len - StrPos + Column + OutputLength) / LineLength;
757         TailColumn  = (Len - StrPos + Column + OutputLength) % LineLength;
758
759         //
760         // If the tail of string reaches screen end, screen rolls up, so if
761         // Row does not equal TailRow, Row should be decremented
762         //
763         // (if we are recalling commands using UPPER and DOWN key, and if the
764         // old command is too long to fit the screen, TailColumn must be 79.
765         //
766         if (TailColumn == 0 && TailRow >= TotalRow && Row != TailRow) {
767           Row--;
768         }
769         //
770         // Calculate the cursor position after current operation. If cursor
771         // reaches line end, update both row and column, otherwise, only
772         // column will be changed.
773         //
774         if (Column + OutputLength >= LineLength) {
775           SkipLength = OutputLength - (LineLength - Column);
776
777           Row += SkipLength / LineLength + 1;
778           if (Row > TotalRow - 1) {
779             Row = TotalRow - 1;
780           }
781
782           Column = SkipLength % LineLength;
783         } else {
784           Column += OutputLength;
785         }
786       }
787
788       Delete = 0;
789     }
790     //
791     // Set the cursor position for this key
792     //
793     ConOut->SetCursorPosition (ConOut, Column, Row);
794   } while (!Done);
795
796   //
797   // Copy the line to the history buffer
798   //
799   if (Str[0]) {
800     //
801     // Allocate and initalize a new key entry for command history
802     //
803     NewLine = AllocateZeroPool (sizeof (INPUT_LINE));
804     if (!NewLine) {
805       return EFI_OUT_OF_RESOURCES;
806     }
807
808     NewLine->Signature  = INPUT_LINE_SIGNATURE;
809     NewLine->Buffer     = AllocateZeroPool ((Len + 1) * sizeof (CHAR16));
810     if (NewLine->Buffer == NULL) {
811       FreePool (NewLine);
812       return EFI_OUT_OF_RESOURCES;
813     }
814
815     StrCpy (NewLine->Buffer, Str);
816     InsertTailList (&SEnvLineHistory, &NewLine->Link);
817     SEnvNoHistory++;
818   }
819   //
820   // If there's too much in the history buffer free an entry
821   //
822   if (SEnvNoHistory > MAX_HISTORY) {
823     LineCmd = CR (
824                 SEnvLineHistory.Flink,
825                 INPUT_LINE,
826                 Link,
827                 INPUT_LINE_SIGNATURE
828                 );
829     RemoveEntryList (&LineCmd->Link);
830     SEnvNoHistory--;
831     if (LineCmd->Buffer) {
832       FreePool (LineCmd->Buffer);
833     }
834
835     FreePool (LineCmd);
836   }
837   //
838   // Return the data to the caller
839   //
840   *BufferSize = Len * sizeof (CHAR16);
841   return EFI_SUCCESS;
842 }
843 //
844 //
845 //
846 EFI_STATUS
847 SEnvReset (
848   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL     *This,
849   IN BOOLEAN                          ExtendedVerification
850   )
851 /*++
852
853 Routine Description:
854
855 Arguments:
856
857    This - This protocol
858    ExtendedVerification - To extended verification
859
860 Returns:
861   EFI_SUCCESS Success
862
863 --*/
864 {
865   return EFI_SUCCESS;
866 }
867
868 EFI_STATUS
869 SEnvOutputString (
870   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This,
871   IN CHAR16                       *String
872   )
873 /*++
874
875 Routine Description:
876
877 Arguments:
878
879   This - This protocol
880   String - The string
881
882 Returns:
883
884 --*/
885 {
886   EFI_STATUS                    Status;
887   ENV_SHELL_REDIR_FILE          *Redir;
888   UINTN                         Len;
889   UINTN                         Size;
890   UINTN                         WriteSize;
891   UINTN                         Index;
892   UINTN                         Start;
893   CHAR8                         Buffer[100];
894   CHAR16                        UnicodeBuffer[100];
895   BOOLEAN                       InvalidChar;
896
897   EFI_SIMPLE_TEXT_IN_PROTOCOL   *TextIn;
898   EFI_SIMPLE_TEXT_OUT_PROTOCOL  *TextOut;
899
900   TextIn  = NULL;
901   TextOut = NULL;
902
903   //
904   // Get Shell redirection device
905   //
906   Redir = CR (This, ENV_SHELL_REDIR_FILE, Out, ENV_REDIR_SIGNATURE);
907   if (EFI_ERROR (Redir->WriteError)) {
908     return Redir->WriteError;
909   }
910
911   Status      = EFI_SUCCESS;
912   InvalidChar = FALSE;
913
914   if (Redir->Ascii) {
915     //
916     // Handle for ASCII
917     //
918     Start = 0;
919     Len   = StrLen (String);
920     while (Len) {
921       Size = Len > sizeof (Buffer) ? sizeof (Buffer) : Len;
922       for (Index = 0; Index < Size; Index += 1) {
923         if (String[Start + Index] > 0xff) {
924           Buffer[Index] = '_';
925           InvalidChar   = TRUE;
926         } else {
927           Buffer[Index] = (CHAR8) String[Start + Index];
928         }
929       }
930
931       WriteSize = Size;
932       if (WriteSize > 0) {
933         Status = Redir->File->Write (Redir->File, &WriteSize, Buffer);
934         if (EFI_ERROR (Status)) {
935           break;
936         }
937       }
938
939       Len -= Size;
940       Start += Size;
941     }
942
943   } else {
944     //
945     // Unicode
946     //
947     Len = StrSize (String) - sizeof (CHAR16);
948     if (Len > 0) {
949       Status = Redir->File->Write (Redir->File, &Len, String);
950     }
951   }
952   //
953   // Error check
954   //
955   if (EFI_ERROR (Status)) {
956     Redir->WriteError = Status;
957     SEnvBatchGetConsole (&TextIn, &TextOut);
958     SPrint (UnicodeBuffer, 100, L"write error: %r\n\r", Status);
959     Status = TextOut->OutputString (TextOut, UnicodeBuffer);
960   }
961
962   if (InvalidChar && !EFI_ERROR (Status)) {
963     Status = EFI_WARN_UNKNOWN_GLYPH;
964   }
965
966   return Status;
967 }
968
969 EFI_STATUS
970 SEnvTestString (
971   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This,
972   IN CHAR16                       *String
973   )
974 /*++
975
976 Routine Description:
977
978 Arguments:
979
980   This - This protocol
981   String - The string
982
983 Returns:
984
985 --*/
986 {
987   EFI_STATUS            Status;
988   ENV_SHELL_REDIR_FILE  *Redir;
989
990   Redir   = CR (This, ENV_SHELL_REDIR_FILE, Out, ENV_REDIR_SIGNATURE);
991   Status  = ST->ConOut->TestString (ST->ConOut, String);
992
993   //
994   // Test String
995   //
996   if (!EFI_ERROR (Status) && Redir->Ascii) {
997     while (*String && *String < 0x100) {
998       String += 1;
999     }
1000
1001     if (*String > 0xff) {
1002       Status = EFI_UNSUPPORTED;
1003     }
1004   }
1005
1006   return Status;
1007 }
1008
1009 EFI_STATUS
1010 SEnvQueryMode (
1011   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This,
1012   IN UINTN                        ModeNumber,
1013   OUT UINTN                       *Columns,
1014   OUT UINTN                       *Rows
1015   )
1016 /*++
1017
1018 Routine Description:
1019
1020 Arguments:
1021
1022   This       - This       
1023   ModeNumber - ModeNumber 
1024   Columns    - Columns    
1025   Rows       - Rows       
1026
1027 Returns:
1028   EFI_INVALID_PARAMETER - Invalid parameter
1029   EFI_SUCCESS - Success
1030
1031 --*/
1032 {
1033   if (ModeNumber > 0) {
1034     return EFI_INVALID_PARAMETER;
1035   }
1036
1037   *Columns  = 0;
1038   *Rows     = 0;
1039   return EFI_SUCCESS;
1040 }
1041
1042 EFI_STATUS
1043 SEnvSetMode (
1044   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This,
1045   IN UINTN                        ModeNumber
1046   )
1047 /*++
1048
1049 Routine Description:
1050
1051 Arguments:
1052
1053   This - This protocol
1054   ModeNumber - ModeNumber
1055
1056 Returns:
1057
1058 --*/
1059 {
1060   return ModeNumber > 0 ? EFI_INVALID_PARAMETER : EFI_SUCCESS;
1061 }
1062
1063 EFI_STATUS
1064 SEnvSetAttribute (
1065   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL     *This,
1066   IN UINTN                            Attribute
1067   )
1068 /*++
1069
1070 Routine Description:
1071
1072 Arguments:
1073
1074   This - This protocol
1075   Attribute - The attribute
1076
1077 Returns:
1078
1079   EFI_SUCCESS - Success
1080
1081 --*/
1082 {
1083   This->Mode->Attribute = (UINT32) Attribute;
1084   return EFI_SUCCESS;
1085 }
1086
1087 EFI_STATUS
1088 SEnvClearScreen (
1089   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This
1090   )
1091 /*++
1092
1093 Routine Description:
1094
1095 Arguments:
1096
1097   This - This protocol
1098
1099 Returns:
1100
1101   EFI_SUCCESS - Success
1102   
1103 --*/
1104 {
1105   return EFI_SUCCESS;
1106 }
1107
1108 EFI_STATUS
1109 SEnvSetCursorPosition (
1110   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This,
1111   IN UINTN                        Column,
1112   IN UINTN                        Row
1113   )
1114 /*++
1115
1116 Routine Description:
1117
1118 Arguments:
1119
1120   This   - This protocol
1121   Column - The column
1122   Row    - The row
1123
1124 Returns:
1125
1126   EFI_UNSUPPORTED - Unsupported
1127
1128 --*/
1129 {
1130   return EFI_UNSUPPORTED;
1131 }
1132
1133 EFI_STATUS
1134 SEnvEnableCursor (
1135   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This,
1136   IN BOOLEAN                      Enable
1137   )
1138 /*++
1139
1140 Routine Description:
1141
1142 Arguments:
1143
1144   This - This protcol
1145   Enable - Enable the cursor
1146   
1147 Returns:
1148
1149   EFI_SUCCESS - Success
1150   
1151 --*/
1152 {
1153   This->Mode->CursorVisible = Enable;
1154   return EFI_SUCCESS;
1155 }
1156
1157 EFI_STATUS
1158 SEnvDummyReset (
1159   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL     *This,
1160   IN BOOLEAN                          ExtendedVerification
1161   )
1162 /*++
1163
1164 Routine Description:
1165
1166 Arguments:
1167
1168   This - This protocol
1169   ExtendedVerification - Extended verification
1170
1171 Returns:
1172
1173   EFI_SUCCESS - Success
1174   
1175 --*/
1176 {
1177   return EFI_SUCCESS;
1178 }
1179
1180 EFI_STATUS
1181 SEnvDummyOutputString (
1182   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This,
1183   IN CHAR16                       *String
1184   )
1185 /*++
1186
1187 Routine Description:
1188
1189 Arguments:
1190
1191   This - This protocol
1192   String - The string
1193
1194 Returns:
1195
1196   EFI_SUCCESS  - Success
1197 --*/
1198 {
1199   return EFI_SUCCESS;
1200 }
1201
1202 EFI_STATUS
1203 SEnvDummyTestString (
1204   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This,
1205   IN CHAR16                       *String
1206   )
1207 /*++
1208
1209 Routine Description:
1210
1211 Arguments:
1212
1213   This - This protocol
1214   String - The string
1215
1216 Returns:
1217
1218   EFI_SUCCESS  - Success
1219 --*/
1220 {
1221   return EFI_SUCCESS;
1222 }
1223
1224 EFI_STATUS
1225 SEnvDummyQueryMode (
1226   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This,
1227   IN UINTN                        ModeNumber,
1228   OUT UINTN                       *Columns,
1229   OUT UINTN                       *Rows
1230   )
1231 /*++
1232
1233 Routine Description:
1234
1235 Arguments:
1236
1237   This       - This protocol
1238   ModeNumber - The mode number
1239   Columns    - The Columns
1240   Rows       - The Rows
1241
1242 Returns:
1243
1244   EFI_SUCCESS  - Success
1245 --*/
1246 {
1247   return EFI_SUCCESS;
1248 }
1249
1250 EFI_STATUS
1251 SEnvDummySetMode (
1252   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This,
1253   IN UINTN                        ModeNumber
1254   )
1255 /*++
1256
1257 Routine Description:
1258
1259 Arguments:
1260   
1261   This - This protocol
1262   ModeNumber - Mode number
1263   
1264 Returns:
1265
1266   EFI_SUCCESS - Success
1267   
1268 --*/
1269 {
1270   return EFI_SUCCESS;
1271 }
1272
1273 EFI_STATUS
1274 SEnvDummySetAttribute (
1275   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL     *This,
1276   IN UINTN                            Attribute
1277   )
1278 /*++
1279
1280 Routine Description:
1281
1282 Arguments:
1283
1284   This - This protocol
1285   Attribute - The attribute
1286
1287 Returns:
1288
1289   EFI_SUCCESS  - Success
1290
1291 --*/
1292 {
1293   return EFI_SUCCESS;
1294 }
1295
1296 EFI_STATUS
1297 SEnvDummyClearScreen (
1298   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This
1299   )
1300 /*++
1301
1302 Routine Description:
1303
1304 Arguments:
1305
1306   This - This protocol
1307
1308 Returns:
1309
1310   EFI_SUCCESS - Success
1311
1312 --*/
1313 {
1314   return EFI_SUCCESS;
1315 }
1316
1317 EFI_STATUS
1318 SEnvDummySetCursorPosition (
1319   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This,
1320   IN UINTN                        Column,
1321   IN UINTN                        Row
1322   )
1323 /*++
1324
1325 Routine Description:
1326
1327 Arguments:
1328
1329   This   - This protocol
1330   Column - The column
1331   Row    - The row
1332
1333 Returns:
1334
1335   EFI_UNSUPPORTED  Unsupported
1336
1337 --*/
1338 {
1339   return EFI_UNSUPPORTED;
1340 }
1341
1342 EFI_STATUS
1343 SEnvDummyEnableCursor (
1344   IN EFI_SIMPLE_TEXT_OUT_PROTOCOL *This,
1345   IN BOOLEAN                      Enable
1346   )
1347 /*++
1348
1349 Routine Description:
1350
1351 Arguments:
1352
1353   This - This protocol
1354   Enable - Enable the Cursor
1355
1356 Returns:
1357
1358   EFI_SUCCESS Success
1359
1360 --*/
1361 {
1362   return EFI_SUCCESS;
1363 }