Reorganize the parsing the length modifiers to follow more closely the specs and handle some missing cases like 'hh' or '%[ieEaA]'. Also, treats 'L' and 'll' synonymously as done by GCC. Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@xxxxxxxxx> --- verify-format.c | 265 +++++++++++++++++++++++++++--------------------- 1 file changed, 151 insertions(+), 114 deletions(-) diff --git a/verify-format.c b/verify-format.c index 4b4730285237..ae5bb2e6e985 100644 --- a/verify-format.c +++ b/verify-format.c @@ -44,6 +44,14 @@ #include "expression.h" #include "verify-format.h" +enum length_mod { + LEN_none, + LEN_hh, LEN_h, + LEN_l, LEN_ll, + LEN_L, + LEN_j, LEN_z, LEN_t, +}; + struct format_type { const char *format; int (*test)(struct format_type *fmt, @@ -113,140 +121,169 @@ static struct format_type printf_fmt_ptr_ref = { .test = printf_fmt_pointer, }; -static int is_float_spec(char t) -{ - return t == 'f' || t == 'g' || t == 'F' || t == 'G'; -} - static struct format_type *parse_printf_get_fmt(struct format_type *type, const char *msg, const char **msgout) { const char *ptr = msg; - int szmod=0; + int szmod = LEN_none; type->test = NULL; *msgout = ptr; - if (*ptr == 's') { - ptr++; - type->test = printf_fmt_string; - type->type = &const_string_ctype; - } else if (*ptr == 'c') { - ptr++; - type->test = printf_fmt_numtype; - type->type = &char_ctype; - } else if (*ptr == 'p') { - ptr++; - type->test = printf_fmt_print_pointer; - /* check for pointer being printed as hex explicitly */ - if (*ptr == 'x' || *ptr == 'X') { + switch (*ptr++) { + case 'h': + szmod = LEN_h; + if (*ptr == 'h') { + szmod = LEN_hh; ptr++; - } else if (isalpha(*ptr)) { - /* probably some extra specifiers after %p */ + } + break; + case 'l': + szmod = LEN_l; + if (*ptr == 'l') { + szmod = LEN_ll; ptr++; - type->test = printf_fmt_pointer; } - } else if (*ptr == 'z') { - // todo - we should construct pointer to int/etc // + break; + case 'q': + szmod = LEN_ll; + break; + case 'L': + szmod = LEN_L; + break; + case 'j': + szmod = LEN_j; + break; + case 'z': + szmod = LEN_z; + break; + case 't': + szmod = LEN_t; + break; + default: + ptr--; + break; + } - ptr++; - if (*ptr == 'd' || *ptr == 'i') { - ptr++; - type->test = printf_fmt_numtype; + switch (*ptr++) { + case 'd': case 'i': + type->test = printf_fmt_numtype; + switch (szmod) { + case LEN_hh: + case LEN_h: + case LEN_none: + type->type = &int_ctype; + break; + case LEN_l: + type->type = &long_ctype; + break; + case LEN_L: + case LEN_ll: + type->type = &llong_ctype; + break; + case LEN_j: + type->type = intmax_ctype; + break; + case LEN_z: type->type = ssize_t_ctype; - } else if (*ptr == 'u' || *ptr == 'x' || *ptr == 'X' || - *ptr == 'o') { - ptr++; - type->test = printf_fmt_numtype; - type->type = size_t_ctype; + break; + case LEN_t: + type->type = ptrdiff_ctype; + break; + default: + type->test = NULL; } - } else { - if (*ptr == 'l') { - szmod++; - ptr++; - if (*ptr == 'l') { - szmod++; - ptr++; - } - } else if (*ptr == 'L') { - szmod++; - ptr++; - } else { - if (*ptr == 'h') { // short/char to int - szmod = -1; - ptr++; - if (*ptr == 'h') // promotion from char - ptr++; - } - if (*ptr == 't') { // ptrdiff_t - szmod = 2; - ptr++; - } - if (*ptr == 'j') { // intmax_t - // todo - replace iwth intmax_ctype when added - szmod = 1; - ptr++; - } + break; + case 'u': case 'o': case 'x': case 'X': + type->test = printf_fmt_numtype; + switch (szmod) { + case LEN_hh: + case LEN_h: + case LEN_none: + type->type = &uint_ctype; + break; + case LEN_l: + type->type = &ulong_ctype; + break; + case LEN_L: + case LEN_ll: + type->type = &ullong_ctype; + break; + case LEN_j: + type->type = uintmax_ctype; + break; + case LEN_z: + type->type = size_t_ctype; + break; + case LEN_t: + type->type = ptrdiff_ctype; + break; + default: + type->test = NULL; } - - if (*ptr == 'x' || *ptr == 'X' || *ptr == 'u' || *ptr == 'o') { - ptr++; - type->test = printf_fmt_numtype; - switch (szmod) { - case -1: - type->type = &ushort_ctype; - break; - case 0: - type->type = &uint_ctype; - break; - case 1: - type->type = &ulong_ctype; - break; - case 2: - type->type = &ullong_ctype; - break; - default: - type->test = NULL; - } - } else if (*ptr == 'i' || *ptr == 'd') { - ptr++; - type->test = printf_fmt_numtype; - switch (szmod) { - case -1: - type->type = &short_ctype; - break; - case 0: - type->type = &int_ctype; - break; - case 1: - type->type = &long_ctype; - break; - case 2: - type->type = &llong_ctype; - break; - default: - type->test = NULL; - } - } else if (*ptr == 'L' && is_float_spec(ptr[1])) { - type->test = printf_fmt_numtype; + break; + case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': + case 'a': case 'A': + type->test = printf_fmt_numtype; + switch (szmod) { + case LEN_none: + type->type = &double_ctype; + break; + case LEN_L: type->type = &ldouble_ctype; - ptr += 2; - } else if (is_float_spec(*ptr)) { - type->test = printf_fmt_numtype; - type->type = szmod == 1 ? &ldouble_ctype : &double_ctype; - ptr++; - } else if (*ptr == 's') { - type->test = printf_fmt_string; + break; + default: + break; + } + break; + case 'c': + type->test = printf_fmt_numtype; + switch (szmod) { + case LEN_none: + type->type = &int_ctype; + break; + case LEN_L: + case LEN_l: + type->type = wint_ctype; + break; + default: + break; + } + break; + case 's': + type->test = printf_fmt_string; + switch (szmod) { + case LEN_none: + type->type = &const_string_ctype; + break; + case LEN_L: + case LEN_l: type->type = &const_wstring_ctype; + break; + default: + break; + } + break; + case 'p': + type->test = printf_fmt_print_pointer; + /* check for pointer being printed as hex explicitly */ + if (*ptr == 'x' || *ptr == 'X') { ptr++; - } else if (*ptr == 'n') { /* pointer to an de-referenced int/etc */ - // todo - we should construct pointer to int/etc // - // also should not have any flags or widths for this - type->test = printf_fmt_pointer; + } else if (isalpha(*ptr)) { + /* probably some extra specifiers after %p */ ptr++; - } else { - // anything else here? + type->test = printf_fmt_pointer; } + break; + case 'n': + /* pointer to an de-referenced int/etc */ + // todo - we should construct pointer to int/etc // + // also should not have any flags or widths for this + type->test = printf_fmt_pointer; + break; + default: + // anything else here? + break; } if (type->test == NULL) -- 2.28.0