diff options
author | Jakob Kaivo <jkk@ung.org> | 2024-06-06 17:02:32 -0400 |
---|---|---|
committer | Jakob Kaivo <jkk@ung.org> | 2024-06-06 17:02:32 -0400 |
commit | d8353afab7c754968ebbaa8fa1cd3f6f4b83d86e (patch) | |
tree | 077bfbff86d29acbd06693bc19def74b7743749d | |
parent | 5da931dfa4b3846b1ca459290a6724eb67d1685c (diff) |
rework formatted I/O to do most validation in __conv(), use __conv() from __printf(), implement more and more robust __printf() functionality
-rw-r--r-- | src/_safety.h | 1 | ||||
-rw-r--r-- | src/stdio/__conv.c | 161 | ||||
-rw-r--r-- | src/stdio/__printf.c | 372 | ||||
-rw-r--r-- | src/stdio/__scanf.c | 20 | ||||
-rw-r--r-- | src/stdio/_stdio.h | 31 |
5 files changed, 278 insertions, 307 deletions
diff --git a/src/_safety.h b/src/_safety.h index 5024e52f..7697589c 100644 --- a/src/_safety.h +++ b/src/_safety.h @@ -60,7 +60,6 @@ extern struct __dangerous { abort(); \ } \ (__arr) = tmp; \ - printf("Adding %s\n", (__val)); \ (__arr)[__count] = strdup(__val); \ (__count)++; \ } while (0) diff --git a/src/stdio/__conv.c b/src/stdio/__conv.c index 3dbfd687..994bb072 100644 --- a/src/stdio/__conv.c +++ b/src/stdio/__conv.c @@ -3,7 +3,18 @@ #include <string.h> #include "_stdio.h" -size_t __conv(const char *format, struct io_conversion *conv) +static char *length_names[] = { + [L_hh] = "hh", + [L_ll] = "ll", + [L_h] = "h", + [L_l] = "l", + [L_j] = "j", + [L_z] = "z", + [L_t] = "t", + [L_L] = "L", +}; + +size_t __conv(const char *format, struct io_conversion *conv, va_list arg) { size_t ret = 0; @@ -15,8 +26,6 @@ size_t __conv(const char *format, struct io_conversion *conv) conv->flags = 0; conv->length = L_default; - conv->has_width = 0; - conv->has_precision = 0; while (strchr("*-+ #0", format[ret])) { switch (format[ret]) { @@ -27,58 +36,130 @@ size_t __conv(const char *format, struct io_conversion *conv) case '#': conv->flags |= F_ALT; break; case '0': conv->flags |= F_ZERO; break; } - /* check for invalid input flags (only * is allowed) */ + + if (conv->dir == IO_IN) { + if ((conv->flags & ~(F_STAR)) != 0) { + UNDEFINED("Flag '%c' is not valid for formatted input", format[ret]); + } + } else { + if (conv->flags & F_STAR) { + int width = va_arg(arg, int); + conv->flags |= F_WIDTH; + if (width < 0) { + conv->flags |= F_LEFT; + conv->width = -width; + } else { + conv->width = width; + } + } + } + ret++; } if (isdigit(format[ret])) { char *end = NULL; - conv->has_width = 1; + conv->flags |= F_WIDTH; conv->width = strtoumax(format + ret, &end, 10); ret += (size_t)(end - (format + ret)); } - /* TODO: precision */ - - if (strchr("hljztL", format[ret])) { - switch (format[ret]) { - case 'h': - if (format[ret + 1] == 'h') { - ret++; - conv->length = L_hh; - } else { - conv->length = L_h; - } - break; - case 'l': - if (format[ret + 1] == 'l') { - ret++; - conv->length = L_ll; + if (format[ret] == '.') { + if (conv->dir == IO_IN) { + UNDEFINED("Precision is not supported for formatted input"); + } + conv->flags |= F_PRECISION; + ret++; + if (format[ret] == '*') { + int prec = va_arg(arg, int); + if (prec > 0) { + conv->precision = prec; } else { - conv->length = L_l; + conv->flags &= ~(F_PRECISION); } - break; - case 'j': - conv->length = L_j; - break; - case 'z': - conv->length = L_z; - break; - case 't': - conv->length = L_t; - break; - case 'L': - conv->length = L_L; - break; - default: - break; + } else { + char *end = NULL; + conv->precision = strtoumax(format + ret, &end, 10); + ret += (size_t)(end - (format + ret)); } - ret++; } - /* TODO: validate */ + for (size_t i = 0; i < sizeof(length_names) / sizeof(length_names[0]); i++) { + if (length_names[i] && strncmp(length_names[i], format + ret, strlen(length_names[i])) == 0) { + conv->length = (enum conversion_length)i; + ret += strlen(length_names[i]); + break; + } + } conv->spec = format[ret]; - ret++; + if (isupper(conv->spec)) { + conv->flags |= F_UPPER; + } + + switch (conv->spec) { + case 'd': + case 'i': + if (conv->length == L_L) { + UNDEFINED("In call to %s(): Length '%s' is not supported with conversion specifier '%c'", conv->func, length_names[conv->length], conv->spec); + } + break; + + case 'o': + case 'u': + case 'x': + case 'X': + if (conv->length == L_L) { + UNDEFINED("In call to %s(): Length '%s' is not supported with conversion specifier '%c'", conv->func, length_names[conv->length], conv->spec); + } + break; + + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': + if (conv->length != L_L && conv->length) { + UNDEFINED("In call to %s(): Length '%s' is not supported with conversion specifier '%c'", conv->func, length_names[conv->length], conv->spec); + } + break; + + case 'c': + if (conv->flags & F_PRECISION) { + UNDEFINED("In call to %s(): Precision is not supported with conversion specifier '%c'", conv->func, conv->spec); + } + /* FALLTHRU */ + + case 's': + case '[': + if (conv->length != L_l && conv->length) { + UNDEFINED("In call to %s(): Length '%s' is not supported with conversion specifier '%c'", conv->func, length_names[conv->length], conv->spec); + } + break; + + case 'p': + case 'n': + case '%': + if (conv->flags & F_WIDTH) { + UNDEFINED("In call to %s(): Field width is not supported with conversion specififier '%c'", conv->func, conv->spec); + } + if (conv->flags & F_PRECISION) { + UNDEFINED("In call to %s(): Precision is not supported with conversion specififier '%c'", conv->func, conv->spec); + } + if (conv->flags) { + UNDEFINED("In call to %s(): Flags are not supported with conversion specifier '%c'", conv->func, conv->spec); + } + if (conv->length) { + UNDEFINED("In call to %s(): Length '%s' is not supported with conversion specifier '%c'", conv->func, length_names[conv->length], conv->spec); + } + break; + + default: + UNDEFINED("In call to %s(): Unknown conversion specifier '%c'", conv->func, conv->spec); + } + return ret; } diff --git a/src/stdio/__printf.c b/src/stdio/__printf.c index 807e0948..34904d30 100644 --- a/src/stdio/__printf.c +++ b/src/stdio/__printf.c @@ -1,4 +1,5 @@ #include <ctype.h> +#include <limits.h> #include <stdio.h> #include <stddef.h> #include <stdlib.h> @@ -17,40 +18,51 @@ #define NUMBUFLEN 64 -#define LEFT (1 << 0) -#define SIGN (1 << 1) -#define SPACE (1 << 2) -#define ALT (1 << 3) -#define ZERO (1 << 4) -#define UPPER (1 << 5) -#define UNSIGNED (1 << 6) +static int __output(struct io_options *opt, struct io_conversion *conv, const char *str, size_t len) +{ + size_t olen = strlen(str); + if (len == 0) { + len = olen; + } -#define FLAG (1 << 0) -#define WIDTH (1 << 1) -#define PRECISION (1 << 2) -#define LENGTH (1 << 3) + if (conv && olen < conv->width && (conv->flags & F_WIDTH) && !(conv->flags & F_LEFT)) { + for (size_t i = 0; i < conv->width - olen; i++) { + __output(opt, NULL, " ", 1); + } + } + + for (size_t i = 0; i < len && str[i] != '\0'; i++) { + if (opt->ret == INT_MAX) { + UNDEFINED("In call to %s(): Attempting to output more than INT_MAX characters", opt->fnname); + } + if (opt->stream) { + fputc(str[i], opt->stream); + } else if (opt->string) { + if ((size_t)opt->ret < opt->maxlen) { + opt->string[opt->ret] = str[i]; + } + } else { + /* file descriptor */ + } + opt->ret++; + } -static int __append(char *s, char *argstring, int nout, size_t n) -{ - s += nout; - while (*argstring) { - if (nout < (int)n) { - *s++ = *argstring; + if (conv && olen < conv->width && (conv->flags & F_WIDTH) && (conv->flags & F_LEFT)) { + for (size_t i = 0; i < conv->width - olen; i++) { + __output(opt, NULL, " ", 1); } - nout++; - argstring++; } - return nout; + + return 0; } -static void __itos(char *s, intmax_t n, int flags, int precision, int base) +static void __utos(char *s, uintmax_t n, enum conversion_flags flags, int precision, int base) { - extern int toupper(int); char digits[] = "0123456789abcdef"; - char sign = n < 0 ? '-' : '+'; + char sign = '+'; char buf[NUMBUFLEN + 1]; char *out = buf + NUMBUFLEN; - if (flags & UPPER && base > 10) { + if (flags & F_UPPER && base > 10) { size_t i; for (i = 0; i < sizeof(digits); i++) { digits[i] = (char)toupper(digits[i]); @@ -68,7 +80,13 @@ static void __itos(char *s, intmax_t n, int flags, int precision, int base) n /= base; out--; } - if (flags & SIGN || sign == '-') { + /* TODO: this has a risk of overflowing */ + while (precision > 0 && (flags & (F_ZERO | F_PRECISION))) { + *out = '0'; + precision--; + out--; + } + if (flags & F_SIGN) { *out = sign; out--; } @@ -78,193 +96,100 @@ static void __itos(char *s, intmax_t n, int flags, int precision, int base) } } -int (__printf)(struct io_options *opt, const char * format, va_list arg) +static void __itos(char *s, intmax_t n, enum conversion_flags flags, int precision, int base) +{ + uintmax_t absn = (uintmax_t)imaxabs(n); + __utos(s, absn, flags | ((n < 0) ? F_SIGN : 0), precision, base); + + if (n < 0) { + char *sign = strrchr(s, '+'); + *sign = '-'; + } +} + +int __printf(struct io_options *opt, const char * format, va_list arg) { - char buf[BUFSIZ]; int nout = 0; intmax_t argint = 0; + uintmax_t arguns = 0; void *argptr = NULL; char numbuf[NUMBUFLEN]; size_t i; - size_t n = 0; - char *s = NULL; + //char *s = NULL; if (opt->stream) { - /* file based */ - s = buf; - n = BUFSIZ; flockfile(opt->stream); - } else if (opt->string) { - /* memory buffer */ - s = opt->string; - n = opt->maxlen; - } else { - /* file descriptor */ - s = buf; - n = BUFSIZ; } for (i = 0; format[i] != 0; i++) { - /* - // zero of more flags "-+ #0" - // optional width "*" or decimal integer - // optional precision ".*" or ".[decimal]" - // optional length modifier "hh", "h", "l", "ll", "j", - // "z", "t", "L" - // conversion specifier "diouxXfFeEgGaAcspn%" - */ - unsigned int flags = 0; - uintmax_t width = 0; - int step = 0; - int precision = 0; + struct io_conversion conv = { + .func = opt->fnname, + .dir = IO_OUT, + }; int base = 10; - unsigned int specified = 0; - enum { def, hh, h, l, ll, j, z, t, L } length = def; if (format[i] != '%') { - if (nout < (int)n) { - s[nout] = format[i]; + size_t prev = i; + while (format[i] != '\0' && format[i] != '%') { + i++; } - nout++; + __output(opt, NULL, format + prev, i - prev); + i--; continue; } - while (step == 0) { - i++; - switch (format[i]) { - case '-': flags |= LEFT; specified |= FLAG; break; - case '+': flags |= SIGN; specified |= FLAG; break; - case ' ': flags |= SPACE; specified |= FLAG; break; - case '#': flags |= ALT; specified |= FLAG; break; - case '0': flags |= ZERO; specified |= FLAG; break; - default: step = 1; break; - } - } - - if (format[i] == '*') { - specified |= WIDTH; - width = va_arg(arg, int); - i++; - } else if (isdigit(format[i])) { - specified |= WIDTH; + i += __conv(format + i, &conv, arg); - char *end; - width = strtoumax(format + i, &end, 10); - i = end - format; - } - (void)width; /* TODO!!!! */ + switch (conv.spec) { + case 'o': /* unsigned int */ + case 'u': + case 'x': + case 'X': + switch (conv.length) { + case L_hh: arguns = (unsigned char)va_arg(arg, unsigned int); break; + case L_h: arguns = (unsigned short int)va_arg(arg, unsigned int); break; + case L_l: arguns = va_arg(arg, unsigned long int); break; - if (format[i] == '.') { - specified |= PRECISION; + #if defined __STDC_VERSION__ && 199901L <= __STDC_VERSION__ + case L_ll: arguns = va_arg(arg, unsigned long long int); break; + #endif - i++; - if (format[i] == '*') { - /* TODO */ - i++; - } else if (isdigit(format[i])) { - char *end; - precision = (int)strtoumax(format + i, &end, 10); - i = end - format; - } else { - precision = 0; - } - } + case L_j: arguns = va_arg(arg, uintmax_t); break; + case L_z: arguns = va_arg(arg, size_t); break; + case L_t: arguns = va_arg(arg, ptrdiff_t); break; - if (format[i] == 'h') { - i++; - if (format[i] == 'h') { - i++; - length = hh; - } else { - length = h; + default: arguns = va_arg(arg, unsigned int); break; } - } else if (format[i] == 'l') { - i++; - if (format[i] == 'l') { - i++; - length = ll; - } else { - length = l; + if (conv.spec == 'o') { + base = 8; + } else if (conv.spec == 'x' || conv.spec == 'X') { + base = 16; } - } else if (format[i] == 'j') { - i++; - length = j; - } else if (format[i] == 'z') { - i++; - length = z; - } else if (format[i] == 't') { - i++; - length = t; - } else if (format[i] == 'L') { - i++; - length = L; - } - - if (isupper(format[i])) { - flags |= UPPER; - } - - if ((flags & ALT) && (!strchr("xXaAeEfFgG", format[i]))) { - UNDEFINED("In call to %s(): The '#' flag is undefined for %%%c", opt->fnname, format[i]); - } else if ((flags & ZERO) && (!strchr("diouxXaAeEfFgG", format[i]))) { - UNDEFINED("In call to %s(): The '0' flag is undefined for %%%c", opt->fnname, format[i]); - } else if ((length == hh) && (!strchr("diouxXn", format[i]))) { - UNDEFINED("In call to %s(): The length 'hh' is undefined for %%%c", opt->fnname, format[i]); - } else if ((length == h) && (!strchr("diouxXn", format[i]))) { - UNDEFINED("In call to %s(): The length 'h' is undefined for %%%c", opt->fnname, format[i]); - } else if ((length == l) && (!strchr("diouxXncsaAeEfFgG", format[i]))) { - UNDEFINED("In call to %s(): The length 'l' is undefined for %%%c", opt->fnname, format[i]); - } else if ((length == ll) && (!strchr("diouxXn", format[i]))) { - UNDEFINED("In call to %s(): The length 'll' is undefined for %%%c", opt->fnname, format[i]); - } else if ((length == j) && (!strchr("diouxXn", format[i]))) { - UNDEFINED("In call to %s(): The length 'j' is undefined for %%%c", opt->fnname, format[i]); - } else if ((length == z) && (!strchr("diouxXn", format[i]))) { - UNDEFINED("In call to %s(): The length 'z' is undefined for %%%c", opt->fnname, format[i]); - } else if ((length == t) && (!strchr("diouxXn", format[i]))) { - UNDEFINED("In call to %s(): The length 't' is undefined for %%%c", opt->fnname, format[i]); - } else if ((length == L) && (!strchr("aAeEfFgG", format[i]))) { - UNDEFINED("In call to %s(): The length 'L' is undefined for %%%c", opt->fnname, format[i]); - } - - switch (format[i]) { - case 'o': /* unsigned int */ - case 'u': - case 'x': - case 'X': - flags |= UNSIGNED; - /* FALLTHRU */ + __utos(numbuf, arguns, conv.flags, conv.precision, base); + __output(opt, &conv, numbuf, 0); + break; case 'd': /* int */ case 'i': - switch (length) { - case hh: argint = (signed char)va_arg(arg, int); break; - case h: argint = (short int)va_arg(arg, int); break; - case l: argint = va_arg(arg, long int); break; + switch (conv.length) { + case L_hh: argint = (signed char)va_arg(arg, int); break; + case L_h: argint = (short int)va_arg(arg, int); break; + case L_l: argint = va_arg(arg, long int); break; #if defined __STDC_VERSION__ && 199901L <= __STDC_VERSION__ - case ll: argint = va_arg(arg, long long int); break; + case L_ll: argint = va_arg(arg, long long int); break; #endif - case j: argint = va_arg(arg, intmax_t); break; - case z: argint = va_arg(arg, size_t); break; - case t: argint = va_arg(arg, ptrdiff_t); break; - case L: nout = -nout; goto end; + case L_j: argint = va_arg(arg, intmax_t); break; + case L_z: argint = va_arg(arg, size_t); break; + case L_t: argint = va_arg(arg, ptrdiff_t); break; + default: argint = va_arg(arg, int); break; } - if (format[i] == 'o') { - base = 8; - } else if (format[i] == 'x') { - base = 16; - } else if (format[i] == 'X') { - base = 16; - flags |= UPPER; - } else { - base = 10; - } - __itos(numbuf, (long int)argint, flags, precision, base); - nout = __append(s, numbuf, nout, n); + __itos(numbuf, argint, conv.flags, conv.precision, 10); + __output(opt, &conv, numbuf, 0); break; @@ -288,122 +213,83 @@ int (__printf)(struct io_options *opt, const char * format, va_list arg) break; case 'c': /* char */ - if (specified & PRECISION) { - UNDEFINED("In call to %s(): Precision with %%c conversions", opt->fnname); - } - - if (length == def) { + if (conv.length == L_default) { char c = va_arg(arg, int); - if (nout < (int)n) { - s[nout] = c; - } + __output(opt, &conv, &c, 1); nout++; - } else if (length == l) { + } else if (conv.length == L_l) { #if defined __STDC_VERSION__ wint_t wc = va_arg(arg, wint_t); char mb[MB_CUR_MAX + 1]; mb[0] = 'W'; mb[1] = 'C'; mb[2] = '\0'; wctomb(mb, wc); - nout = __append(s, mb, nout, n); - #else - nout = __append(s, "NOSUP", nout, n); + __output(opt, &conv, mb, 0); #endif - } else { - nout = -nout; - goto end; } break; case 's': /* string */ - if (length == def) { + if (conv.length == L_default) { char *string = va_arg(arg, char *); - nout = __append(s, string, nout, n); - } else if (length == l) { + __output(opt, &conv, string, 0); + } else if (conv.length == L_l) { #if defined __STDC_VERSION__ wchar_t *ws = va_arg(arg, wchar_t *); char *mbs = malloc(wcslen(ws) * MB_CUR_MAX + 1); wcstombs(mbs, ws, wcslen(ws) * MB_CUR_MAX + 1); - nout = __append(s, mbs, nout, n); + __output(opt, &conv, mbs, 0); free(mbs); - #else - nout = __append(s, "NOSUP", nout, n); #endif - } else { - nout = -nout; - goto end; } break; case 'p': /* pointer */ - if (specified & PRECISION) { - UNDEFINED("In call to %s(): Precision with %%p conversion", opt->fnname); - } argptr = va_arg(arg, void *); - char *s_to_track = s + nout; - nout = __append(s, "0x", nout, n); - __itos(numbuf, (intptr_t)argptr, ZERO, sizeof(argptr) * 2, 16); - nout = __append(s, numbuf, nout, n); - ADD_PREV_STRING(s_to_track, __stdio_h.formatted_pointers, __stdio_h.nformatted_pointers); + __itos(numbuf, (intptr_t)argptr, F_ALT | F_ZERO, sizeof(argptr) * 2, 16); + __output(opt, &conv, numbuf, 0); + ADD_PREV_STRING(numbuf, __stdio_h.formatted_pointers, __stdio_h.nformatted_pointers); break; case 'n': /* write-back */ - if (specified & FLAG) { - UNDEFINED("In call to %s(): Flags with %%n conversion", opt->fnname); - } else if (specified & 0) { - /* TODO: output suppression (might only be for input) */ - } else if (specified & WIDTH) { - UNDEFINED("In call to %s(): Width with %%n conversion", opt->fnname); - } else if (specified & PRECISION) { - UNDEFINED("In call to %s(): Precision with %%n conversion", opt->fnname); - } - - switch (length) { - case def: - int *ip = va_arg(arg, int *); - *ip = nout; - break; - case hh: + switch (conv.length) { + case L_hh: signed char *sc = va_arg(arg, signed char *); *sc = nout; break; - case h: + case L_h: short int *si = va_arg(arg, short int *); *si = nout; break; - case l: + case L_l: long int *li = va_arg(arg, long int *); *li = nout; break; - case ll: + case L_ll: long long int *lli = va_arg(arg, long long int *); *lli = nout; break; - case j: + case L_j: intmax_t *im = va_arg(arg, intmax_t *); *im = nout; break; - case z: + case L_z: size_t *sz = va_arg(arg, size_t *); *sz = nout; break; - case t: + case L_t: ptrdiff_t *pd = va_arg(arg, ptrdiff_t *); *pd = nout; break; - case L: - UNDEFINED("In call to %s(): Invalid length 'L' for %%n conversion", opt->fnname); + default: + int *ip = va_arg(arg, int *); + *ip = nout; + break; } break; case '%': /* literal '%' */ - if (specified != 0) { - UNDEFINED("In call to %s(): \"%%%%\" conversion is not literally \"%%%%\"", opt->fnname); - } - if (nout < (int)n) { - s[nout] = '%'; - } - nout++; + __output(opt, &conv, "%", 1); break; default: /* undefined */ @@ -411,17 +297,11 @@ int (__printf)(struct io_options *opt, const char * format, va_list arg) } } - end: if (opt->stream) { - fwrite(buf, 1, nout % BUFSIZ, opt->stream); funlockfile(opt->stream); - #ifdef _POSIX_SOURCE - } else if (opt->fd != -1) { - write(opt->fd, buf, nout % BUFSIZ); - #endif } - return nout; + return opt->ret; } /* diff --git a/src/stdio/__scanf.c b/src/stdio/__scanf.c index 9869fa37..5e43303e 100644 --- a/src/stdio/__scanf.c +++ b/src/stdio/__scanf.c @@ -126,7 +126,10 @@ int __scanf(struct io_options *opt, const char * format, va_list arg) } while (*format) { - struct io_conversion conv = { .dir = IO_IN }; + struct io_conversion conv = { + .func = opt->fnname, + .dir = IO_IN, + }; if (isspace(*format)) { int c = 0; @@ -156,7 +159,7 @@ int __scanf(struct io_options *opt, const char * format, va_list arg) break; } - format += __conv(format, &conv); + format += __conv(format, &conv, arg); switch (conv.spec) { case 'd': /* base 10 int */ @@ -171,8 +174,6 @@ int __scanf(struct io_options *opt, const char * format, va_list arg) //case L_z: ASSIGN(signed size_t, arg, i, 0, 0); break; /* TODO!!! */ //case L_t: ASSIGN(signed ptrdiff_t, arg, i, 0, 0); break; /* TODO!!! */ default: ASSIGN(int, arg, i, INT_MIN, INT_MAX); break; - - /* case L_L: UNDEFINED(""); break; */ } break; @@ -190,8 +191,6 @@ int __scanf(struct io_options *opt, const char * format, va_list arg) case L_z: ASSIGN(size_t, arg, u, 0, SIZE_MAX); break; case L_t: ASSIGN(ptrdiff_t, arg, u, 0, PTRDIFF_MAX); break; default: ASSIGN(unsigned int, arg, u, 0, UINT_MAX); break; - - /* case L_L: UNDEFINED(""); break; */ } break; @@ -218,16 +217,12 @@ int __scanf(struct io_options *opt, const char * format, va_list arg) case 'c': /* width (default 1) characters */ - if (conv.has_width == 0) { + if ((conv.flags & F_WIDTH) == 0) { conv.width = 1; } break; case 's': - if (conv.length != L_default && conv.length != L_l) { - //__bad_length(length, 's'); - } - char *str = va_arg(arg, char *); /* TODO: only use width if conv.has_width == 1 */ @@ -269,14 +264,13 @@ int __scanf(struct io_options *opt, const char * format, va_list arg) break; default: - UNDEFINED("Unknown conversion specifier '%c'", *format); + /* ub already handled by __conv() */ break; } format++; } - (void)arg; return ret; } diff --git a/src/stdio/_stdio.h b/src/stdio/_stdio.h index cdd8fb47..1894e956 100644 --- a/src/stdio/_stdio.h +++ b/src/stdio/_stdio.h @@ -97,34 +97,51 @@ struct io_options { }; struct io_conversion { + const char *func; enum { IO_IN, IO_OUT } dir; - enum { + enum conversion_flags { + /* explicit */ F_STAR = (1<<0), F_LEFT = (1<<1), F_SIGN = (1<<2), F_SPACE = (1<<3), F_ALT = (1<<4), - F_ZERO = (1<<4), + F_ZERO = (1<<5), + + /* inferred */ + F_UPPER = (1<<10), + F_WIDTH = (1<<11), + F_PRECISION = (1<<12), } flags; - enum { + enum conversion_length { L_default, L_hh, + L_ll, L_h, L_l, - L_ll, L_j, L_z, L_t, L_L, } length; - int has_width:1; - int has_precision:1; uintmax_t width; uintmax_t precision; char spec; + union { + uintmax_t u; + intmax_t i; + float f; + double d; + long double ld; + char *s; + wchar_t *wcs; + char c; + wchar_t wc; + void *ptr; + } val; }; -size_t __conv(const char *, struct io_conversion *); +size_t __conv(const char *, struct io_conversion *, va_list); int __printf(struct io_options * restrict, const char * restrict, va_list); int __scanf(struct io_options * restrict, const char * restrict, va_list); |