Home
       vfprintf.c - scc - simple c99 compiler
  HTML git clone git://git.simple-cc.org/scc
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
       vfprintf.c (6546B)
       ---
            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 ch, n, flags, width, left, fill, prec;
          207         size_t inc, len, cnt = 0;
          208         char *s;
          209         wchar_t *ws;
          210         struct conv conv;
          211         char buf[MAXPREC+1];
          212         wchar_t wbuf[2];
          213         va_list va2;
          214 
          215         va_copy(va2, va);
          216         for (cnt = 0; ch = *fmt++; cnt += inc) {
          217                 if (ch != '%') {
          218                         putc(ch, fp);
          219                         inc = 1;
          220                         continue;
          221                 }
          222 
          223                 fill = ' ';
          224                 prec = left = flags = width =  0;
          225                 conv.prec = 0;
          226                 conv.base = 10;
          227                 conv.sign = '\0';
          228                 conv.digs = "0123456789ABCDEFX";
          229 
          230 flags:
          231                 switch (*fmt++) {
          232                 case ' ':
          233                         if (conv.sign == '\0')
          234                                 conv.sign = ' ';
          235                         goto flags;
          236                 case '+':
          237                         conv.sign = '+';
          238                         goto flags;
          239                 case '#':
          240                         flags |= ALTFORM;
          241                         goto flags;
          242                 case '.':
          243                         if (*fmt == '*') {
          244                                 fmt++;
          245                                 n = va_arg(va2, int);
          246                         } else {
          247                                 for (n = 0; isdigit(ch = *fmt); fmt++)
          248                                         n = n * 10 + ch - '0';
          249                         }
          250                         if (n >= 0) {
          251                                 conv.prec = n;
          252                                 prec = 1;
          253                         }
          254                         goto flags;
          255                 case '*':
          256                         width = va_arg(va2, int);
          257                         goto flags;
          258                 case '-':
          259                         left = 1;
          260                         ++fmt;
          261                 case '1':
          262                 case '2':
          263                 case '3':
          264                 case '4':
          265                 case '5':
          266                 case '6':
          267                 case '7':
          268                 case '8':
          269                 case '9':
          270                         --fmt;
          271                         for (n = 0; isdigit(ch = *fmt); ++fmt)
          272                                 n = n * 10 + ch - '0';
          273                         if (left)
          274                                 n = -n;
          275                         width = n;
          276                         goto flags;
          277                 case '0':
          278                         fill = '0';
          279                         goto flags;
          280                 case 'l':
          281                         flags += LONG;
          282                         goto flags;
          283                 case 'h':
          284                         flags += SHORT;
          285                         goto flags;
          286                 case '%':
          287                         ch = '%';
          288                         goto cout;
          289                 case 'c':
          290                         if (flags & LONG) {
          291                                 wbuf[0] = va_arg(va2, wint_t);
          292                                 wbuf[1] = L'\0';
          293                                 ws = wbuf;
          294                                 len = 1;
          295                                 goto wstrout;
          296                         }
          297                         ch = va_arg(va2, int);
          298                 cout:
          299                         buf[0] = ch;
          300                         buf[1] = '\0';
          301                         s = buf;
          302                         len = 1;
          303                         goto strout;
          304                 case 'j':
          305                         flags |= INTMAX;
          306                         goto flags;
          307                 case 't':
          308                         flags |= PTRDIFF;
          309                         goto flags;
          310                 case 'z':
          311                         flags |= SIZET;
          312                         goto flags;
          313                 case 'u':
          314                         flags |= UNSIGNED;
          315                 case 'i':
          316                 case 'd':
          317                         conv.base = 10;
          318                         goto numeric;
          319                 case 'p':
          320                         flags |= VOIDPTR | ALTFORM;
          321                 case 'x':
          322                         conv.digs = "0123456789abcdefx";
          323                 case 'X':
          324                 numeric16:
          325                         conv.base = 16;
          326                         flags |= UNSIGNED;
          327                         goto numeric;
          328                 case 'o':
          329                         conv.base = 8;
          330                         flags |= UNSIGNED;
          331                 numeric:
          332                         if (conv.prec > MAXPREC)
          333                                 conv.prec = MAXPREC;
          334                         s = numtostr(getnum(&va2, flags, &conv.sign),
          335                                      flags,
          336                                      &conv,
          337                                      &buf[MAXPREC]);
          338                         len = &buf[MAXPREC] - s;
          339                         goto strout;
          340                 case 'L':
          341                 case 'a':
          342                 case 'A':
          343                 case 'e':
          344                 case 'E':
          345                 case 'f':
          346                 case 'g':
          347                 case 'G':
          348                         /* TODO */
          349                         break;
          350                 case 's':
          351                         if (flags & LONG) {
          352                                 ws = va_arg(va2, wchar_t *);
          353                                 len = wcslen(ws);
          354                                 if (prec && len > conv.prec)
          355                                         len = conv.prec;
          356                                 goto wstrout;
          357                         } else {
          358                                 s = va_arg(va2, char *);
          359                                 len = strlen(s);
          360                                 if (prec && len > conv.prec)
          361                                         len = conv.prec;
          362                                 goto strout;
          363                         }
          364                 wstrout:
          365                         inc = wstrout(ws, len, width, fill, fp);
          366                         break;
          367                 strout:
          368                         inc = strout(s, len, width, fill, fp);
          369                         break;
          370                 case 'n':
          371                         savecnt(&va2, flags, cnt);
          372                         break;
          373                 case '\0':
          374                         goto out_loop;
          375                 }
          376         }
          377 
          378 out_loop:
          379         return (ferror(fp)) ? EOF : cnt;
          380 }