scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | Submodules | README | 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 }