scc

simple C compiler
git clone git://git.2f30.org/scc
Log | Files | Refs | README | LICENSE

vfprintf.c (6786B)


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