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>89#include "../libc.h"1011#define MAXPREC 501213#undef vfprintf1415enum {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};2728struct conv {29 int sign;30 size_t prec;31 char *digs;32 int base;33};3435static uintmax_t36getnum(va_list *va, int flags, int *sign)37{38 uintmax_t uval;39 intmax_t val;4041 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 }6566 if ((flags & UNSIGNED) == 0 && val < 0) {67 *sign = '-';68 uval = -val;69 }70 return uval;71}7273static char *74numtostr(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;7980 *buf = '\0';81 do {82 *--buf = conv->digs[val % base];83 val /= base;84 } while (val > 0);8586 while (buf0 - buf < prec)87 *--buf = '0';8889 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;99100 return buf;101}102103static void104savecnt(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 else119 *va_arg(*va, int*) = cnt;120}121122static size_t123wstrout(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;128129 if (width < 0) {130 left = 1;131 width = -width;132 }133134 adjust = len < width ? width - len : 0;135 if (left)136 adjust = -adjust;137138 for ( ; adjust > 0; adjust--) {139 _fputwc(fill, fp, &n);140 cnt += n;141 }142143 for ( ; len-- > 0 && (wc = *ws) != '\0'; ++ws) {144 _fputwc(wc, fp, &n);145 cnt += n;146 }147148 for ( ; adjust < 0; adjust++) {149 _fputwc(' ', fp, &n);150 cnt += n;151 }152153 return cnt;154}155156static size_t157strout(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;161162 if (width < 0) {163 left = 1;164 width = -width;165 }166167 adjust = len < width ? width - len : 0;168 if (left)169 adjust = -adjust;170171 if (fill == '0') {172 if (*s == '-' || *s == '+')173 prefix = 1;174 else if (*s == '0' && toupper(s[1]) == 'X')175 prefix = 2;176 else177 prefix = 0;178 while (prefix--) {179 putc(*s++, fp);180 ++cnt;181 --len;182 }183 }184185 for ( ; adjust > 0; adjust--) {186 putc(fill, fp);187 ++cnt;188 }189190 for ( ; len-- > 0 && (ch = *s) != '\0'; ++s) {191 putc(ch, fp);192 ++cnt;193 }194195 for ( ; adjust < 0; adjust++) {196 putc(' ', fp);197 ++cnt;198 }199200 return cnt;201}202203int204vfprintf(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;215216 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 }223224 fill = ' ';225 prec = left = flags = width = 0;226 conv.prec = 0;227 conv.base = 10;228 conv.sign = '\0';229 conv.digs = "0123456789ABCDEFX";230231flags: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 }378379out_loop:380 return (ferror(fp)) ? EOF : cnt;381}