xref: /DragonStub/lib/print.c (revision 803b49c40bb0b720b90d9c31d372911f1b946aa7)
1 /*++
2 
3 Copyright (c) 1998  Intel Corporation
4 
5 Module Name:
6 
7     print.c
8 
9 Abstract:
10 
11 
12 
13 
14 Revision History
15 
16 --*/
17 
18 #include "lib.h"
19 #include "efistdarg.h"                        // !!!
20 
21 //
22 // Declare runtime functions
23 //
24 
25 #ifdef RUNTIME_CODE
26 #ifndef __GNUC__
27 #pragma RUNTIME_CODE(DbgPrint)
28 
29 // For debugging..
30 
31 /*
32 #pragma RUNTIME_CODE(_Print)
33 #pragma RUNTIME_CODE(PFLUSH)
34 #pragma RUNTIME_CODE(PSETATTR)
35 #pragma RUNTIME_CODE(PPUTC)
36 #pragma RUNTIME_CODE(PGETC)
37 #pragma RUNTIME_CODE(PITEM)
38 #pragma RUNTIME_CODE(ValueToHex)
39 #pragma RUNTIME_CODE(ValueToString)
40 #pragma RUNTIME_CODE(TimeToString)
41 */
42 
43 #endif /* !defined(__GNUC__) */
44 #endif
45 
46 //
47 //
48 //
49 
50 
51 #define PRINT_STRING_LEN            200
52 #define PRINT_ITEM_BUFFER_LEN       100
53 
54 typedef struct {
55     BOOLEAN             Ascii;
56     UINTN               Index;
57     union {
58         CONST CHAR16    *pw;
59         CONST CHAR8     *pc;
60     } un;
61 } POINTER;
62 
63 #define pw	un.pw
64 #define pc	un.pc
65 
66 typedef struct _pitem {
67 
68     POINTER     Item;
69     CHAR16      Scratch[PRINT_ITEM_BUFFER_LEN];
70     UINTN       Width;
71     UINTN       FieldWidth;
72     UINTN       *WidthParse;
73     CHAR16      Pad;
74     BOOLEAN     PadBefore;
75     BOOLEAN     Comma;
76     BOOLEAN     Long;
77 } PRINT_ITEM;
78 
79 
80 typedef struct _pstate {
81     // Input
82     POINTER     fmt;
83     va_list     args;
84 
85     // Output
86     CHAR16      *Buffer;
87     CHAR16      *End;
88     CHAR16      *Pos;
89     UINTN       Len;
90 
91     UINTN       Attr;
92     UINTN       RestoreAttr;
93 
94     UINTN       AttrNorm;
95     UINTN       AttrHighlight;
96     UINTN       AttrError;
97 
98     INTN        (EFIAPI *Output)(VOID *context, CHAR16 *str);
99     INTN        (EFIAPI *SetAttr)(VOID *context, UINTN attr);
100     VOID        *Context;
101 
102     // Current item being formatted
103     struct _pitem  *Item;
104 } PRINT_STATE;
105 
106 //
107 // Internal fucntions
108 //
109 
110 STATIC
111 UINTN
112 _Print (
113     IN PRINT_STATE     *ps
114     );
115 
116 STATIC
117 UINTN
118 _IPrint (
119     IN UINTN                            Column,
120     IN UINTN                            Row,
121     IN SIMPLE_TEXT_OUTPUT_INTERFACE     *Out,
122     IN CONST CHAR16                     *fmt,
123     IN CONST CHAR8                      *fmta,
124     IN va_list                          args
125     );
126 
127 STATIC
128 INTN EFIAPI
129 _DbgOut (
130     IN VOID     *Context,
131     IN CHAR16   *Buffer
132     );
133 
134 STATIC
135 VOID
136 PFLUSH (
137     IN OUT PRINT_STATE     *ps
138     );
139 
140 STATIC
141 VOID
142 PPUTC (
143     IN OUT PRINT_STATE     *ps,
144     IN CHAR16              c
145     );
146 
147 STATIC
148 VOID
149 PITEM (
150     IN OUT PRINT_STATE  *ps
151     );
152 
153 STATIC
154 CHAR16
155 PGETC (
156     IN POINTER      *p
157     );
158 
159 STATIC
160 VOID
161 PSETATTR (
162     IN OUT PRINT_STATE  *ps,
163     IN UINTN             Attr
164     );
165 
166 //
167 //
168 //
169 
170 INTN EFIAPI
171 _SPrint (
172     IN VOID     *Context,
173     IN CHAR16   *Buffer
174     );
175 
176 INTN EFIAPI
177 _PoolPrint (
178     IN VOID     *Context,
179     IN CHAR16   *Buffer
180     );
181 
182 INTN
183 DbgPrint (
184     IN INTN         mask,
185     IN CONST CHAR8  *fmt,
186     ...
187     )
188 /*++
189 
190 Routine Description:
191 
192     Prints a formatted unicode string to the default StandardError console
193 
194 Arguments:
195 
196     mask        - Bit mask of debug string.  If a bit is set in the
197                   mask that is also set in EFIDebug the string is
198                   printed; otherwise, the string is not printed
199 
200     fmt         - Format string
201 
202 Returns:
203 
204     Length of string printed to the StandardError console
205 
206 --*/
207 {
208     SIMPLE_TEXT_OUTPUT_INTERFACE    *DbgOut;
209     PRINT_STATE     ps;
210     va_list         args;
211     UINTN           back;
212     UINTN           attr;
213     UINTN           SavedAttribute;
214 
215 
216     if (!(EFIDebug & mask)) {
217         return 0;
218     }
219 
220     va_start (args, fmt);
221     ZeroMem (&ps, sizeof(ps));
222 
223     ps.Output = _DbgOut;
224     ps.fmt.Ascii = TRUE;
225     ps.fmt.pc = fmt;
226     va_copy(ps.args, args);
227     ps.Attr = EFI_TEXT_ATTR(EFI_LIGHTGRAY, EFI_RED);
228 
229     DbgOut = LibRuntimeDebugOut;
230 
231     if (!DbgOut) {
232         DbgOut = ST->StdErr;
233     }
234 
235     if (DbgOut) {
236         ps.Attr = DbgOut->Mode->Attribute;
237         ps.Context = DbgOut;
238         ps.SetAttr = (INTN (EFIAPI *)(VOID *, UINTN))  DbgOut->SetAttribute;
239     }
240 
241     SavedAttribute = ps.Attr;
242 
243     back = (ps.Attr >> 4) & 0xf;
244     ps.AttrNorm = EFI_TEXT_ATTR(EFI_LIGHTGRAY, back);
245     ps.AttrHighlight = EFI_TEXT_ATTR(EFI_WHITE, back);
246     ps.AttrError = EFI_TEXT_ATTR(EFI_YELLOW, back);
247 
248     attr = ps.AttrNorm;
249 
250     if (mask & D_WARN) {
251         attr = ps.AttrHighlight;
252     }
253 
254     if (mask & D_ERROR) {
255         attr = ps.AttrError;
256     }
257 
258     if (ps.SetAttr) {
259         ps.Attr = attr;
260         uefi_call_wrapper(ps.SetAttr, 2, ps.Context, attr);
261     }
262 
263     _Print (&ps);
264 
265     va_end (ps.args);
266     va_end (args);
267 
268     //
269     // Restore original attributes
270     //
271 
272     if (ps.SetAttr) {
273         uefi_call_wrapper(ps.SetAttr, 2, ps.Context, SavedAttribute);
274     }
275 
276     return 0;
277 }
278 
279 STATIC
280 INTN
281 IsLocalPrint(void *func)
282 {
283 	if (func == _DbgOut || func == _SPrint || func == _PoolPrint)
284 		return 1;
285 	return 0;
286 }
287 
288 STATIC
289 INTN EFIAPI
290 _DbgOut (
291     IN VOID     *Context,
292     IN CHAR16   *Buffer
293     )
294 // Append string worker for DbgPrint
295 {
296     SIMPLE_TEXT_OUTPUT_INTERFACE    *DbgOut;
297 
298     DbgOut = Context;
299 //    if (!DbgOut && ST && ST->ConOut) {
300 //        DbgOut = ST->ConOut;
301 //    }
302 
303     if (DbgOut) {
304 	if (IsLocalPrint(DbgOut->OutputString))
305 		DbgOut->OutputString(DbgOut, Buffer);
306         else
307 		uefi_call_wrapper(DbgOut->OutputString, 2, DbgOut, Buffer);
308     }
309 
310     return 0;
311 }
312 
313 INTN EFIAPI
314 _SPrint (
315     IN VOID     *Context,
316     IN CHAR16   *Buffer
317     )
318 // Append string worker for UnicodeSPrint, PoolPrint and CatPrint
319 {
320     UINTN           len;
321     POOL_PRINT      *spc;
322 
323     spc = Context;
324     len = StrLen(Buffer);
325 
326     //
327     // Is the string is over the max truncate it
328     //
329 
330     if (spc->len + len > spc->maxlen) {
331         len = spc->maxlen - spc->len;
332     }
333 
334     //
335     // Append the new text
336     //
337 
338     CopyMem (spc->str + spc->len, Buffer, len * sizeof(CHAR16));
339     spc->len += len;
340 
341     //
342     // Null terminate it
343     //
344 
345     if (spc->len < spc->maxlen) {
346         spc->str[spc->len] = 0;
347     } else if (spc->maxlen) {
348         spc->str[spc->maxlen] = 0;
349     }
350 
351     return 0;
352 }
353 
354 
355 INTN EFIAPI
356 _PoolPrint (
357     IN VOID     *Context,
358     IN CHAR16   *Buffer
359     )
360 // Append string worker for PoolPrint and CatPrint
361 {
362     UINTN           newlen;
363     POOL_PRINT      *spc;
364 
365     spc = Context;
366     newlen = spc->len + StrLen(Buffer) + 1;
367 
368     //
369     // Is the string is over the max, grow the buffer
370     //
371 
372     if (newlen > spc->maxlen) {
373 
374         //
375         // Grow the pool buffer
376         //
377 
378         newlen += PRINT_STRING_LEN;
379         spc->maxlen = newlen;
380         spc->str = ReallocatePool (
381                         spc->str,
382                         spc->len * sizeof(CHAR16),
383                         spc->maxlen * sizeof(CHAR16)
384                         );
385 
386         if (!spc->str) {
387             spc->len = 0;
388             spc->maxlen = 0;
389         }
390     }
391 
392     //
393     // Append the new text
394     //
395 
396     return _SPrint (Context, Buffer);
397 }
398 
399 
400 
401 VOID
402 _PoolCatPrint (
403     IN CONST CHAR16     *fmt,
404     IN va_list          args,
405     IN OUT POOL_PRINT   *spc,
406     IN INTN             (EFIAPI *Output)(VOID *context, CHAR16 *str)
407     )
408 // Dispatch function for UnicodeSPrint, PoolPrint, and CatPrint
409 {
410     PRINT_STATE         ps;
411 
412     ZeroMem (&ps, sizeof(ps));
413     ps.Output  = Output;
414     ps.Context = spc;
415     ps.fmt.pw = fmt;
416     va_copy(ps.args, args);
417     _Print (&ps);
418     va_end(ps.args);
419 }
420 
421 
422 
423 UINTN
424 UnicodeVSPrint (
425     OUT CHAR16        *Str,
426     IN UINTN          StrSize,
427     IN CONST CHAR16   *fmt,
428     va_list           args
429     )
430 /*++
431 
432 Routine Description:
433 
434     Prints a formatted unicode string to a buffer using a va_list
435 
436 Arguments:
437 
438     Str         - Output buffer to print the formatted string into
439 
440     StrSize     - Size of Str.  String is truncated to this size.
441                   A size of 0 means there is no limit
442 
443     fmt         - The format string
444 
445     args        - va_list
446 
447 
448 Returns:
449 
450     String length returned in buffer
451 
452 --*/
453 {
454     POOL_PRINT          spc;
455 
456     spc.str    = Str;
457     spc.maxlen = StrSize / sizeof(CHAR16) - 1;
458     spc.len    = 0;
459 
460     _PoolCatPrint (fmt, args, &spc, _SPrint);
461 
462     return spc.len;
463 }
464 
465 UINTN
466 UnicodeSPrint (
467     OUT CHAR16        *Str,
468     IN UINTN          StrSize,
469     IN CONST CHAR16   *fmt,
470     ...
471     )
472 /*++
473 
474 Routine Description:
475 
476     Prints a formatted unicode string to a buffer
477 
478 Arguments:
479 
480     Str         - Output buffer to print the formatted string into
481 
482     StrSize     - Size of Str.  String is truncated to this size.
483                   A size of 0 means there is no limit
484 
485     fmt         - The format string
486 
487 Returns:
488 
489     String length returned in buffer
490 
491 --*/
492 {
493     va_list          args;
494     UINTN            len;
495 
496     va_start (args, fmt);
497     len = UnicodeVSPrint(Str, StrSize, fmt, args);
498     va_end (args);
499 
500     return len;
501 }
502 
503 CHAR16 *
504 VPoolPrint (
505     IN CONST CHAR16     *fmt,
506     va_list             args
507     )
508 /*++
509 
510 Routine Description:
511 
512     Prints a formatted unicode string to allocated pool using va_list argument.
513     The caller must free the resulting buffer.
514 
515 Arguments:
516 
517     fmt         - The format string
518     args        - The arguments in va_list form
519 
520 Returns:
521 
522     Allocated buffer with the formatted string printed in it.
523     The caller must free the allocated buffer.   The buffer
524     allocation is not packed.
525 
526 --*/
527 {
528     POOL_PRINT          spc;
529     ZeroMem (&spc, sizeof(spc));
530     _PoolCatPrint (fmt, args, &spc, _PoolPrint);
531     return spc.str;
532 }
533 
534 CHAR16 *
535 PoolPrint (
536     IN CONST CHAR16     *fmt,
537     ...
538     )
539 /*++
540 
541 Routine Description:
542 
543     Prints a formatted unicode string to allocated pool.  The caller
544     must free the resulting buffer.
545 
546 Arguments:
547 
548     fmt         - The format string
549 
550 Returns:
551 
552     Allocated buffer with the formatted string printed in it.
553     The caller must free the allocated buffer.   The buffer
554     allocation is not packed.
555 
556 --*/
557 {
558     va_list args;
559     CHAR16 *pool;
560     va_start (args, fmt);
561     pool = VPoolPrint(fmt, args);
562     va_end (args);
563     return pool;
564 }
565 
566 CHAR16 *
567 CatPrint (
568     IN OUT POOL_PRINT   *Str,
569     IN CONST CHAR16     *fmt,
570     ...
571     )
572 /*++
573 
574 Routine Description:
575 
576     Concatenates a formatted unicode string to allocated pool.
577     The caller must free the resulting buffer.
578 
579 Arguments:
580 
581     Str         - Tracks the allocated pool, size in use, and
582                   amount of pool allocated.
583 
584     fmt         - The format string
585 
586 Returns:
587 
588     Allocated buffer with the formatted string printed in it.
589     The caller must free the allocated buffer.   The buffer
590     allocation is not packed.
591 
592 --*/
593 {
594     va_list             args;
595 
596     va_start (args, fmt);
597     _PoolCatPrint (fmt, args, Str, _PoolPrint);
598     va_end (args);
599     return Str->str;
600 }
601 
602 
603 
604 UINTN
605 Print (
606     IN CONST CHAR16   *fmt,
607     ...
608     )
609 /*++
610 
611 Routine Description:
612 
613     Prints a formatted unicode string to the default console
614 
615 Arguments:
616 
617     fmt         - Format string
618 
619 Returns:
620 
621     Length of string printed to the console
622 
623 --*/
624 {
625     va_list     args;
626     UINTN       back;
627 
628     va_start (args, fmt);
629     back = _IPrint ((UINTN) -1, (UINTN) -1, ST->ConOut, fmt, NULL, args);
630     va_end (args);
631     return back;
632 }
633 
634 UINTN
635 VPrint (
636     IN CONST CHAR16   *fmt,
637     va_list           args
638     )
639 /*++
640 
641 Routine Description:
642 
643     Prints a formatted unicode string to the default console using a va_list
644 
645 Arguments:
646 
647     fmt         - Format string
648     args        - va_list
649 Returns:
650 
651     Length of string printed to the console
652 
653 --*/
654 {
655     return _IPrint ((UINTN) -1, (UINTN) -1, ST->ConOut, fmt, NULL, args);
656 }
657 
658 
659 UINTN
660 PrintAt (
661     IN UINTN          Column,
662     IN UINTN          Row,
663     IN CONST CHAR16   *fmt,
664     ...
665     )
666 /*++
667 
668 Routine Description:
669 
670     Prints a formatted unicode string to the default console, at
671     the supplied cursor position
672 
673 Arguments:
674 
675     Column, Row - The cursor position to print the string at
676 
677     fmt         - Format string
678 
679 Returns:
680 
681     Length of string printed to the console
682 
683 --*/
684 {
685     va_list     args;
686     UINTN       back;
687 
688     va_start (args, fmt);
689     back = _IPrint (Column, Row, ST->ConOut, fmt, NULL, args);
690     va_end (args);
691     return back;
692 }
693 
694 
695 UINTN
696 IPrint (
697     IN SIMPLE_TEXT_OUTPUT_INTERFACE    *Out,
698     IN CONST CHAR16                    *fmt,
699     ...
700     )
701 /*++
702 
703 Routine Description:
704 
705     Prints a formatted unicode string to the specified console
706 
707 Arguments:
708 
709     Out         - The console to print the string too
710 
711     fmt         - Format string
712 
713 Returns:
714 
715     Length of string printed to the console
716 
717 --*/
718 {
719     va_list     args;
720     UINTN       back;
721 
722     va_start (args, fmt);
723     back = _IPrint ((UINTN) -1, (UINTN) -1, Out, fmt, NULL, args);
724     va_end (args);
725     return back;
726 }
727 
728 
729 UINTN
730 IPrintAt (
731     IN SIMPLE_TEXT_OUTPUT_INTERFACE     *Out,
732     IN UINTN                            Column,
733     IN UINTN                            Row,
734     IN CONST CHAR16                     *fmt,
735     ...
736     )
737 /*++
738 
739 Routine Description:
740 
741     Prints a formatted unicode string to the specified console, at
742     the supplied cursor position
743 
744 Arguments:
745 
746     Out         - The console to print the string to
747 
748     Column, Row - The cursor position to print the string at
749 
750     fmt         - Format string
751 
752 Returns:
753 
754     Length of string printed to the console
755 
756 --*/
757 {
758     va_list     args;
759     UINTN       back;
760 
761     va_start (args, fmt);
762     back = _IPrint (Column, Row, Out, fmt, NULL, args);
763     va_end (args);
764     return back;
765 }
766 
767 
768 UINTN
769 _IPrint (
770     IN UINTN                            Column,
771     IN UINTN                            Row,
772     IN SIMPLE_TEXT_OUTPUT_INTERFACE     *Out,
773     IN CONST CHAR16                     *fmt,
774     IN CONST CHAR8                      *fmta,
775     IN va_list                          args
776     )
777 // Display string worker for: Print, PrintAt, IPrint, IPrintAt
778 {
779     PRINT_STATE     ps;
780     UINTN            back;
781 
782     ZeroMem (&ps, sizeof(ps));
783     ps.Context = Out;
784     ps.Output  = (INTN (EFIAPI *)(VOID *, CHAR16 *)) Out->OutputString;
785     ps.SetAttr = (INTN (EFIAPI *)(VOID *, UINTN))  Out->SetAttribute;
786     ps.Attr = Out->Mode->Attribute;
787 
788     back = (ps.Attr >> 4) & 0xF;
789     ps.AttrNorm = EFI_TEXT_ATTR(EFI_LIGHTGRAY, back);
790     ps.AttrHighlight = EFI_TEXT_ATTR(EFI_WHITE, back);
791     ps.AttrError = EFI_TEXT_ATTR(EFI_YELLOW, back);
792 
793     if (fmt) {
794         ps.fmt.pw = fmt;
795     } else {
796         ps.fmt.Ascii = TRUE;
797         ps.fmt.pc = fmta;
798     }
799 
800     va_copy(ps.args, args);
801 
802     if (Column != (UINTN) -1) {
803         uefi_call_wrapper(Out->SetCursorPosition, 3, Out, Column, Row);
804     }
805 
806     back = _Print (&ps);
807     va_end(ps.args);
808     return back;
809 }
810 
811 
812 UINTN
813 AsciiPrint (
814     IN CONST CHAR8    *fmt,
815     ...
816     )
817 /*++
818 
819 Routine Description:
820 
821     For those whom really can't deal with unicode, a print
822     function that takes an ascii format string
823 
824 Arguments:
825 
826     fmt         - ascii format string
827 
828 Returns:
829 
830     Length of string printed to the console
831 
832 --*/
833 
834 {
835     va_list     args;
836     UINTN       back;
837 
838     va_start (args, fmt);
839     back = _IPrint ((UINTN) -1, (UINTN) -1, ST->ConOut, NULL, fmt, args);
840     va_end (args);
841     return back;
842 }
843 
844 
845 UINTN
846 AsciiVSPrint (
847     OUT CHAR8         *Str,
848     IN UINTN          StrSize,
849     IN CONST CHAR8    *fmt,
850     va_list           args
851 )
852 /*++
853 
854 Routine Description:
855 
856     Prints a formatted ascii string to a buffer using a va_list
857 
858 Arguments:
859 
860     Str         - Output buffer to print the formatted string into
861 
862     StrSize     - Size of Str.  String is truncated to this size.
863                   A size of 0 means there is no limit
864 
865     fmt         - The format string
866 
867     args        - va_list
868 
869 
870 Returns:
871 
872     String length returned in buffer
873 
874 --*/
875 // Use UnicodeVSPrint() and convert back to ASCII
876 {
877     CHAR16 *UnicodeStr, *UnicodeFmt;
878     UINTN i, Len;
879 
880     UnicodeStr = AllocatePool(StrSize * sizeof(CHAR16));
881     if (!UnicodeStr)
882         return 0;
883 
884     UnicodeFmt = PoolPrint(L"%a", fmt);
885     if (!UnicodeFmt) {
886         FreePool(UnicodeStr);
887         return 0;
888     }
889 
890     Len = UnicodeVSPrint(UnicodeStr, StrSize, UnicodeFmt, args);
891     FreePool(UnicodeFmt);
892 
893     // The strings are ASCII so just do a plain Unicode conversion
894     for (i = 0; i < Len; i++)
895         Str[i] = (CHAR8)UnicodeStr[i];
896     Str[Len] = 0;
897     FreePool(UnicodeStr);
898 
899     return Len;
900 }
901 
902 
903 STATIC
904 VOID
905 PFLUSH (
906     IN OUT PRINT_STATE     *ps
907     )
908 {
909     *ps->Pos = 0;
910     if (IsLocalPrint(ps->Output))
911 	ps->Output(ps->Context, ps->Buffer);
912     else
913     	uefi_call_wrapper(ps->Output, 2, ps->Context, ps->Buffer);
914     ps->Pos = ps->Buffer;
915 }
916 
917 STATIC
918 VOID
919 PSETATTR (
920     IN OUT PRINT_STATE  *ps,
921     IN UINTN             Attr
922     )
923 {
924    PFLUSH (ps);
925 
926    ps->RestoreAttr = ps->Attr;
927    if (ps->SetAttr) {
928 	uefi_call_wrapper(ps->SetAttr, 2, ps->Context, Attr);
929    }
930 
931    ps->Attr = Attr;
932 }
933 
934 STATIC
935 VOID
936 PPUTC (
937     IN OUT PRINT_STATE     *ps,
938     IN CHAR16              c
939     )
940 {
941     // if this is a newline, add a carraige return
942     if (c == '\n') {
943         PPUTC (ps, '\r');
944     }
945 
946     *ps->Pos = c;
947     ps->Pos += 1;
948     ps->Len += 1;
949 
950     // if at the end of the buffer, flush it
951     if (ps->Pos >= ps->End) {
952         PFLUSH(ps);
953     }
954 }
955 
956 
957 STATIC
958 CHAR16
959 PGETC (
960     IN POINTER      *p
961     )
962 {
963     CHAR16      c;
964 
965     c = p->Ascii ? p->pc[p->Index] : p->pw[p->Index];
966     p->Index += 1;
967 
968     return  c;
969 }
970 
971 
972 STATIC
973 VOID
974 PITEM (
975     IN OUT PRINT_STATE  *ps
976     )
977 {
978     UINTN               Len, i;
979     PRINT_ITEM          *Item;
980     CHAR16              c;
981 
982     // Get the length of the item
983     Item = ps->Item;
984     Item->Item.Index = 0;
985     while (Item->Item.Index < Item->FieldWidth) {
986         c = PGETC(&Item->Item);
987         if (!c) {
988             Item->Item.Index -= 1;
989             break;
990         }
991     }
992     Len = Item->Item.Index;
993 
994     // if there is no item field width, use the items width
995     if (Item->FieldWidth == (UINTN) -1) {
996         Item->FieldWidth = Len;
997     }
998 
999     // if item is larger then width, update width
1000     if (Len > Item->Width) {
1001         Item->Width = Len;
1002     }
1003 
1004 
1005     // if pad field before, add pad char
1006     if (Item->PadBefore) {
1007         for (i=Item->Width; i < Item->FieldWidth; i+=1) {
1008             PPUTC (ps, ' ');
1009         }
1010     }
1011 
1012     // pad item
1013     for (i=Len; i < Item->Width; i++) {
1014         PPUTC (ps, Item->Pad);
1015     }
1016 
1017     // add the item
1018     Item->Item.Index=0;
1019     while (Item->Item.Index < Len) {
1020         PPUTC (ps, PGETC(&Item->Item));
1021     }
1022 
1023     // If pad at the end, add pad char
1024     if (!Item->PadBefore) {
1025         for (i=Item->Width; i < Item->FieldWidth; i+=1) {
1026             PPUTC (ps, ' ');
1027         }
1028     }
1029 }
1030 
1031 
1032 STATIC
1033 UINTN
1034 _Print (
1035     IN PRINT_STATE     *ps
1036     )
1037 /*++
1038 
1039 Routine Description:
1040 
1041     %w.lF   -   w = width
1042                 l = field width
1043                 F = format of arg
1044 
1045   Args F:
1046     0       -   pad with zeros
1047     -       -   justify on left (default is on right)
1048     ,       -   add comma's to field
1049     *       -   width provided on stack
1050     n       -   Set output attribute to normal (for this field only)
1051     h       -   Set output attribute to highlight (for this field only)
1052     e       -   Set output attribute to error (for this field only)
1053     l       -   Value is 64 bits
1054 
1055     a       -   ascii string
1056     s       -   unicode string
1057     X       -   fixed 8 byte value in hex
1058     x       -   hex value
1059     d       -   value as signed decimal
1060     u       -   value as unsigned decimal
1061     f       -   value as floating point
1062     c       -   Unicode char
1063     t       -   EFI time structure
1064     g       -   Pointer to GUID
1065     r       -   EFI status code (result code)
1066     D       -   pointer to Device Path with normal ending.
1067 
1068     N       -   Set output attribute to normal
1069     H       -   Set output attribute to highlight
1070     E       -   Set output attribute to error
1071     %       -   Print a %
1072 
1073 Arguments:
1074 
1075     SystemTable     - The system table
1076 
1077 Returns:
1078 
1079     Number of charactors written
1080 
1081 --*/
1082 {
1083     CHAR16          c;
1084     UINTN           Attr;
1085     PRINT_ITEM      Item;
1086     CHAR16          Buffer[PRINT_STRING_LEN];
1087 
1088     ps->Len = 0;
1089     ps->Buffer = Buffer;
1090     ps->Pos = Buffer;
1091     ps->End = Buffer + PRINT_STRING_LEN - 1;
1092     ps->Item = &Item;
1093 
1094     ps->fmt.Index = 0;
1095     while ((c = PGETC(&ps->fmt))) {
1096 
1097         if (c != '%') {
1098             PPUTC ( ps, c );
1099             continue;
1100         }
1101 
1102         // setup for new item
1103         Item.FieldWidth = (UINTN) -1;
1104         Item.Width = 0;
1105         Item.WidthParse = &Item.Width;
1106         Item.Pad = ' ';
1107         Item.PadBefore = TRUE;
1108         Item.Comma = FALSE;
1109         Item.Long = FALSE;
1110         Item.Item.Ascii = FALSE;
1111         Item.Item.pw = NULL;
1112         ps->RestoreAttr = 0;
1113         Attr = 0;
1114 
1115         while ((c = PGETC(&ps->fmt))) {
1116 
1117             switch (c) {
1118 
1119             case '%':
1120                 //
1121                 // %% -> %
1122                 //
1123                 Item.Scratch[0] = '%';
1124                 Item.Scratch[1] = 0;
1125                 Item.Item.pw = Item.Scratch;
1126                 break;
1127 
1128             case '0':
1129                 Item.Pad = '0';
1130                 break;
1131 
1132             case '-':
1133                 Item.PadBefore = FALSE;
1134                 break;
1135 
1136             case ',':
1137                 Item.Comma = TRUE;
1138                 break;
1139 
1140             case '.':
1141                 Item.WidthParse = &Item.FieldWidth;
1142                 break;
1143 
1144             case '*':
1145                 *Item.WidthParse = va_arg(ps->args, UINTN);
1146                 break;
1147 
1148             case '1':
1149             case '2':
1150             case '3':
1151             case '4':
1152             case '5':
1153             case '6':
1154             case '7':
1155             case '8':
1156             case '9':
1157                 *Item.WidthParse = 0;
1158                 do {
1159                     *Item.WidthParse = *Item.WidthParse * 10 + c - '0';
1160                     c = PGETC(&ps->fmt);
1161                 } while (c >= '0'  &&  c <= '9') ;
1162                 ps->fmt.Index -= 1;
1163                 break;
1164 
1165             case 'a':
1166                 Item.Item.pc = va_arg(ps->args, CHAR8 *);
1167                 Item.Item.Ascii = TRUE;
1168                 if (!Item.Item.pc) {
1169                     Item.Item.pc = (CHAR8 *)"(null)";
1170                 }
1171                 break;
1172 
1173             case 's':
1174                 Item.Item.pw = va_arg(ps->args, CHAR16 *);
1175                 if (!Item.Item.pw) {
1176                     Item.Item.pw = L"(null)";
1177                 }
1178                 break;
1179 
1180             case 'c':
1181                 Item.Scratch[0] = (CHAR16) va_arg(ps->args, UINTN);
1182                 Item.Scratch[1] = 0;
1183                 Item.Item.pw = Item.Scratch;
1184                 break;
1185 
1186             case 'l':
1187                 Item.Long = TRUE;
1188                 break;
1189 
1190             case 'X':
1191                 Item.Width = Item.Long ? 16 : 8;
1192                 Item.Pad = '0';
1193 #if __GNUC__ >= 7
1194 		__attribute__ ((fallthrough));
1195 #endif
1196             case 'x':
1197                 ValueToHex (
1198                     Item.Scratch,
1199                     Item.Long ? va_arg(ps->args, UINT64) : va_arg(ps->args, UINT32)
1200                     );
1201                 Item.Item.pw = Item.Scratch;
1202 
1203                 break;
1204 
1205 
1206             case 'g':
1207                 GuidToString (Item.Scratch, va_arg(ps->args, EFI_GUID *));
1208                 Item.Item.pw = Item.Scratch;
1209                 break;
1210 
1211             case 'u':
1212                 ValueToString (
1213                     Item.Scratch,
1214                     Item.Comma,
1215                     Item.Long ? va_arg(ps->args, UINT64) : va_arg(ps->args, UINT32)
1216                     );
1217                 Item.Item.pw = Item.Scratch;
1218                 break;
1219 
1220             case 'd':
1221                 ValueToString (
1222                     Item.Scratch,
1223                     Item.Comma,
1224                     Item.Long ? va_arg(ps->args, INT64) : va_arg(ps->args, INT32)
1225                     );
1226                 Item.Item.pw = Item.Scratch;
1227                 break;
1228 
1229             case 'D':
1230             {
1231                 EFI_DEVICE_PATH *dp = va_arg(ps->args, EFI_DEVICE_PATH *);
1232                 CHAR16 *dpstr = DevicePathToStr(dp);
1233                 StrnCpy(Item.Scratch, dpstr, PRINT_ITEM_BUFFER_LEN);
1234                 Item.Scratch[PRINT_ITEM_BUFFER_LEN-1] = L'\0';
1235                 FreePool(dpstr);
1236 
1237                 Item.Item.pw = Item.Scratch;
1238                 break;
1239             }
1240 
1241             case 'f':
1242                 FloatToString (
1243                     Item.Scratch,
1244                     Item.Comma,
1245                     va_arg(ps->args, double)
1246                     );
1247                 Item.Item.pw = Item.Scratch;
1248                 break;
1249 
1250             case 't':
1251                 TimeToString (Item.Scratch, va_arg(ps->args, EFI_TIME *));
1252                 Item.Item.pw = Item.Scratch;
1253                 break;
1254 
1255             case 'r':
1256                 StatusToString (Item.Scratch, va_arg(ps->args, EFI_STATUS));
1257                 Item.Item.pw = Item.Scratch;
1258                 break;
1259 
1260             case 'n':
1261                 PSETATTR(ps, ps->AttrNorm);
1262                 break;
1263 
1264             case 'h':
1265                 PSETATTR(ps, ps->AttrHighlight);
1266                 break;
1267 
1268             case 'e':
1269                 PSETATTR(ps, ps->AttrError);
1270                 break;
1271 
1272             case 'N':
1273                 Attr = ps->AttrNorm;
1274                 break;
1275 
1276             case 'H':
1277                 Attr = ps->AttrHighlight;
1278                 break;
1279 
1280             case 'E':
1281                 Attr = ps->AttrError;
1282                 break;
1283 
1284             default:
1285                 Item.Scratch[0] = '?';
1286                 Item.Scratch[1] = 0;
1287                 Item.Item.pw = Item.Scratch;
1288                 break;
1289             }
1290 
1291             // if we have an Item
1292             if (Item.Item.pw) {
1293                 PITEM (ps);
1294                 break;
1295             }
1296 
1297             // if we have an Attr set
1298             if (Attr) {
1299                 PSETATTR(ps, Attr);
1300                 ps->RestoreAttr = 0;
1301                 break;
1302             }
1303         }
1304 
1305         if (ps->RestoreAttr) {
1306             PSETATTR(ps, ps->RestoreAttr);
1307         }
1308     }
1309 
1310     // Flush buffer
1311     PFLUSH (ps);
1312     return ps->Len;
1313 }
1314 
1315 STATIC CHAR8 Hex[] = {'0','1','2','3','4','5','6','7',
1316                       '8','9','A','B','C','D','E','F'};
1317 
1318 VOID
1319 ValueToHex (
1320     IN CHAR16   *Buffer,
1321     IN UINT64   v
1322     )
1323 {
1324     CHAR8           str[30], *p1;
1325     CHAR16          *p2;
1326 
1327     if (!v) {
1328         Buffer[0] = '0';
1329         Buffer[1] = 0;
1330         return ;
1331     }
1332 
1333     p1 = str;
1334     p2 = Buffer;
1335 
1336     while (v) {
1337         // Without the cast, the MSVC compiler may insert a reference to __allmull
1338         *(p1++) = Hex[(UINTN)(v & 0xf)];
1339         v = RShiftU64 (v, 4);
1340     }
1341 
1342     while (p1 != str) {
1343         *(p2++) = *(--p1);
1344     }
1345     *p2 = 0;
1346 }
1347 
1348 
1349 VOID
1350 ValueToString (
1351     IN CHAR16   *Buffer,
1352     IN BOOLEAN  Comma,
1353     IN INT64    v
1354     )
1355 {
1356     STATIC CHAR8 ca[] = {  3, 1, 2 };
1357     CHAR8        str[40], *p1;
1358     CHAR16       *p2;
1359     UINTN        c, r;
1360 
1361     if (!v) {
1362         Buffer[0] = '0';
1363         Buffer[1] = 0;
1364         return ;
1365     }
1366 
1367     p1 = str;
1368     p2 = Buffer;
1369 
1370     if (v < 0) {
1371         *(p2++) = '-';
1372         v = -v;
1373     }
1374 
1375     while (v) {
1376         v = (INT64)DivU64x32 ((UINT64)v, 10, &r);
1377         *(p1++) = (CHAR8)r + '0';
1378     }
1379 
1380     c = (UINTN) (Comma ? ca[(p1 - str) % 3] : 999) + 1;
1381     while (p1 != str) {
1382 
1383         c -= 1;
1384         if (!c) {
1385             *(p2++) = ',';
1386             c = 3;
1387         }
1388 
1389         *(p2++) = *(--p1);
1390     }
1391     *p2 = 0;
1392 }
1393 
1394 VOID
1395 FloatToString (
1396     IN CHAR16   *Buffer,
1397     IN BOOLEAN  Comma,
1398     IN double   v
1399     )
1400 {
1401     /*
1402      * Integer part.
1403      */
1404     INTN i = (INTN)v;
1405     ValueToString(Buffer, Comma, i);
1406 
1407 
1408     /*
1409      * Decimal point.
1410      */
1411     UINTN x = StrLen(Buffer);
1412     Buffer[x] = L'.';
1413     x++;
1414 
1415 
1416     /*
1417      * Keep fractional part.
1418      */
1419     float f = (float)(v - i);
1420     if (f < 0) f = -f;
1421 
1422 
1423     /*
1424      * Leading fractional zeroes.
1425      */
1426     f *= 10.0;
1427     while (   (f != 0)
1428            && ((INTN)f == 0))
1429     {
1430       Buffer[x] = L'0';
1431       x++;
1432       f *= 10.0;
1433     }
1434 
1435 
1436     /*
1437      * Fractional digits.
1438      */
1439     while ((float)(INTN)f != f)
1440     {
1441       f *= 10;
1442     }
1443     ValueToString(Buffer + x, FALSE, (INTN)f);
1444     return;
1445 }
1446 
1447 VOID
1448 TimeToString (
1449     OUT CHAR16      *Buffer,
1450     IN EFI_TIME     *Time
1451     )
1452 {
1453     UINTN       Hour, Year;
1454     CHAR16      AmPm;
1455 
1456     AmPm = 'a';
1457     Hour = Time->Hour;
1458     if (Time->Hour == 0) {
1459         Hour = 12;
1460     } else if (Time->Hour >= 12) {
1461         AmPm = 'p';
1462         if (Time->Hour >= 13) {
1463             Hour -= 12;
1464         }
1465     }
1466 
1467     Year = Time->Year % 100;
1468 
1469     // bugbug: for now just print it any old way
1470     UnicodeSPrint (Buffer, 0, L"%02d/%02d/%02d  %02d:%02d%c",
1471         Time->Month,
1472         Time->Day,
1473         Year,
1474         Hour,
1475         Time->Minute,
1476         AmPm
1477         );
1478 }
1479 
1480 
1481 
1482 
1483 VOID
1484 DumpHex (
1485     IN UINTN        Indent,
1486     IN UINTN        Offset,
1487     IN UINTN        DataSize,
1488     IN VOID         *UserData
1489     )
1490 {
1491     CHAR8           *Data, Val[50], Str[20], c;
1492     UINTN           Size, Index;
1493 
1494     UINTN           ScreenCount;
1495     UINTN           TempColumn;
1496     UINTN           ScreenSize;
1497     CHAR16          ReturnStr[1];
1498 
1499 
1500     uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &TempColumn, &ScreenSize);
1501     ScreenCount = 0;
1502     ScreenSize -= 2;
1503 
1504     Data = UserData;
1505     while (DataSize) {
1506         Size = 16;
1507         if (Size > DataSize) {
1508             Size = DataSize;
1509         }
1510 
1511         for (Index=0; Index < Size; Index += 1) {
1512             c = Data[Index];
1513             Val[Index*3+0] = Hex[c>>4];
1514             Val[Index*3+1] = Hex[c&0xF];
1515             Val[Index*3+2] = (Index == 7)?'-':' ';
1516             Str[Index] = (c < ' ' || c > 'z') ? '.' : c;
1517         }
1518 
1519         Val[Index*3] = 0;
1520         Str[Index] = 0;
1521         Print (L"%*a%X: %-.48a *%a*\n", Indent, "", Offset, Val, Str);
1522 
1523         Data += Size;
1524         Offset += Size;
1525         DataSize -= Size;
1526 
1527         ScreenCount++;
1528         if (ScreenCount >= ScreenSize && ScreenSize != 0) {
1529             //
1530             // If ScreenSize == 0 we have the console redirected so don't
1531             //  block updates
1532             //
1533             ScreenCount = 0;
1534             Print (L"Press Enter to continue :");
1535             Input (L"", ReturnStr, sizeof(ReturnStr)/sizeof(CHAR16));
1536             Print (L"\n");
1537         }
1538 
1539     }
1540 }
1541