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