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 /src/stdio/__conv.c | |
parent | 5da931dfa4b3846b1ca459290a6724eb67d1685c (diff) |
rework formatted I/O to do most validation in __conv(), use __conv() from __printf(), implement more and more robust __printf() functionality
Diffstat (limited to 'src/stdio/__conv.c')
-rw-r--r-- | src/stdio/__conv.c | 161 |
1 files changed, 121 insertions, 40 deletions
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; } |