Home | History | Annotate | Line # | Download | only in lib
      1 /*	$NetBSD: print.c,v 1.5 2021/09/30 19:02:48 jmcneill 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 UnicodeSPrint, 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 // Dispatch function for UnicodeSPrint, 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 UnicodeVSPrint (
    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 UnicodeSPrint (
    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 = UnicodeVSPrint(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 AsciiPrint (
    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 UINTN
    848 AsciiVSPrint (
    849     OUT CHAR8         *Str,
    850     IN UINTN          StrSize,
    851     IN CONST CHAR8    *fmt,
    852     va_list           args
    853 )
    854 /*++
    855 
    856 Routine Description:
    857 
    858     Prints a formatted ascii string to a buffer using a va_list
    859 
    860 Arguments:
    861 
    862     Str         - Output buffer to print the formatted string into
    863 
    864     StrSize     - Size of Str.  String is truncated to this size.
    865                   A size of 0 means there is no limit
    866 
    867     fmt         - The format string
    868 
    869     args        - va_list
    870 
    871 
    872 Returns:
    873 
    874     String length returned in buffer
    875 
    876 --*/
    877 // Use UnicodeVSPrint() and convert back to ASCII
    878 {
    879     CHAR16 *UnicodeStr, *UnicodeFmt;
    880     UINTN i, Len;
    881 
    882     UnicodeStr = AllocatePool(StrSize * sizeof(CHAR16));
    883     if (!UnicodeStr)
    884         return 0;
    885 
    886     UnicodeFmt = PoolPrint(L"%a", fmt);
    887     if (!UnicodeFmt) {
    888         FreePool(UnicodeStr);
    889         return 0;
    890     }
    891 
    892     Len = UnicodeVSPrint(UnicodeStr, StrSize, UnicodeFmt, args);
    893     FreePool(UnicodeFmt);
    894 
    895     // The strings are ASCII so just do a plain Unicode conversion
    896     for (i = 0; i < Len; i++)
    897         Str[i] = (CHAR8)UnicodeStr[i];
    898     Str[Len] = 0;
    899     FreePool(UnicodeStr);
    900 
    901     return Len;
    902 }
    903 
    904 
    905 STATIC
    906 VOID
    907 PFLUSH (
    908     IN OUT PRINT_STATE     *ps
    909     )
    910 {
    911     *ps->Pos = 0;
    912     if (IsLocalPrint(ps->Output))
    913 	ps->Output(ps->Context, ps->Buffer);
    914     else
    915     	uefi_call_wrapper(ps->Output, 2, ps->Context, ps->Buffer);
    916     ps->Pos = ps->Buffer;
    917 }
    918 
    919 STATIC
    920 VOID
    921 PSETATTR (
    922     IN OUT PRINT_STATE  *ps,
    923     IN UINTN             Attr
    924     )
    925 {
    926    PFLUSH (ps);
    927 
    928    ps->RestoreAttr = ps->Attr;
    929    if (ps->SetAttr) {
    930 	uefi_call_wrapper(ps->SetAttr, 2, ps->Context, Attr);
    931    }
    932 
    933    ps->Attr = Attr;
    934 }
    935 
    936 STATIC
    937 VOID
    938 PPUTC (
    939     IN OUT PRINT_STATE     *ps,
    940     IN CHAR16              c
    941     )
    942 {
    943     // if this is a newline, add a carraige return
    944     if (c == '\n') {
    945         PPUTC (ps, '\r');
    946     }
    947 
    948     *ps->Pos = c;
    949     ps->Pos += 1;
    950     ps->Len += 1;
    951 
    952     // if at the end of the buffer, flush it
    953     if (ps->Pos >= ps->End) {
    954         PFLUSH(ps);
    955     }
    956 }
    957 
    958 
    959 STATIC
    960 CHAR16
    961 PGETC (
    962     IN POINTER      *p
    963     )
    964 {
    965     CHAR16      c;
    966 
    967     c = p->Ascii ? p->pc[p->Index] : p->pw[p->Index];
    968     p->Index += 1;
    969 
    970     return  c;
    971 }
    972 
    973 
    974 STATIC
    975 VOID
    976 PITEM (
    977     IN OUT PRINT_STATE  *ps
    978     )
    979 {
    980     UINTN               Len, i;
    981     PRINT_ITEM          *Item;
    982     CHAR16              c;
    983 
    984     // Get the length of the item
    985     Item = ps->Item;
    986     Item->Item.Index = 0;
    987     while (Item->Item.Index < Item->FieldWidth) {
    988         c = PGETC(&Item->Item);
    989         if (!c) {
    990             Item->Item.Index -= 1;
    991             break;
    992         }
    993     }
    994     Len = Item->Item.Index;
    995 
    996     // if there is no item field width, use the items width
    997     if (Item->FieldWidth == (UINTN) -1) {
    998         Item->FieldWidth = Len;
    999     }
   1000 
   1001     // if item is larger then width, update width
   1002     if (Len > Item->Width) {
   1003         Item->Width = Len;
   1004     }
   1005 
   1006 
   1007     // if pad field before, add pad char
   1008     if (Item->PadBefore) {
   1009         for (i=Item->Width; i < Item->FieldWidth; i+=1) {
   1010             PPUTC (ps, ' ');
   1011         }
   1012     }
   1013 
   1014     // pad item
   1015     for (i=Len; i < Item->Width; i++) {
   1016         PPUTC (ps, Item->Pad);
   1017     }
   1018 
   1019     // add the item
   1020     Item->Item.Index=0;
   1021     while (Item->Item.Index < Len) {
   1022         PPUTC (ps, PGETC(&Item->Item));
   1023     }
   1024 
   1025     // If pad at the end, add pad char
   1026     if (!Item->PadBefore) {
   1027         for (i=Item->Width; i < Item->FieldWidth; i+=1) {
   1028             PPUTC (ps, ' ');
   1029         }
   1030     }
   1031 }
   1032 
   1033 
   1034 STATIC
   1035 UINTN
   1036 _Print (
   1037     IN PRINT_STATE     *ps
   1038     )
   1039 /*++
   1040 
   1041 Routine Description:
   1042 
   1043     %w.lF   -   w = width
   1044                 l = field width
   1045                 F = format of arg
   1046 
   1047   Args F:
   1048     0       -   pad with zeros
   1049     -       -   justify on left (default is on right)
   1050     ,       -   add comma's to field
   1051     *       -   width provided on stack
   1052     n       -   Set output attribute to normal (for this field only)
   1053     h       -   Set output attribute to highlight (for this field only)
   1054     e       -   Set output attribute to error (for this field only)
   1055     l       -   Value is 64 bits
   1056 
   1057     a       -   ascii string
   1058     s       -   unicode string
   1059     X       -   fixed 8 byte value in hex
   1060     x       -   hex value
   1061     d       -   value as signed decimal
   1062     u       -   value as unsigned decimal
   1063     f       -   value as floating point
   1064     c       -   Unicode char
   1065     t       -   EFI time structure
   1066     g       -   Pointer to GUID
   1067     r       -   EFI status code (result code)
   1068     D       -   pointer to Device Path with normal ending.
   1069 
   1070     N       -   Set output attribute to normal
   1071     H       -   Set output attribute to highlight
   1072     E       -   Set output attribute to error
   1073     %       -   Print a %
   1074 
   1075 Arguments:
   1076 
   1077     SystemTable     - The system table
   1078 
   1079 Returns:
   1080 
   1081     Number of charactors written
   1082 
   1083 --*/
   1084 {
   1085     CHAR16          c;
   1086     UINTN           Attr;
   1087     PRINT_ITEM      Item;
   1088     CHAR16          Buffer[PRINT_STRING_LEN];
   1089 
   1090     ps->Len = 0;
   1091     ps->Buffer = Buffer;
   1092     ps->Pos = Buffer;
   1093     ps->End = Buffer + PRINT_STRING_LEN - 1;
   1094     ps->Item = &Item;
   1095 
   1096     ps->fmt.Index = 0;
   1097     while ((c = PGETC(&ps->fmt))) {
   1098 
   1099         if (c != '%') {
   1100             PPUTC ( ps, c );
   1101             continue;
   1102         }
   1103 
   1104         // setup for new item
   1105         Item.FieldWidth = (UINTN) -1;
   1106         Item.Width = 0;
   1107         Item.WidthParse = &Item.Width;
   1108         Item.Pad = ' ';
   1109         Item.PadBefore = TRUE;
   1110         Item.Comma = FALSE;
   1111         Item.Long = FALSE;
   1112         Item.Item.Ascii = FALSE;
   1113         Item.Item.pw = NULL;
   1114         ps->RestoreAttr = 0;
   1115         Attr = 0;
   1116 
   1117         while ((c = PGETC(&ps->fmt))) {
   1118 
   1119             switch (c) {
   1120 
   1121             case '%':
   1122                 //
   1123                 // %% -> %
   1124                 //
   1125                 Item.Scratch[0] = '%';
   1126                 Item.Scratch[1] = 0;
   1127                 Item.Item.pw = Item.Scratch;
   1128                 break;
   1129 
   1130             case '0':
   1131                 Item.Pad = '0';
   1132                 break;
   1133 
   1134             case '-':
   1135                 Item.PadBefore = FALSE;
   1136                 break;
   1137 
   1138             case ',':
   1139                 Item.Comma = TRUE;
   1140                 break;
   1141 
   1142             case '.':
   1143                 Item.WidthParse = &Item.FieldWidth;
   1144                 break;
   1145 
   1146             case '*':
   1147                 *Item.WidthParse = va_arg(ps->args, UINTN);
   1148                 break;
   1149 
   1150             case '1':
   1151             case '2':
   1152             case '3':
   1153             case '4':
   1154             case '5':
   1155             case '6':
   1156             case '7':
   1157             case '8':
   1158             case '9':
   1159                 *Item.WidthParse = 0;
   1160                 do {
   1161                     *Item.WidthParse = *Item.WidthParse * 10 + c - '0';
   1162                     c = PGETC(&ps->fmt);
   1163                 } while (c >= '0'  &&  c <= '9') ;
   1164                 ps->fmt.Index -= 1;
   1165                 break;
   1166 
   1167             case 'a':
   1168                 Item.Item.pc = va_arg(ps->args, CHAR8 *);
   1169                 Item.Item.Ascii = TRUE;
   1170                 if (!Item.Item.pc) {
   1171                     Item.Item.pc = (CHAR8 *)"(null)";
   1172                 }
   1173                 break;
   1174 
   1175             case 's':
   1176                 Item.Item.pw = va_arg(ps->args, CHAR16 *);
   1177                 if (!Item.Item.pw) {
   1178                     Item.Item.pw = L"(null)";
   1179                 }
   1180                 break;
   1181 
   1182             case 'c':
   1183                 Item.Scratch[0] = (CHAR16) va_arg(ps->args, UINTN);
   1184                 Item.Scratch[1] = 0;
   1185                 Item.Item.pw = Item.Scratch;
   1186                 break;
   1187 
   1188             case 'l':
   1189                 Item.Long = TRUE;
   1190                 break;
   1191 
   1192             case 'X':
   1193                 Item.Width = Item.Long ? 16 : 8;
   1194                 Item.Pad = '0';
   1195 #if __GNUC__ >= 7
   1196 		__attribute__ ((fallthrough));
   1197 #endif
   1198             case 'x':
   1199                 ValueToHex (
   1200                     Item.Scratch,
   1201                     Item.Long ? va_arg(ps->args, UINT64) : va_arg(ps->args, UINT32)
   1202                     );
   1203                 Item.Item.pw = Item.Scratch;
   1204 
   1205                 break;
   1206 
   1207 
   1208             case 'g':
   1209                 GuidToString (Item.Scratch, va_arg(ps->args, EFI_GUID *));
   1210                 Item.Item.pw = Item.Scratch;
   1211                 break;
   1212 
   1213             case 'u':
   1214                 ValueToString (
   1215                     Item.Scratch,
   1216                     Item.Comma,
   1217                     Item.Long ? va_arg(ps->args, UINT64) : va_arg(ps->args, UINT32)
   1218                     );
   1219                 Item.Item.pw = Item.Scratch;
   1220                 break;
   1221 
   1222             case 'd':
   1223                 ValueToString (
   1224                     Item.Scratch,
   1225                     Item.Comma,
   1226                     Item.Long ? va_arg(ps->args, INT64) : va_arg(ps->args, INT32)
   1227                     );
   1228                 Item.Item.pw = Item.Scratch;
   1229                 break;
   1230 
   1231             case 'D':
   1232             {
   1233                 EFI_DEVICE_PATH *dp = va_arg(ps->args, EFI_DEVICE_PATH *);
   1234                 CHAR16 *dpstr = DevicePathToStr(dp);
   1235                 StrnCpy(Item.Scratch, dpstr, PRINT_ITEM_BUFFER_LEN);
   1236                 Item.Scratch[PRINT_ITEM_BUFFER_LEN-1] = L'\0';
   1237                 FreePool(dpstr);
   1238 
   1239                 Item.Item.pw = Item.Scratch;
   1240                 break;
   1241             }
   1242 
   1243 #ifndef __NetBSD__
   1244             case 'f':
   1245                 FloatToString (
   1246                     Item.Scratch,
   1247                     Item.Comma,
   1248                     va_arg(ps->args, double)
   1249                     );
   1250                 Item.Item.pw = Item.Scratch;
   1251                 break;
   1252 #endif
   1253 
   1254             case 't':
   1255                 TimeToString (Item.Scratch, va_arg(ps->args, EFI_TIME *));
   1256                 Item.Item.pw = Item.Scratch;
   1257                 break;
   1258 
   1259             case 'r':
   1260                 StatusToString (Item.Scratch, va_arg(ps->args, EFI_STATUS));
   1261                 Item.Item.pw = Item.Scratch;
   1262                 break;
   1263 
   1264             case 'n':
   1265                 PSETATTR(ps, ps->AttrNorm);
   1266                 break;
   1267 
   1268             case 'h':
   1269                 PSETATTR(ps, ps->AttrHighlight);
   1270                 break;
   1271 
   1272             case 'e':
   1273                 PSETATTR(ps, ps->AttrError);
   1274                 break;
   1275 
   1276             case 'N':
   1277                 Attr = ps->AttrNorm;
   1278                 break;
   1279 
   1280             case 'H':
   1281                 Attr = ps->AttrHighlight;
   1282                 break;
   1283 
   1284             case 'E':
   1285                 Attr = ps->AttrError;
   1286                 break;
   1287 
   1288             default:
   1289                 Item.Scratch[0] = '?';
   1290                 Item.Scratch[1] = 0;
   1291                 Item.Item.pw = Item.Scratch;
   1292                 break;
   1293             }
   1294 
   1295             // if we have an Item
   1296             if (Item.Item.pw) {
   1297                 PITEM (ps);
   1298                 break;
   1299             }
   1300 
   1301             // if we have an Attr set
   1302             if (Attr) {
   1303                 PSETATTR(ps, Attr);
   1304                 ps->RestoreAttr = 0;
   1305                 break;
   1306             }
   1307         }
   1308 
   1309         if (ps->RestoreAttr) {
   1310             PSETATTR(ps, ps->RestoreAttr);
   1311         }
   1312     }
   1313 
   1314     // Flush buffer
   1315     PFLUSH (ps);
   1316     return ps->Len;
   1317 }
   1318 
   1319 STATIC CHAR8 Hex[] = {'0','1','2','3','4','5','6','7',
   1320                       '8','9','A','B','C','D','E','F'};
   1321 
   1322 VOID
   1323 ValueToHex (
   1324     IN CHAR16   *Buffer,
   1325     IN UINT64   v
   1326     )
   1327 {
   1328     CHAR8           str[30], *p1;
   1329     CHAR16          *p2;
   1330 
   1331     if (!v) {
   1332         Buffer[0] = '0';
   1333         Buffer[1] = 0;
   1334         return ;
   1335     }
   1336 
   1337     p1 = str;
   1338     p2 = Buffer;
   1339 
   1340     while (v) {
   1341         // Without the cast, the MSVC compiler may insert a reference to __allmull
   1342         *(p1++) = Hex[(UINTN)(v & 0xf)];
   1343         v = RShiftU64 (v, 4);
   1344     }
   1345 
   1346     while (p1 != str) {
   1347         *(p2++) = *(--p1);
   1348     }
   1349     *p2 = 0;
   1350 }
   1351 
   1352 
   1353 VOID
   1354 ValueToString (
   1355     IN CHAR16   *Buffer,
   1356     IN BOOLEAN  Comma,
   1357     IN INT64    v
   1358     )
   1359 {
   1360     STATIC CHAR8 ca[] = {  3, 1, 2 };
   1361     CHAR8        str[40], *p1;
   1362     CHAR16       *p2;
   1363     UINTN        c, r;
   1364 
   1365     if (!v) {
   1366         Buffer[0] = '0';
   1367         Buffer[1] = 0;
   1368         return ;
   1369     }
   1370 
   1371     p1 = str;
   1372     p2 = Buffer;
   1373 
   1374     if (v < 0) {
   1375         *(p2++) = '-';
   1376         v = -v;
   1377     }
   1378 
   1379     while (v) {
   1380         v = (INT64)DivU64x32 ((UINT64)v, 10, &r);
   1381         *(p1++) = (CHAR8)r + '0';
   1382     }
   1383 
   1384     c = (UINTN) (Comma ? ca[(p1 - str) % 3] : 999) + 1;
   1385     while (p1 != str) {
   1386 
   1387         c -= 1;
   1388         if (!c) {
   1389             *(p2++) = ',';
   1390             c = 3;
   1391         }
   1392 
   1393         *(p2++) = *(--p1);
   1394     }
   1395     *p2 = 0;
   1396 }
   1397 
   1398 #ifndef __NetBSD__
   1399 VOID
   1400 FloatToString (
   1401     IN CHAR16   *Buffer,
   1402     IN BOOLEAN  Comma,
   1403     IN double   v
   1404     )
   1405 {
   1406     /*
   1407      * Integer part.
   1408      */
   1409     INTN i = (INTN)v;
   1410     ValueToString(Buffer, Comma, i);
   1411 
   1412 
   1413     /*
   1414      * Decimal point.
   1415      */
   1416     UINTN x = StrLen(Buffer);
   1417     Buffer[x] = L'.';
   1418     x++;
   1419 
   1420 
   1421     /*
   1422      * Keep fractional part.
   1423      */
   1424     float f = (float)(v - i);
   1425     if (f < 0) f = -f;
   1426 
   1427 
   1428     /*
   1429      * Leading fractional zeroes.
   1430      */
   1431     f *= 10.0;
   1432     while (   (f != 0)
   1433            && ((INTN)f == 0))
   1434     {
   1435       Buffer[x] = L'0';
   1436       x++;
   1437       f *= 10.0;
   1438     }
   1439 
   1440 
   1441     /*
   1442      * Fractional digits.
   1443      */
   1444     while ((float)(INTN)f != f)
   1445     {
   1446       f *= 10;
   1447     }
   1448     ValueToString(Buffer + x, FALSE, (INTN)f);
   1449     return;
   1450 }
   1451 #endif
   1452 
   1453 VOID
   1454 TimeToString (
   1455     OUT CHAR16      *Buffer,
   1456     IN EFI_TIME     *Time
   1457     )
   1458 {
   1459     UINTN       Hour, Year;
   1460     CHAR16      AmPm;
   1461 
   1462     AmPm = 'a';
   1463     Hour = Time->Hour;
   1464     if (Time->Hour == 0) {
   1465         Hour = 12;
   1466     } else if (Time->Hour >= 12) {
   1467         AmPm = 'p';
   1468         if (Time->Hour >= 13) {
   1469             Hour -= 12;
   1470         }
   1471     }
   1472 
   1473     Year = Time->Year % 100;
   1474 
   1475     // bugbug: for now just print it any old way
   1476     UnicodeSPrint (Buffer, 0, L"%02d/%02d/%02d  %02d:%02d%c",
   1477         Time->Month,
   1478         Time->Day,
   1479         Year,
   1480         Hour,
   1481         Time->Minute,
   1482         AmPm
   1483         );
   1484 }
   1485 
   1486 
   1487 
   1488 
   1489 VOID
   1490 DumpHex (
   1491     IN UINTN        Indent,
   1492     IN UINTN        Offset,
   1493     IN UINTN        DataSize,
   1494     IN VOID         *UserData
   1495     )
   1496 {
   1497     CHAR8           *Data, Val[50], Str[20], c;
   1498     UINTN           Size, Index;
   1499 
   1500     UINTN           ScreenCount;
   1501     UINTN           TempColumn;
   1502     UINTN           ScreenSize;
   1503     CHAR16          ReturnStr[1];
   1504 
   1505 
   1506     uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &TempColumn, &ScreenSize);
   1507     ScreenCount = 0;
   1508     ScreenSize -= 2;
   1509 
   1510     Data = UserData;
   1511     while (DataSize) {
   1512         Size = 16;
   1513         if (Size > DataSize) {
   1514             Size = DataSize;
   1515         }
   1516 
   1517         for (Index=0; Index < Size; Index += 1) {
   1518             c = Data[Index];
   1519             Val[Index*3+0] = Hex[c>>4];
   1520             Val[Index*3+1] = Hex[c&0xF];
   1521             Val[Index*3+2] = (Index == 7)?'-':' ';
   1522             Str[Index] = (c < ' ' || c > 'z') ? '.' : c;
   1523         }
   1524 
   1525         Val[Index*3] = 0;
   1526         Str[Index] = 0;
   1527         Print (L"%*a%X: %-.48a *%a*\n", Indent, "", Offset, Val, Str);
   1528 
   1529         Data += Size;
   1530         Offset += Size;
   1531         DataSize -= Size;
   1532 
   1533         ScreenCount++;
   1534         if (ScreenCount >= ScreenSize && ScreenSize != 0) {
   1535             //
   1536             // If ScreenSize == 0 we have the console redirected so don't
   1537             //  block updates
   1538             //
   1539             ScreenCount = 0;
   1540             Print (L"Press Enter to continue :");
   1541             Input (L"", ReturnStr, sizeof(ReturnStr)/sizeof(CHAR16));
   1542             Print (L"\n");
   1543         }
   1544 
   1545     }
   1546 }
   1547