scc

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