printfmt.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       printfmt.c (3746B)
       ---
            1 // Stripped-down primitive printf-style formatting routines,
            2 // used in common by printf, sprintf, fprintf, etc.
            3 // This code is also used by both the kernel and user programs.
            4 //
            5 // Space or zero padding and a field width are supported for the numeric
            6 // formats only. 
            7 
            8 
            9 #include <stdio.h>
           10 #include <stdarg.h>
           11 #include <inttypes.h>
           12 
           13 #include "ioprivate.h"
           14 
           15 
           16 typedef unsigned char                uchar;
           17 typedef unsigned long long        ull;
           18 
           19 /*
           20  * Print a number (base <= 16) in reverse order,
           21  * using specified putch function and associated pointer putdat.
           22  */
           23 static int
           24 printnum(int (*putch)(int, void*), void *putdat,
           25          ull num, unsigned base, int width, int padc)
           26 {
           27         int nout;
           28 
           29         // first recursively print all preceding (more significant) digits
           30         if (num >= base) {
           31                 nout = printnum(putch, putdat, num / base, base, width-1,
           32                                 padc);
           33                 if (nout < 0)
           34                         return -1;
           35         } else {
           36                 // print any needed pad characters before first digit
           37                 nout = 0;
           38                 while (--width > 0) {
           39                         if (putch(padc, putdat) < 0)
           40                                 return -1;
           41                         nout++;
           42                 }
           43         }
           44 
           45         // then print this (the least significant) digit
           46         if (putch("0123456789abcdef"[num % base], putdat) < 0)
           47                 return -1;
           48         nout++;
           49 
           50         return nout;
           51 }
           52 
           53 // Main function to format and print a string.
           54 int
           55 vprintfmt(int (*putch)(int, void*), void *putdat, const char *fmt, va_list ap)
           56 {
           57         register char *p;
           58         register int ch;
           59         int nout = 0;
           60 
           61         for (;;) {
           62                 while ((ch = *(uchar *) fmt++) != '%') {
           63                         if (ch == '\0')
           64                                 return nout;
           65                         if (putch(ch, putdat) < 0)
           66                                 return -1;
           67                         nout++;
           68                 }
           69 
           70                 // Process a %-escape sequence
           71                 int padc = ' ';
           72                 int width = 0;
           73                 int length = 0;
           74                 int base = 10;
           75                 ull num;
           76 
           77         reswitch:
           78                 switch (ch = *(uchar *) fmt++) {
           79 
           80                 // flag to pad with 0's instead of spaces
           81                 case '0':
           82                         padc = '0';
           83                         goto reswitch;
           84 
           85                 // width field
           86                 case '1':
           87                 case '2':
           88                 case '3':
           89                 case '4':
           90                 case '5':
           91                 case '6':
           92                 case '7':
           93                 case '8':
           94                 case '9':
           95                         for (width = 0;; ++fmt) {
           96                                 width = width * 10 + ch - '0';
           97                                 ch = *fmt;
           98                                 if (ch < '0' || ch > '9')
           99                                         break;
          100                         }
          101                         goto reswitch;
          102 
          103                 case 'l':
          104                         length++;
          105                         goto reswitch;
          106 
          107                 // character
          108                 case 'c':
          109                         if (putch(va_arg(ap, int), putdat) < 0)
          110                                 return -1;
          111                         nout++;
          112                         break;
          113 
          114                 // string
          115                 case 's':
          116                         if ((p = va_arg(ap, char *)) == NULL)
          117                                         p = "(null)";
          118                         while ((ch = *p++) != '\0') {
          119                                 if (putch(ch, putdat) < 0)
          120                                         return -1;
          121                                 nout++;
          122                         }
          123                         break;
          124 
          125                 // signed decimal
          126                 case 'd': ;
          127                         // Pick up an appropriate-sized signed input value
          128                         long long snum;
          129                         if (length == 0)
          130                                 snum = va_arg(ap, int);
          131                         else if (length == 1)
          132                                 snum = va_arg(ap, long);
          133                         else
          134                                 snum = va_arg(ap, long long);
          135 
          136                         // Output the minus sign if appropriate
          137                         if (snum < 0) {
          138                                 if (putch('-', putdat) < 0)
          139                                         return -1;
          140                                 nout++;
          141                                 num = -snum;
          142                         } else
          143                                 num = snum;
          144                         goto number;
          145 
          146                 // unsigned hexadecimal
          147                 case 'x':
          148                         base = 16;
          149                         // fall thru...
          150 
          151                 // unsigned decimal
          152                 case 'u':
          153                         // Pick up the appropriate-sized input argument
          154                         if (length == 0)
          155                                 num = va_arg(ap, unsigned);
          156                         else if (length == 1)
          157                                 num = va_arg(ap, unsigned long);
          158                         else
          159                                 num = va_arg(ap, unsigned long long);
          160 
          161                 number: ;
          162                         // Print the number in the appropriate base
          163                         int rc = printnum(putch, putdat, num, base, width,
          164                                                 padc);
          165                         if (rc < 0)
          166                                 return -1;
          167                         nout += rc;
          168                         break;
          169 
          170                 // unrecognized escape sequence - just print it literally
          171                 default:
          172                         if (putch('%', putdat) < 0)
          173                                 return -1;
          174                         nout++;
          175                         /* FALLTHROUGH */
          176 
          177                 // escaped '%' character
          178                 case '%':
          179                         if (putch(ch, putdat) < 0)
          180                                 return -1;
          181                         nout++;
          182                 }
          183         }
          184 }
          185 
          186 int
          187 printfmt(int (*putch)(int, void*), void *putdat, const char *fmt, ...)
          188 {
          189         va_list ap;
          190         va_start(ap, fmt);
          191         int rc = vprintfmt(putch, putdat, fmt, ap);
          192         va_end(ap);
          193         return rc;
          194 }
          195