]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Application/Shell/ConsoleLogger.c
fix for word wrapping.
[mirror_edk2.git] / ShellPkg / Application / Shell / ConsoleLogger.c
1 /** @file
2 Provides interface to shell console logger.
3
4 Copyright (c) 2009 - 2011, 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 #include "ConsoleLogger.h"
15 #include "Shell.h"
16
17 STATIC CONST CHAR16 mCrLfString[3] = { CHAR_CARRIAGE_RETURN, CHAR_LINEFEED, CHAR_NULL };
18
19 /**
20 Install our intermediate ConOut into the system table to
21 keep a log of all the info that is displayed to the user.
22
23 @param[in] ScreensToSave Sets how many screen-worths of data to save.
24 @param[out] ConsoleInfo The object to pass into later functions.
25
26 @retval EFI_SUCCESS The operation was successful.
27 @return other The operation failed.
28
29 @sa ConsoleLoggerResetBuffers
30 @sa InstallProtocolInterface
31 **/
32 EFI_STATUS
33 EFIAPI
34 ConsoleLoggerInstall(
35 IN CONST UINTN ScreensToSave,
36 OUT CONSOLE_LOGGER_PRIVATE_DATA **ConsoleInfo
37 )
38 {
39 EFI_STATUS Status;
40 ASSERT(ConsoleInfo != NULL);
41
42 (*ConsoleInfo) = AllocateZeroPool(sizeof(CONSOLE_LOGGER_PRIVATE_DATA));
43 if ((*ConsoleInfo) == NULL) {
44 return (EFI_OUT_OF_RESOURCES);
45 }
46
47 (*ConsoleInfo)->Signature = CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE;
48 (*ConsoleInfo)->OldConOut = gST->ConOut;
49 (*ConsoleInfo)->OldConHandle = gST->ConsoleOutHandle;
50 (*ConsoleInfo)->Buffer = NULL;
51 (*ConsoleInfo)->BufferSize = 0;
52 (*ConsoleInfo)->OriginalStartRow = 0;
53 (*ConsoleInfo)->CurrentStartRow = 0;
54 (*ConsoleInfo)->RowsPerScreen = 0;
55 (*ConsoleInfo)->ColsPerScreen = 0;
56 (*ConsoleInfo)->Attributes = NULL;
57 (*ConsoleInfo)->AttribSize = 0;
58 (*ConsoleInfo)->ScreenCount = ScreensToSave;
59 (*ConsoleInfo)->HistoryMode.MaxMode = 1;
60 (*ConsoleInfo)->HistoryMode.Mode = 0;
61 (*ConsoleInfo)->HistoryMode.Attribute = 0;
62 (*ConsoleInfo)->HistoryMode.CursorColumn = 0;
63 (*ConsoleInfo)->HistoryMode.CursorRow = 0;
64 (*ConsoleInfo)->HistoryMode.CursorVisible = FALSE;
65 (*ConsoleInfo)->OurConOut.Reset = ConsoleLoggerReset;
66 (*ConsoleInfo)->OurConOut.OutputString = ConsoleLoggerOutputString;
67 (*ConsoleInfo)->OurConOut.TestString = ConsoleLoggerTestString;
68 (*ConsoleInfo)->OurConOut.QueryMode = ConsoleLoggerQueryMode;
69 (*ConsoleInfo)->OurConOut.SetMode = ConsoleLoggerSetMode;
70 (*ConsoleInfo)->OurConOut.SetAttribute = ConsoleLoggerSetAttribute;
71 (*ConsoleInfo)->OurConOut.ClearScreen = ConsoleLoggerClearScreen;
72 (*ConsoleInfo)->OurConOut.SetCursorPosition = ConsoleLoggerSetCursorPosition;
73 (*ConsoleInfo)->OurConOut.EnableCursor = ConsoleLoggerEnableCursor;
74 (*ConsoleInfo)->OurConOut.Mode = gST->ConOut->Mode;
75 (*ConsoleInfo)->Enabled = TRUE;
76
77 Status = ConsoleLoggerResetBuffers(*ConsoleInfo);
78 if (EFI_ERROR(Status)) {
79 SHELL_FREE_NON_NULL((*ConsoleInfo));
80 *ConsoleInfo = NULL;
81 return (Status);
82 }
83
84 Status = gBS->InstallProtocolInterface(&gImageHandle, &gEfiSimpleTextOutProtocolGuid, EFI_NATIVE_INTERFACE, (VOID*)&((*ConsoleInfo)->OurConOut));
85 if (EFI_ERROR(Status)) {
86 SHELL_FREE_NON_NULL((*ConsoleInfo)->Buffer);
87 SHELL_FREE_NON_NULL((*ConsoleInfo)->Attributes);
88 SHELL_FREE_NON_NULL((*ConsoleInfo));
89 *ConsoleInfo = NULL;
90 return (Status);
91 }
92
93 gST->ConsoleOutHandle = gImageHandle;
94 gST->ConOut = &(*ConsoleInfo)->OurConOut;
95
96 return (Status);
97 }
98
99 /**
100 Return the system to the state it was before InstallConsoleLogger
101 was installed.
102
103 @param[in] ConsoleInfo The object from the install function.
104
105 @retval EFI_SUCCESS The operation was successful
106 @return other The operation failed. This was from UninstallProtocolInterface.
107 **/
108 EFI_STATUS
109 EFIAPI
110 ConsoleLoggerUninstall(
111 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
112 )
113 {
114 ASSERT(ConsoleInfo != NULL);
115 ASSERT(ConsoleInfo->OldConOut != NULL);
116
117 if (ConsoleInfo->Buffer != NULL) {
118 FreePool(ConsoleInfo->Buffer);
119 DEBUG_CODE(ConsoleInfo->Buffer = NULL;);
120 DEBUG_CODE(ConsoleInfo->BufferSize = 0;);
121 }
122 if (ConsoleInfo->Attributes != NULL) {
123 FreePool(ConsoleInfo->Attributes);
124 DEBUG_CODE(ConsoleInfo->Attributes = NULL;);
125 DEBUG_CODE(ConsoleInfo->AttribSize = 0;);
126 }
127
128 gST->ConsoleOutHandle = ConsoleInfo->OldConHandle;
129 gST->ConOut = ConsoleInfo->OldConOut;
130
131 return (gBS->UninstallProtocolInterface(gImageHandle, &gEfiSimpleTextOutProtocolGuid, (VOID*)&ConsoleInfo->OurConOut));
132 }
133
134 /**
135 Displays previously logged output back to the screen.
136
137 This will scroll the screen forwards and backwards through the log of previous
138 output. If Rows is 0 then the size of 1/2 the screen will be scrolled. If Rows
139 is (UINTN)(-1) then the size of the screen will be scrolled.
140
141 @param[in] Forward If TRUE then the log will be displayed forwards (scroll to newer).
142 If FALSE then the log will be displayed backwards (scroll to older).
143 @param[in] Rows Determines how many rows the log should scroll.
144 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
145 **/
146 EFI_STATUS
147 EFIAPI
148 ConsoleLoggerDisplayHistory(
149 IN CONST BOOLEAN Forward,
150 IN CONST UINTN Rows,
151 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
152 )
153 {
154 UINTN RowChange;
155
156 ASSERT(ConsoleInfo != NULL);
157
158 //
159 // Calculate the row number change
160 //
161 switch (Rows) {
162 case ((UINTN)(-1)):
163 RowChange = ConsoleInfo->RowsPerScreen;
164 break;
165 case (0):
166 RowChange = ConsoleInfo->RowsPerScreen / 2;
167 break;
168 default:
169 RowChange = Rows;
170 break;
171 }
172
173 //
174 // Do the math for direction
175 //
176 if (Forward) {
177 if ((ConsoleInfo->OriginalStartRow - ConsoleInfo->CurrentStartRow) < RowChange) {
178 RowChange = ConsoleInfo->OriginalStartRow - ConsoleInfo->CurrentStartRow;
179 }
180 } else {
181 if (ConsoleInfo->CurrentStartRow < RowChange) {
182 RowChange = ConsoleInfo->CurrentStartRow;
183 }
184 }
185
186 //
187 // If we are already at one end or the other
188 //
189 if (RowChange == 0) {
190 return (EFI_SUCCESS);
191 }
192
193 //
194 // Clear the screen
195 //
196 ConsoleInfo->OldConOut->ClearScreen(ConsoleInfo->OldConOut);
197
198 //
199 // Set the new start row
200 //
201 if (Forward) {
202 ConsoleInfo->CurrentStartRow += RowChange;
203 } else {
204 ConsoleInfo->CurrentStartRow -= RowChange;
205 }
206
207 //
208 // Change the screen
209 //
210 return (UpdateDisplayFromHistory(ConsoleInfo));
211 }
212
213 /**
214 Function to return to normal output whent he scrolling is complete.
215 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
216
217 @retval EFI_SUCCESS The operation was successful.
218 @return other The operation failed. See UpdateDisplayFromHistory.
219
220 @sa UpdateDisplayFromHistory
221 **/
222 EFI_STATUS
223 EFIAPI
224 ConsoleLoggerStopHistory(
225 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
226 )
227 {
228 ASSERT(ConsoleInfo != NULL);
229 if (ConsoleInfo->CurrentStartRow == ConsoleInfo->OriginalStartRow) {
230 return (EFI_SUCCESS);
231 }
232 ConsoleInfo->CurrentStartRow = ConsoleInfo->OriginalStartRow;
233 return (UpdateDisplayFromHistory(ConsoleInfo));
234 }
235
236 /**
237 Updates the hidden ConOut to be displaying the correct stuff.
238 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
239
240 @retval EFI_SUCCESS The operation was successful.
241 @return other The operation failed.
242 **/
243 EFI_STATUS
244 EFIAPI
245 UpdateDisplayFromHistory(
246 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
247 )
248 {
249 EFI_STATUS Status;
250 EFI_STATUS RetVal;
251 CHAR16 *Screen;
252 INT32 *Attributes;
253 UINTN CurrentRow;
254 CHAR16 TempCharHolder;
255 UINTN Column;
256 INT32 CurrentAttrib;
257 UINTN CurrentColumn;
258 CHAR16 *StringSegment;
259 CHAR16 *StringSegmentEnd;
260 CHAR16 StringSegmentEndChar;
261
262 ASSERT(ConsoleInfo != NULL);
263 TempCharHolder = CHAR_NULL;
264 RetVal = EFI_SUCCESS;
265
266 //
267 // Disable cursor visibility and move it to the top left corner
268 //
269 ConsoleInfo->OldConOut->EnableCursor (ConsoleInfo->OldConOut, FALSE);
270 ConsoleInfo->OldConOut->SetCursorPosition (ConsoleInfo->OldConOut, 0, 0);
271
272 Screen = &ConsoleInfo->Buffer[(ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->CurrentStartRow];
273 Attributes = &ConsoleInfo->Attributes[ConsoleInfo->ColsPerScreen * ConsoleInfo->CurrentStartRow];
274 for ( CurrentRow = 0
275 ; CurrentRow < ConsoleInfo->RowsPerScreen
276 ; CurrentRow++
277 , Screen += (ConsoleInfo->ColsPerScreen + 2)
278 , Attributes += ConsoleInfo->ColsPerScreen
279 ){
280 //
281 // dont use the last char - prevents screen scroll
282 //
283 if (CurrentRow == (ConsoleInfo->RowsPerScreen-1)){
284 TempCharHolder = Screen[ConsoleInfo->ColsPerScreen - 1];
285 Screen[ConsoleInfo->ColsPerScreen - 1] = CHAR_NULL;
286 }
287
288 for ( Column = 0
289 ; Column < ConsoleInfo->ColsPerScreen
290 ; Column++
291 ){
292 if (Screen[Column] != CHAR_NULL) {
293 CurrentAttrib = Attributes[Column];
294 CurrentColumn = Column;
295 StringSegment = &Screen[Column];
296
297 //
298 // Find the first char with a different arrribute and make that temporarily NULL
299 // so we can do fewer printout statements. (later) restore that one and we will
300 // start at that collumn on the next loop.
301 //
302 StringSegmentEndChar = CHAR_NULL;
303 for ( StringSegmentEnd = StringSegment
304 ; StringSegmentEnd != CHAR_NULL
305 ; StringSegmentEnd++
306 , Column++
307 ){
308 if (Attributes[Column] != CurrentAttrib) {
309 StringSegmentEndChar = *StringSegmentEnd;
310 *StringSegmentEnd = CHAR_NULL;
311 break;
312 }
313 } // StringSegmentEnd loop
314
315 //
316 // Now write out as much as had the same Attributes
317 //
318
319 ConsoleInfo->OldConOut->SetAttribute(ConsoleInfo->OldConOut, CurrentAttrib);
320 ConsoleInfo->OldConOut->SetCursorPosition(ConsoleInfo->OldConOut, CurrentColumn, CurrentRow);
321 Status = ConsoleInfo->OldConOut->OutputString(ConsoleInfo->OldConOut, StringSegment);
322
323 if (EFI_ERROR(Status)) {
324 ASSERT(FALSE);
325 RetVal = Status;
326 }
327
328 //
329 // If we found a change in attribute put the character back and decrement the column
330 // so when it increments it will point at that character and we will start printing
331 // a segment with that new attribute
332 //
333 if (StringSegmentEndChar != CHAR_NULL) {
334 *StringSegmentEnd = StringSegmentEndChar;
335 StringSegmentEndChar = CHAR_NULL;
336 Column--;
337 }
338 }
339 } // column for loop
340
341 //
342 // If we removed the last char and this was the last row put it back
343 //
344 if (TempCharHolder != CHAR_NULL) {
345 Screen[ConsoleInfo->ColsPerScreen - 1] = TempCharHolder;
346 TempCharHolder = CHAR_NULL;
347 }
348 } // row for loop
349
350 //
351 // If we are setting the screen back to original turn on the cursor and make it visible
352 // and set the attributes back to what they were
353 //
354 if (ConsoleInfo->CurrentStartRow == ConsoleInfo->OriginalStartRow) {
355 ConsoleInfo->OldConOut->SetAttribute (
356 ConsoleInfo->OldConOut,
357 ConsoleInfo->HistoryMode.Attribute
358 );
359 ConsoleInfo->OldConOut->SetCursorPosition (
360 ConsoleInfo->OldConOut,
361 ConsoleInfo->HistoryMode.CursorColumn,
362 ConsoleInfo->HistoryMode.CursorRow - ConsoleInfo->OriginalStartRow
363 );
364
365 Status = ConsoleInfo->OldConOut->EnableCursor (
366 ConsoleInfo->OldConOut,
367 ConsoleInfo->HistoryMode.CursorVisible
368 );
369 if (EFI_ERROR (Status)) {
370 RetVal = Status;
371 }
372 }
373
374 return (RetVal);
375 }
376
377 /**
378 Reset the text output device hardware and optionaly run diagnostics
379
380 @param This pointer to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
381 @param ExtendedVerification Indicates that a more extensive test may be performed
382
383 @retval EFI_SUCCESS The text output device was reset.
384 @retval EFI_DEVICE_ERROR The text output device is not functioning correctly and
385 could not be reset.
386 **/
387 EFI_STATUS
388 EFIAPI
389 ConsoleLoggerReset (
390 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
391 IN BOOLEAN ExtendedVerification
392 )
393 {
394 EFI_STATUS Status;
395 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
396 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
397
398 //
399 // Forward the request to the original ConOut
400 //
401 Status = ConsoleInfo->OldConOut->Reset (ConsoleInfo->OldConOut, ExtendedVerification);
402
403 //
404 // Check that the buffers are still correct for logging
405 //
406 if (!EFI_ERROR (Status)) {
407 ConsoleLoggerResetBuffers(ConsoleInfo);
408 }
409
410 return Status;
411 }
412
413 /**
414 Appends a string to the history buffer. If the buffer is full then the oldest
415 information in the buffer will be dropped. Information is added in a line by
416 line manner such that an empty line takes up just as much space as a full line.
417
418 @param[in] String String pointer to add.
419 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
420 **/
421 EFI_STATUS
422 EFIAPI
423 AppendStringToHistory(
424 IN CONST CHAR16 *String,
425 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
426 )
427 {
428 CONST CHAR16 *Walker;
429 UINTN CopySize;
430 UINTN PrintIndex;
431 UINTN Index;
432
433 ASSERT(ConsoleInfo != NULL);
434
435 for ( Walker = String
436 ; Walker != NULL && *Walker != CHAR_NULL
437 ; Walker++
438 ){
439 switch (*Walker) {
440 case (CHAR_BACKSPACE):
441 if (ConsoleInfo->HistoryMode.CursorColumn > 0) {
442 ConsoleInfo->HistoryMode.CursorColumn--;
443 }
444 break;
445 case (CHAR_LINEFEED):
446 if (ConsoleInfo->HistoryMode.CursorRow >= (INT32)((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)-1)) {
447 //
448 // Should never be bigger
449 //
450 ASSERT(ConsoleInfo->HistoryMode.CursorRow == (INT32)((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)-1));
451
452 //
453 // scroll history attributes 'up' 1 row and set the last row to default attribute
454 //
455 CopySize = ConsoleInfo->ColsPerScreen
456 * ((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) - 1)
457 * sizeof(ConsoleInfo->Attributes[0]);
458 ASSERT(CopySize < ConsoleInfo->AttribSize);
459 CopyMem(
460 ConsoleInfo->Attributes,
461 ConsoleInfo->Attributes + ConsoleInfo->ColsPerScreen,
462 CopySize
463 );
464
465 for ( Index = 0
466 ; Index < ConsoleInfo->ColsPerScreen
467 ; Index++
468 ){
469 *(ConsoleInfo->Attributes + (CopySize/sizeof(ConsoleInfo->Attributes)) + Index) = ConsoleInfo->HistoryMode.Attribute;
470 }
471
472 //
473 // scroll history buffer 'up' 1 row and set the last row to spaces (L' ')
474 //
475 CopySize = (ConsoleInfo->ColsPerScreen + 2)
476 * ((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) - 1)
477 * sizeof(ConsoleInfo->Buffer[0]);
478 ASSERT(CopySize < ConsoleInfo->BufferSize);
479 CopyMem(
480 ConsoleInfo->Buffer,
481 ConsoleInfo->Buffer + (ConsoleInfo->ColsPerScreen + 2),
482 CopySize
483 );
484
485 //
486 // Set that last row of chars to spaces
487 //
488 SetMem16(((UINT8*)ConsoleInfo->Buffer)+CopySize, ConsoleInfo->ColsPerScreen*sizeof(CHAR16), L' ');
489 } else {
490 //
491 // we are not on the last row
492 //
493
494 //
495 // We should not be scrolling history
496 //
497 ASSERT (ConsoleInfo->OriginalStartRow == ConsoleInfo->CurrentStartRow);
498 //
499 // are we at the end of a row?
500 //
501 if (ConsoleInfo->HistoryMode.CursorRow == (INT32) (ConsoleInfo->OriginalStartRow + ConsoleInfo->RowsPerScreen - 1)) {
502 ConsoleInfo->OriginalStartRow++;
503 ConsoleInfo->CurrentStartRow++;
504 }
505 ConsoleInfo->HistoryMode.CursorRow++;
506 }
507 break;
508 case (CHAR_CARRIAGE_RETURN):
509 //
510 // Move the cursor to the beginning of the current row.
511 //
512 ConsoleInfo->HistoryMode.CursorColumn = 0;
513 break;
514 default:
515 //
516 // Acrtually print characters into the history buffer
517 //
518
519 PrintIndex = ConsoleInfo->HistoryMode.CursorRow * ConsoleInfo->ColsPerScreen + ConsoleInfo->HistoryMode.CursorColumn;
520
521 for ( // no initializer needed
522 ; ConsoleInfo->HistoryMode.CursorColumn < (INT32) ConsoleInfo->ColsPerScreen
523 ; ConsoleInfo->HistoryMode.CursorColumn++
524 , PrintIndex++
525 , Walker++
526 ){
527 if (*Walker == CHAR_NULL
528 ||*Walker == CHAR_BACKSPACE
529 ||*Walker == CHAR_LINEFEED
530 ||*Walker == CHAR_CARRIAGE_RETURN
531 ){
532 Walker--;
533 break;
534 }
535 //
536 // The buffer is 2*CursorRow more since it has that many \r\n characters at the end of each row.
537 //
538
539 ASSERT(PrintIndex + ConsoleInfo->HistoryMode.CursorRow < ConsoleInfo->BufferSize);
540 ConsoleInfo->Buffer[PrintIndex + (2*ConsoleInfo->HistoryMode.CursorRow)] = *Walker;
541 ASSERT(PrintIndex < ConsoleInfo->AttribSize);
542 ConsoleInfo->Attributes[PrintIndex] = ConsoleInfo->HistoryMode.Attribute;
543 } // for loop
544
545 //
546 // Add the carriage return and line feed at the end of the lines
547 //
548 if (ConsoleInfo->HistoryMode.CursorColumn >= (INT32)ConsoleInfo->ColsPerScreen) {
549 AppendStringToHistory(L"\r\n", ConsoleInfo);
550 Walker--;
551 }
552
553 break;
554 } // switch for character
555 } // for loop
556
557 return (EFI_SUCCESS);
558 }
559
560 /**
561 Worker function to handle printing the output to the screen
562 and the history buffer
563
564 @param[in] String The string to output
565 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
566
567 @retval EFI_SUCCESS The string was printed
568 @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
569 the text.
570 @retval EFI_UNSUPPORTED The output device's mode is not currently in a
571 defined text mode.
572 @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
573 characters in the Unicode string could not be
574 rendered and were skipped.
575 **/
576 EFI_STATUS
577 EFIAPI
578 ConsoleLoggerOutputStringSplit(
579 IN CONST CHAR16 *String,
580 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
581 )
582 {
583 EFI_STATUS Status;
584
585 //
586 // Forward the request to the original ConOut
587 //
588 Status = ConsoleInfo->OldConOut->OutputString (ConsoleInfo->OldConOut, (CHAR16*)String);
589
590 if (EFI_ERROR(Status)) {
591 return (Status);
592 }
593
594 return (AppendStringToHistory(String, ConsoleInfo));
595 }
596
597 /**
598 Function to handle page break mode.
599
600 This function will prompt for continue or break.
601
602 @retval EFI_SUCCESS Continue was choosen
603 @return other Break was choosen
604 **/
605 EFI_STATUS
606 EFIAPI
607 ConsoleLoggerDoPageBreak(
608 VOID
609 )
610 {
611 SHELL_PROMPT_RESPONSE *Resp;
612 EFI_STATUS Status;
613
614 Resp = NULL;
615 ASSERT(ShellInfoObject.PageBreakEnabled);
616 ShellInfoObject.PageBreakEnabled = FALSE;
617 Status = ShellPromptForResponseHii(ShellPromptResponseTypeQuitContinue, STRING_TOKEN(STR_SHELL_QUIT_CONT), ShellInfoObject.HiiHandle, (VOID**)&Resp);
618 ShellInfoObject.PageBreakEnabled = TRUE;
619 ASSERT(Resp != NULL);
620 if (Resp == NULL) {
621 return (EFI_NOT_FOUND);
622 }
623 if (EFI_ERROR(Status)) {
624 if (Resp != NULL) {
625 FreePool(Resp);
626 }
627 return (Status);
628 }
629 if (*Resp == ShellPromptResponseContinue) {
630 FreePool(Resp);
631 ShellInfoObject.ConsoleInfo->RowCounter = 0;
632 // ShellInfoObject.ConsoleInfo->OurConOut.Mode->CursorRow = 0;
633 // ShellInfoObject.ConsoleInfo->OurConOut.Mode->CursorColumn = 0;
634
635 return (EFI_SUCCESS);
636 } else if (*Resp == ShellPromptResponseQuit) {
637 FreePool(Resp);
638 ShellInfoObject.ConsoleInfo->Enabled = FALSE;
639 return (EFI_DEVICE_ERROR);
640 } else {
641 ASSERT(FALSE);
642 }
643 return (EFI_SUCCESS);
644 }
645 /**
646 Worker function to handle printing the output with page breaks.
647
648 @param[in] String The string to output
649 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
650
651 @retval EFI_SUCCESS The string was printed
652 @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
653 the text.
654 @retval EFI_UNSUPPORTED The output device's mode is not currently in a
655 defined text mode.
656 @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
657 characters in the Unicode string could not be
658 rendered and were skipped.
659 **/
660 EFI_STATUS
661 EFIAPI
662 ConsoleLoggerPrintWithPageBreak(
663 IN CONST CHAR16 *String,
664 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
665 )
666 {
667 CONST CHAR16 *Walker;
668 CONST CHAR16 *LineStart;
669 CHAR16 *StringCopy;
670 CHAR16 TempChar;
671
672 StringCopy = NULL;
673 StringCopy = StrnCatGrow(&StringCopy, NULL, String, 0);
674 if (StringCopy == NULL) {
675 return (EFI_OUT_OF_RESOURCES);
676 }
677
678 for ( Walker = StringCopy
679 , LineStart = StringCopy
680 ; Walker != NULL && *Walker != CHAR_NULL
681 ; Walker++
682 ){
683 switch (*Walker) {
684 case (CHAR_BACKSPACE):
685 if (ConsoleInfo->OurConOut.Mode->CursorColumn > 0) {
686 ConsoleInfo->OurConOut.Mode->CursorColumn--;
687 }
688 break;
689 case (CHAR_LINEFEED):
690 //
691 // add a temp NULL terminator
692 //
693 TempChar = *(Walker + 1);
694 *((CHAR16*)(Walker+1)) = CHAR_NULL;
695
696 //
697 // output the string
698 //
699 ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo);
700
701 //
702 // restore the temp NULL terminator to it's original character
703 //
704 *((CHAR16*)(Walker+1)) = TempChar;
705
706 //
707 // Update LineStart Variable
708 //
709 LineStart = Walker + 1;
710
711 //
712 // increment row count
713 //
714 ShellInfoObject.ConsoleInfo->RowCounter++;
715 ConsoleInfo->OurConOut.Mode->CursorRow++;
716
717 break;
718 case (CHAR_CARRIAGE_RETURN):
719 //
720 // Move the cursor to the beginning of the current row.
721 //
722 ConsoleInfo->OurConOut.Mode->CursorColumn = 0;
723 break;
724 default:
725 //
726 // increment column count
727 //
728 ConsoleInfo->OurConOut.Mode->CursorColumn++;
729 //
730 // check if that is the last column
731 //
732 if ((INTN)ConsoleInfo->ColsPerScreen == ConsoleInfo->OurConOut.Mode->CursorColumn + 1) {
733 //
734 // output a line similar to the linefeed character.
735 //
736
737 //
738 // add a temp NULL terminator
739 //
740 TempChar = *(Walker + 1);
741 *((CHAR16*)(Walker+1)) = CHAR_NULL;
742
743 //
744 // output the string
745 //
746 ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo);
747
748 //
749 // restore the temp NULL terminator to it's original character
750 //
751 *((CHAR16*)(Walker+1)) = TempChar;
752
753 //
754 // Update LineStart Variable
755 //
756 LineStart = Walker + 1;
757
758 //
759 // increment row count and zero the column
760 //
761 ShellInfoObject.ConsoleInfo->RowCounter++;
762 ConsoleInfo->OurConOut.Mode->CursorRow++;
763 ConsoleInfo->OurConOut.Mode->CursorColumn = 0;
764 } // last column on line
765 break;
766 } // switch for character
767
768 //
769 // check if that was the last printable row. If yes handle PageBreak mode
770 //
771 if ((ConsoleInfo->RowsPerScreen) -1 == ShellInfoObject.ConsoleInfo->RowCounter) {
772 if (EFI_ERROR(ConsoleLoggerDoPageBreak())) {
773 //
774 // We got an error which means 'break' and halt the printing
775 //
776 SHELL_FREE_NON_NULL(StringCopy);
777 return (EFI_DEVICE_ERROR);
778 }
779 }
780 } // for loop
781
782 if (LineStart != NULL && *LineStart != CHAR_NULL) {
783 ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo);
784 }
785
786 SHELL_FREE_NON_NULL(StringCopy);
787 return (EFI_SUCCESS);
788 }
789
790 /**
791 Write a Unicode string to the output device.
792
793 @param[in] This Protocol instance pointer.
794 @param[in] WString The NULL-terminated Unicode string to be displayed on the output
795 device(s). All output devices must also support the Unicode
796 drawing defined in this file.
797 @retval EFI_SUCCESS The string was output to the device.
798 @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
799 the text.
800 @retval EFI_UNSUPPORTED The output device's mode is not currently in a
801 defined text mode.
802 @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
803 characters in the Unicode string could not be
804 rendered and were skipped.
805 **/
806 EFI_STATUS
807 EFIAPI
808 ConsoleLoggerOutputString (
809 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
810 IN CHAR16 *WString
811 )
812 {
813 EFI_INPUT_KEY Key;
814 UINTN EventIndex;
815 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
816 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
817 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
818 return (EFI_UNSUPPORTED);
819 }
820 ASSERT(ShellInfoObject.ConsoleInfo == ConsoleInfo);
821 if (ShellInfoObject.HaltOutput) {
822 //
823 // just get some key
824 //
825 gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
826 gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
827 ShellInfoObject.HaltOutput = FALSE;
828 }
829 if (!ShellInfoObject.ConsoleInfo->Enabled) {
830 return (EFI_DEVICE_ERROR);
831 } else if (ShellInfoObject.PageBreakEnabled) {
832 return (ConsoleLoggerPrintWithPageBreak(WString, ConsoleInfo));
833 } else {
834 return (ConsoleLoggerOutputStringSplit(WString, ConsoleInfo));
835 }
836 }
837
838 /**
839 Verifies that all characters in a Unicode string can be output to the
840 target device.
841
842 @param[in] This Protocol instance pointer.
843 @param[in] WString The NULL-terminated Unicode string to be examined for the output
844 device(s).
845
846 @retval EFI_SUCCESS The device(s) are capable of rendering the output string.
847 @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be
848 rendered by one or more of the output devices mapped
849 by the EFI handle.
850
851 **/
852 EFI_STATUS
853 EFIAPI
854 ConsoleLoggerTestString (
855 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
856 IN CHAR16 *WString
857 )
858 {
859 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
860 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
861 //
862 // Forward the request to the original ConOut
863 //
864 return (ConsoleInfo->OldConOut->TestString (ConsoleInfo->OldConOut, WString));
865 }
866
867 /**
868 Returns information for an available text mode that the output device(s)
869 supports.
870
871 @param[in] This Protocol instance pointer.
872 @param[in] ModeNumber The mode number to return information on.
873 @param[out] Columns Upon return, the number of columns in the selected geometry
874 @param[out] Rows Upon return, the number of rows in the selected geometry
875
876 @retval EFI_SUCCESS The requested mode information was returned.
877 @retval EFI_DEVICE_ERROR The device had an error and could not
878 complete the request.
879 @retval EFI_UNSUPPORTED The mode number was not valid.
880 **/
881 EFI_STATUS
882 EFIAPI
883 ConsoleLoggerQueryMode (
884 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
885 IN UINTN ModeNumber,
886 OUT UINTN *Columns,
887 OUT UINTN *Rows
888 )
889 {
890 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
891 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
892 //
893 // Forward the request to the original ConOut
894 //
895 return (ConsoleInfo->OldConOut->QueryMode (
896 ConsoleInfo->OldConOut,
897 ModeNumber,
898 Columns,
899 Rows
900 ));
901 }
902
903 /**
904 Sets the output device(s) to a specified mode.
905
906 @param[in] This Protocol instance pointer.
907 @param[in] ModeNumber The mode number to set.
908
909
910 @retval EFI_SUCCESS The requested text mode was set.
911 @retval EFI_DEVICE_ERROR The device had an error and
912 could not complete the request.
913 @retval EFI_UNSUPPORTED The mode number was not valid.
914 **/
915 EFI_STATUS
916 EFIAPI
917 ConsoleLoggerSetMode (
918 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
919 IN UINTN ModeNumber
920 )
921 {
922 EFI_STATUS Status;
923
924 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
925 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
926
927 //
928 // Forward the request to the original ConOut
929 //
930 Status = ConsoleInfo->OldConOut->SetMode (ConsoleInfo->OldConOut, ModeNumber);
931
932 //
933 // Check that the buffers are still correct for logging
934 //
935 if (!EFI_ERROR (Status)) {
936 ConsoleInfo->OurConOut.Mode = gST->ConOut->Mode;
937 ConsoleLoggerResetBuffers(ConsoleInfo);
938 }
939
940 return Status;
941 }
942
943 /**
944 Sets the background and foreground colors for the OutputString () and
945 ClearScreen () functions.
946
947 @param[in] This Protocol instance pointer.
948 @param[in] Attribute The attribute to set. Bits 0..3 are the foreground color, and
949 bits 4..6 are the background color. All other bits are undefined
950 and must be zero. The valid Attributes are defined in this file.
951
952 @retval EFI_SUCCESS The attribute was set.
953 @retval EFI_DEVICE_ERROR The device had an error and
954 could not complete the request.
955 @retval EFI_UNSUPPORTED The attribute requested is not defined.
956
957 **/
958 EFI_STATUS
959 EFIAPI
960 ConsoleLoggerSetAttribute (
961 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
962 IN UINTN Attribute
963 )
964 {
965 EFI_STATUS Status;
966
967 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
968 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
969
970 //
971 // Forward the request to the original ConOut
972 //
973 Status = ConsoleInfo->OldConOut->SetAttribute (ConsoleInfo->OldConOut, Attribute);
974
975 //
976 // Record console output history
977 //
978 if (!EFI_ERROR (Status)) {
979 ConsoleInfo->HistoryMode.Attribute = (INT32) Attribute;
980 }
981
982 return Status;
983 }
984
985 /**
986 Clears the output device(s) display to the currently selected background
987 color.
988
989 @param[in] This Protocol instance pointer.
990
991 @retval EFI_SUCCESS The operation completed successfully.
992 @retval EFI_DEVICE_ERROR The device had an error and
993 could not complete the request.
994 @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
995 **/
996 EFI_STATUS
997 EFIAPI
998 ConsoleLoggerClearScreen (
999 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
1000 )
1001 {
1002 EFI_STATUS Status;
1003 CHAR16 *Screen;
1004 INT32 *Attributes;
1005 UINTN Row;
1006 UINTN Column;
1007 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
1008
1009 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
1010 return (EFI_UNSUPPORTED);
1011 }
1012
1013 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
1014
1015 //
1016 // Forward the request to the original ConOut
1017 //
1018 Status = ConsoleInfo->OldConOut->ClearScreen (ConsoleInfo->OldConOut);
1019
1020 //
1021 // Record console output history
1022 //
1023 if (!EFI_ERROR (Status)) {
1024 Screen = &ConsoleInfo->Buffer[(ConsoleInfo->ColsPerScreen + 1) * ConsoleInfo->CurrentStartRow];
1025 Attributes = &ConsoleInfo->Attributes[ConsoleInfo->ColsPerScreen * ConsoleInfo->CurrentStartRow];
1026 for ( Row = ConsoleInfo->OriginalStartRow
1027 ; Row < (ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)
1028 ; Row++
1029 ){
1030 for ( Column = 0
1031 ; Column < ConsoleInfo->ColsPerScreen
1032 ; Column++
1033 , Screen++
1034 , Attributes++
1035 ){
1036 *Screen = L' ';
1037 *Attributes = ConsoleInfo->OldConOut->Mode->Attribute;
1038 }
1039 //
1040 // Skip the NULL on each column end in text buffer only
1041 //
1042 Screen++;
1043 }
1044 ConsoleInfo->HistoryMode.CursorColumn = 0;
1045 ConsoleInfo->HistoryMode.CursorRow = 0;
1046 }
1047
1048 return Status;
1049 }
1050
1051 /**
1052 Sets the current coordinates of the cursor position
1053
1054 @param[in] This Protocol instance pointer.
1055 @param[in] Column Column to put the cursor in. Must be between zero and Column returned from QueryMode
1056 @param[in] Row Row to put the cursor in. Must be between zero and Row returned from QueryMode
1057
1058 @retval EFI_SUCCESS The operation completed successfully.
1059 @retval EFI_DEVICE_ERROR The device had an error and
1060 could not complete the request.
1061 @retval EFI_UNSUPPORTED The output device is not in a valid text mode, or the
1062 cursor position is invalid for the current mode.
1063 **/
1064 EFI_STATUS
1065 EFIAPI
1066 ConsoleLoggerSetCursorPosition (
1067 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
1068 IN UINTN Column,
1069 IN UINTN Row
1070 )
1071 {
1072 EFI_STATUS Status;
1073 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
1074
1075 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
1076 return (EFI_UNSUPPORTED);
1077 }
1078
1079 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
1080 //
1081 // Forward the request to the original ConOut
1082 //
1083 Status = ConsoleInfo->OldConOut->SetCursorPosition (
1084 ConsoleInfo->OldConOut,
1085 Column,
1086 Row
1087 );
1088
1089 //
1090 // Record console output history
1091 //
1092 if (!EFI_ERROR (Status)) {
1093 ConsoleInfo->HistoryMode.CursorColumn = (INT32)Column;
1094 ConsoleInfo->HistoryMode.CursorRow = (INT32)(ConsoleInfo->OriginalStartRow + Row);
1095 }
1096
1097 return Status;
1098 }
1099
1100 /**
1101 Makes the cursor visible or invisible
1102
1103 @param[in] This Protocol instance pointer.
1104 @param[in] Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is
1105 set to be invisible.
1106
1107 @retval EFI_SUCCESS The operation completed successfully.
1108 @retval EFI_DEVICE_ERROR The device had an error and could not complete the
1109 request, or the device does not support changing
1110 the cursor mode.
1111 @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
1112 **/
1113 EFI_STATUS
1114 EFIAPI
1115 ConsoleLoggerEnableCursor (
1116 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
1117 IN BOOLEAN Visible
1118 )
1119 {
1120 EFI_STATUS Status;
1121
1122 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
1123 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
1124 //
1125 // Forward the request to the original ConOut
1126 //
1127 Status = ConsoleInfo->OldConOut->EnableCursor (ConsoleInfo->OldConOut, Visible);
1128
1129 //
1130 // Record console output history
1131 //
1132 if (!EFI_ERROR (Status)) {
1133 ConsoleInfo->HistoryMode.CursorVisible = Visible;
1134 }
1135
1136 return Status;
1137 }
1138
1139 /**
1140 Function to update and verify that the current buffers are correct.
1141
1142 @param[in] ConsoleInfo The pointer to the instance of the console logger information.
1143
1144 This will be used when a mode has changed or a reset ocurred to verify all
1145 history buffers.
1146 **/
1147 EFI_STATUS
1148 EFIAPI
1149 ConsoleLoggerResetBuffers(
1150 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
1151 )
1152 {
1153 EFI_STATUS Status;
1154
1155 if (ConsoleInfo->Buffer != NULL) {
1156 FreePool(ConsoleInfo->Buffer);
1157 ConsoleInfo->Buffer = NULL;
1158 ConsoleInfo->BufferSize = 0;
1159 }
1160 if (ConsoleInfo->Attributes != NULL) {
1161 FreePool(ConsoleInfo->Attributes);
1162 ConsoleInfo->Attributes = NULL;
1163 ConsoleInfo->AttribSize = 0;
1164 }
1165
1166 Status = gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &ConsoleInfo->ColsPerScreen, &ConsoleInfo->RowsPerScreen);
1167 if (EFI_ERROR(Status)){
1168 return (Status);
1169 }
1170
1171 ConsoleInfo->BufferSize = (ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount * sizeof(ConsoleInfo->Buffer[0]);
1172 ConsoleInfo->AttribSize = ConsoleInfo->ColsPerScreen * ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount * sizeof(ConsoleInfo->Attributes[0]);
1173
1174 ConsoleInfo->Buffer = (CHAR16*)AllocateZeroPool(ConsoleInfo->BufferSize);
1175
1176 if (ConsoleInfo->Buffer == NULL) {
1177 return (EFI_OUT_OF_RESOURCES);
1178 }
1179
1180 ConsoleInfo->Attributes = (INT32*)AllocateZeroPool(ConsoleInfo->AttribSize);
1181 if (ConsoleInfo->Attributes == NULL) {
1182 FreePool(ConsoleInfo->Buffer);
1183 ConsoleInfo->Buffer = NULL;
1184 return (EFI_OUT_OF_RESOURCES);
1185 }
1186
1187 CopyMem (&ConsoleInfo->HistoryMode, ConsoleInfo->OldConOut->Mode, sizeof (EFI_SIMPLE_TEXT_OUTPUT_MODE));
1188
1189 return (EFI_SUCCESS);
1190 }