scc

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

vfprintf.c (6495B)


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