vfprintf.c - scc - simple c99 compiler
 (HTM) git clone git://git.simple-cc.org/scc
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
       vfprintf.c (6547B)
       ---
            1 #include <ctype.h>
            2 #include <limits.h>
            3 #include <stdarg.h>
            4 #include <stdint.h>
            5 #include <stdio.h>
            6 #include <string.h>
            7 #include <wchar.h>
            8 
            9 #include "../libc.h"
           10 
           11 #define MAXPREC    50
           12 
           13 #undef vfprintf
           14 
           15 enum {
           16         LONG     = 1 << 0,
           17         LLONG    = 1 << 1,
           18         SHORT    = 1 << 2,
           19         CHAR     = 1 << 3,
           20         SIZET    = 1 << 4,
           21         PTRDIFF  = 1 << 5,
           22         INTMAX   = 1 << 6,
           23         VOIDPTR  = 1 << 7,
           24         UNSIGNED = 1 << 8,
           25         ALTFORM  = 1 << 9,
           26 };
           27 
           28 struct conv {
           29         int sign;
           30         size_t prec;
           31         char *digs;
           32         int base;
           33 };
           34 
           35 static uintmax_t
           36 getnum(va_list *va, int flags, int *sign)
           37 {
           38         uintmax_t uval;
           39         intmax_t val;
           40 
           41         if (flags & CHAR) {
           42                 val = va_arg(*va, int);
           43                 uval = (unsigned char) val;
           44         } else if (flags & SHORT) {
           45                 val = va_arg(*va, int);
           46                 uval = (unsigned short) val;
           47         } else if (flags & LONG) {
           48                 val = va_arg(*va, long);
           49                 uval = (unsigned long) val;
           50         } else if (flags & LLONG) {
           51                 val = va_arg(*va, long long);
           52                 uval = (unsigned long long) val;
           53         } else if (flags & SIZET) {
           54                 val = 0;
           55                 uval = va_arg(*va, size_t);
           56         } else if (flags & INTMAX) {
           57                 val = va_arg(*va, intmax_t);
           58                 uval = (uintmax_t) val;
           59         } else if (flags & VOIDPTR) {
           60                 uval = (uintmax_t) va_arg(*va, void *);
           61         } else {
           62                 val = va_arg(*va, int);
           63                 uval = (unsigned) val;
           64         }
           65 
           66         if ((flags & UNSIGNED) == 0 && val < 0) {
           67                 *sign = '-';
           68                 uval = -val;
           69         }
           70         return uval;
           71 }
           72 
           73 static char *
           74 numtostr(uintmax_t val, int flags, struct conv *conv, char *buf)
           75 {
           76         char *buf0 = buf;
           77         int base = conv->base, prec = conv->prec;
           78         uintmax_t oval = val;
           79 
           80         *buf = '\0';
           81         do {
           82                 *--buf = conv->digs[val % base];
           83                 val /= base;
           84         } while (val > 0);
           85 
           86         while (buf0 - buf < prec)
           87                 *--buf = '0';
           88 
           89         if (flags & ALTFORM) {
           90                 if (base == 8 && *buf != '0') {
           91                         *--buf = '0';
           92                 } else if (base == 16 && oval != 0) {
           93                         *--buf = conv->digs[16];
           94                         *--buf = '0';
           95                 }
           96         }
           97         if (conv->sign)
           98                 *--buf = conv->sign;
           99 
          100         return buf;
          101 }
          102 
          103 static void
          104 savecnt(va_list *va, int flags, size_t cnt)
          105 {
          106         if (flags & CHAR)
          107                 *va_arg(*va, char*) = cnt;
          108         else if (flags & SHORT)
          109                 *va_arg(*va, short*) = cnt;
          110         else if (flags & LONG)
          111                 *va_arg(*va, long*) = cnt;
          112         else if (flags & LLONG)
          113                 *va_arg(*va, long long*) = cnt;
          114         else if (flags & SIZET)
          115                 *va_arg(*va, size_t*) = cnt;
          116         else if (flags & INTMAX)
          117                 *va_arg(*va, intmax_t*) = cnt;
          118         else
          119                 *va_arg(*va, int*) = cnt;
          120 }
          121 
          122 static size_t
          123 wstrout(wchar_t *ws, size_t len, int width, int fill, FILE *restrict fp)
          124 {
          125         int left = 0, adjust, n;
          126         size_t cnt = 0;
          127         wchar_t wc;
          128 
          129         if (width < 0) {
          130                 left = 1;
          131                 width = -width;
          132         }
          133 
          134         adjust = len < width ? width - len : 0;
          135         if (left)
          136                 adjust = -adjust;
          137 
          138         for ( ; adjust > 0; adjust--) {
          139                 _fputwc(fill, fp, &n);
          140                 cnt += n;
          141         }
          142 
          143         for ( ; len-- > 0 && (wc = *ws) != '\0'; ++ws) {
          144                 _fputwc(wc, fp, &n);
          145                 cnt += n;
          146         }
          147 
          148         for ( ; adjust < 0; adjust++) {
          149                 _fputwc(' ', fp, &n);
          150                 cnt += n;
          151         }
          152 
          153         return cnt;
          154 }
          155 
          156 static size_t
          157 strout(char *s, size_t len, int width, int fill, FILE *restrict fp)
          158 {
          159         int left = 0, adjust, ch, prefix;
          160         size_t cnt = 0;
          161 
          162         if (width < 0) {
          163                 left = 1;
          164                 width = -width;
          165         }
          166 
          167         adjust = len < width ? width - len : 0;
          168         if (left)
          169                 adjust = -adjust;
          170 
          171         if (fill == '0') {
          172                 if (*s == '-' || *s == '+')
          173                         prefix = 1;
          174                 else if (*s == '0' && toupper(s[1]) == 'X')
          175                         prefix = 2;
          176                 else
          177                         prefix = 0;
          178                 while (prefix--) {
          179                         putc(*s++, fp);
          180                         ++cnt;
          181                         --len;
          182                 }
          183         }
          184 
          185         for ( ; adjust > 0; adjust--) {
          186                 putc(fill, fp);
          187                 ++cnt;
          188         }
          189 
          190         for ( ; len-- > 0 && (ch = *s) != '\0'; ++s) {
          191                 putc(ch, fp);
          192                 ++cnt;
          193         }
          194 
          195         for ( ; adjust < 0; adjust++) {
          196                 putc(' ', fp);
          197                 ++cnt;
          198         }
          199 
          200         return cnt;
          201 }
          202 
          203 int
          204 vfprintf(FILE *restrict fp, const char *restrict fmt, va_list va)
          205 {
          206         int cnt, ch, n;
          207         int flags, width, left, fill, prec;
          208         size_t inc, len;
          209         char *s;
          210         wchar_t *ws;
          211         struct conv conv;
          212         char buf[MAXPREC+1];
          213         wchar_t wbuf[2];
          214         va_list va2;
          215 
          216         va_copy(va2, va);
          217         for (cnt = 0; ch = *fmt++; cnt += inc) {
          218                 if (ch != '%') {
          219                         putc(ch, fp);
          220                         inc = 1;
          221                         continue;
          222                 }
          223 
          224                 fill = ' ';
          225                 prec = left = flags = width =  0;
          226                 conv.prec = 0;
          227                 conv.base = 10;
          228                 conv.sign = '\0';
          229                 conv.digs = "0123456789ABCDEFX";
          230 
          231 flags:
          232                 switch (*fmt++) {
          233                 case ' ':
          234                         if (conv.sign == '\0')
          235                                 conv.sign = ' ';
          236                         goto flags;
          237                 case '+':
          238                         conv.sign = '+';
          239                         goto flags;
          240                 case '#':
          241                         flags |= ALTFORM;
          242                         goto flags;
          243                 case '.':
          244                         if (*fmt == '*') {
          245                                 fmt++;
          246                                 n = va_arg(va2, int);
          247                         } else {
          248                                 for (n = 0; isdigit(ch = *fmt); fmt++)
          249                                         n = n * 10 + ch - '0';
          250                         }
          251                         if (n >= 0) {
          252                                 conv.prec = n;
          253                                 prec = 1;
          254                         }
          255                         goto flags;
          256                 case '*':
          257                         width = va_arg(va2, int);
          258                         goto flags;
          259                 case '-':
          260                         left = 1;
          261                         ++fmt;
          262                 case '1':
          263                 case '2':
          264                 case '3':
          265                 case '4':
          266                 case '5':
          267                 case '6':
          268                 case '7':
          269                 case '8':
          270                 case '9':
          271                         --fmt;
          272                         for (n = 0; isdigit(ch = *fmt); ++fmt)
          273                                 n = n * 10 + ch - '0';
          274                         if (left)
          275                                 n = -n;
          276                         width = n;
          277                         goto flags;
          278                 case '0':
          279                         fill = '0';
          280                         goto flags;
          281                 case 'l':
          282                         flags += LONG;
          283                         goto flags;
          284                 case 'h':
          285                         flags += SHORT;
          286                         goto flags;
          287                 case '%':
          288                         ch = '%';
          289                         goto cout;
          290                 case 'c':
          291                         if (flags & LONG) {
          292                                 wbuf[0] = va_arg(va2, wint_t);
          293                                 wbuf[1] = L'\0';
          294                                 ws = wbuf;
          295                                 len = 1;
          296                                 goto wstrout;
          297                         }
          298                         ch = va_arg(va2, int);
          299                 cout:
          300                         buf[0] = ch;
          301                         buf[1] = '\0';
          302                         s = buf;
          303                         len = 1;
          304                         goto strout;
          305                 case 'j':
          306                         flags |= INTMAX;
          307                         goto flags;
          308                 case 't':
          309                         flags |= PTRDIFF;
          310                         goto flags;
          311                 case 'z':
          312                         flags |= SIZET;
          313                         goto flags;
          314                 case 'u':
          315                         flags |= UNSIGNED;
          316                 case 'i':
          317                 case 'd':
          318                         conv.base = 10;
          319                         goto numeric;
          320                 case 'p':
          321                         flags |= VOIDPTR | ALTFORM;
          322                 case 'x':
          323                         conv.digs = "0123456789abcdefx";
          324                 case 'X':
          325                 numeric16:
          326                         conv.base = 16;
          327                         flags |= UNSIGNED;
          328                         goto numeric;
          329                 case 'o':
          330                         conv.base = 8;
          331                         flags |= UNSIGNED;
          332                 numeric:
          333                         if (conv.prec > MAXPREC)
          334                                 conv.prec = MAXPREC;
          335                         s = numtostr(getnum(&va2, flags, &conv.sign),
          336                                      flags,
          337                                      &conv,
          338                                      &buf[MAXPREC]);
          339                         len = &buf[MAXPREC] - s;
          340                         goto strout;
          341                 case 'L':
          342                 case 'a':
          343                 case 'A':
          344                 case 'e':
          345                 case 'E':
          346                 case 'f':
          347                 case 'g':
          348                 case 'G':
          349                         /* TODO */
          350                         break;
          351                 case 's':
          352                         if (flags & LONG) {
          353                                 ws = va_arg(va2, wchar_t *);
          354                                 len = wcslen(ws);
          355                                 if (prec && len > conv.prec)
          356                                         len = conv.prec;
          357                                 goto wstrout;
          358                         } else {
          359                                 s = va_arg(va2, char *);
          360                                 len = strlen(s);
          361                                 if (prec && len > conv.prec)
          362                                         len = conv.prec;
          363                                 goto strout;
          364                         }
          365                 wstrout:
          366                         inc = wstrout(ws, len, width, fill, fp);
          367                         break;
          368                 strout:
          369                         inc = strout(s, len, width, fill, fp);
          370                         break;
          371                 case 'n':
          372                         savecnt(&va2, flags, cnt);
          373                         break;
          374                 case '\0':
          375                         goto out_loop;
          376                 }
          377         }
          378 
          379 out_loop:
          380         return (ferror(fp)) ? EOF : cnt;
          381 }