scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | README | LICENSE

vfprintf.c (6337B)


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