scc

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

vfprintf.c (6496B)


      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 = -val;
     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 static size_t
    190 strnlen(const char *s, size_t maxlen)
    191 {
    192 	size_t n;
    193 
    194 	for (n = 0; n < maxlen && *s++; ++n)
    195 		;
    196 	return n;
    197 }
    198 
    199 int
    200 vfprintf(FILE * restrict fp, const char * restrict fmt, va_list va)
    201 {
    202 	int ch, n, flags, width, left, fill, cnt = 0;
    203 	size_t inc, len;
    204 	char *s;
    205 	wchar_t *ws;
    206 	struct conv conv;
    207 	char buf[MAXPREC+1];
    208 	wchar_t wbuf[2];
    209 	va_list va2;
    210 
    211 	va_copy(va2, va);
    212 	for (cnt = 0; ch = *fmt++; cnt += inc) {
    213 		if (ch != '%') {
    214 			putc(ch, fp);
    215 			inc = 1;
    216 			continue;
    217 		}
    218 
    219 		fill = ' ';
    220 		left = flags = width =  0;
    221 		conv.prec = -1;
    222 		conv.base = 10;
    223 		conv.sign = '\0';
    224 		conv.digs = "0123456789ABCDEFX";
    225 
    226 flags:
    227 		switch (*fmt++) {
    228 		case ' ':
    229 			if (conv.sign == '\0')
    230 				conv.sign = ' ';
    231 			goto flags;
    232 		case '+':
    233 			conv.sign = '+';
    234 			goto flags;
    235 		case '#':
    236 			flags |= ALTFORM;
    237 			goto flags;
    238 		case '.':
    239 			if (*fmt == '*') {
    240 				fmt++;
    241 				n = va_arg(va2, int);
    242 			} else {
    243 				for (n = 0; isdigit(ch = *fmt); fmt++)
    244 					n = n * 10 + ch - '0';
    245 			}
    246 			if (n > MAXPREC)
    247 				n = MAXPREC;
    248 			if (n > 0)
    249 				conv.prec = n;
    250 			goto flags;
    251 		case '*':
    252 			width = va_arg(va2, int);
    253 			goto flags;
    254 		case '-':
    255 			left = 1;
    256 			++fmt;
    257 		case '1':
    258 		case '2':
    259 		case '3':
    260 		case '4':
    261 		case '5':
    262 		case '6':
    263 		case '7':
    264 		case '8':
    265 		case '9':
    266 			--fmt;
    267 			for (n = 0; isdigit(ch = *fmt); ++fmt)
    268 				n = n * 10 + ch - '0';
    269 			if (left)
    270 				n = -n;
    271 			width = n;
    272 			goto flags;
    273 		case '0':
    274 			fill = '0';
    275 			goto flags;
    276 		case 'l':
    277 			flags += LONG;
    278 			goto flags;
    279 		case 'h':
    280 			flags += SHORT;
    281 			goto flags;
    282 		case '%':
    283 			ch = '%';
    284 			goto cout;
    285 		case 'c':
    286 			if (flags & LONG) {
    287 				wbuf[0] = va_arg(va2, wint_t);
    288 				wbuf[1] = L'\0';
    289 				ws = wbuf;
    290 				len = 1;
    291 				goto wstrout;
    292 			}
    293 			ch = va_arg(va2, int);
    294 		cout:
    295 			buf[0] = ch;
    296 			buf[1] = '\0';
    297 			s = buf;
    298 			len = 1;
    299 			goto strout;
    300 		case 'j':
    301 			flags |= INTMAX;
    302 			goto flags;
    303 		case 't':
    304 			flags |= PTRDIFF;
    305 			goto flags;
    306 		case 'z':
    307 			flags |= SIZET;
    308 			goto flags;
    309 		case 'u':
    310 			flags |= UNSIGNED;
    311 		case 'i':
    312 		case 'd':
    313 			conv.base = 10;
    314 			goto numeric;
    315 		case 'p':
    316 			flags |= VOIDPTR | ALTFORM;
    317 			goto numeric16;
    318 		case 'x':
    319 			conv.digs = "0123456789abcdefx";
    320 		case 'X':
    321 		numeric16:
    322 			conv.base = 16;
    323 			flags |= UNSIGNED;
    324 			goto numeric;
    325 		case 'o':
    326 			conv.base = 8;
    327 			flags |= UNSIGNED;
    328 		numeric:
    329 			if (conv.prec != -1)
    330 				fill = ' ';
    331 			s = numtostr(getnum(&va2, flags, &conv.sign),
    332 			             flags,
    333 			             &conv,
    334 			             &buf[MAXPREC]);
    335 			len = &buf[MAXPREC] - s;
    336 			goto strout;
    337 		case 'L':
    338 		case 'a':
    339 		case 'A':
    340 		case 'e':
    341 		case 'E':
    342 		case 'f':
    343 		case 'g':
    344 		case 'G':
    345 			/* TODO */
    346 		case 's':
    347 			if (flags & LONG) {
    348 				ws = va_arg(va2, wchar_t *);
    349 				/* len = wcsnlen(ws, conv.prec); */
    350 				goto wstrout;
    351 			} else {
    352 				s = va_arg(va2, char *);
    353 				if ((len = strlen(s)) > conv.prec)
    354 					len = conv.prec;
    355 				goto strout;
    356 			}
    357 		wstrout:
    358 			inc = wstrout(ws, len, width, fill, fp);
    359 			break;
    360 		strout:
    361 			inc = strout(s, len, width, fill, fp);
    362 			break;
    363 		case 'n':
    364 			savecnt(&va2, flags, cnt);
    365 			break;
    366 		case '\0':
    367 			goto out_loop;
    368 		}
    369 	}
    370 
    371 out_loop:
    372 	return (ferror(fp)) ? EOF : cnt;
    373 }