static void * format_parse (const char *format, bool translated, char **invalid_reason) { struct spec spec; unsigned int unnumbered_arg_count; struct spec *result; spec.directives = 0; spec.numbered_arg_count = 0; spec.allocated = 0; spec.numbered = NULL; unnumbered_arg_count = 0; for (; *format != '\0';) if (*format++ == '%') { /* A directive. */ spec.directives++; if (*format == '%') format++; else { bool brackets = false; bool done = false; unsigned int number = 0; enum format_arg_type type = FAT_NONE; if (*format == '|') { format++; brackets = true; } if (isdigit (*format) && *format != '0') { const char *f = format; unsigned int m = 0; do { m = 10 * m + (*f - '0'); f++; } while (isdigit (*f)); if ((!brackets && *f == '%') || *f == '$') { if (m == 0) /* can happen if m overflows */ { *invalid_reason = INVALID_ARGNO_0 (spec.directives); goto bad_format; } number = m; if (*f == '%') { type = FAT_ANY; done = true; } format = ++f; } } if (!done) { /* Parse flags. */ for (;;) { if (*format == ' ' || *format == '+' || *format == '-' || *format == '#' || *format == '0' || *format == '\'' || *format == '_' || *format == '=' || *format == 'h' || *format == 'l') format++; else break; } /* Parse width. */ if (*format == '*') { unsigned int width_number = 0; format++; if (isdigit (*format)) { const char *f = format; unsigned int m = 0; do { m = 10 * m + (*f - '0'); f++; } while (isdigit (*f)); if (*f == '$') { if (m == 0) { *invalid_reason = INVALID_WIDTH_ARGNO_0 (spec.directives); goto bad_format; } width_number = m; format = ++f; } } if (width_number) { /* Numbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (unnumbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); goto bad_format; } if (spec.allocated == spec.numbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[spec.numbered_arg_count].number = width_number; spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER; spec.numbered_arg_count++; } else { /* Unnumbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (spec.numbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); goto bad_format; } if (spec.allocated == unnumbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1; spec.numbered[unnumbered_arg_count].type = FAT_INTEGER; unnumbered_arg_count++; } } else if (isdigit (*format)) { do format++; while (isdigit (*format)); } /* Parse precision. */ if (*format == '.') { format++; if (*format == '*') { unsigned int precision_number = 0; format++; if (isdigit (*format)) { const char *f = format; unsigned int m = 0; do { m = 10 * m + (*f - '0'); f++; } while (isdigit (*f)); if (*f == '$') { if (m == 0) { *invalid_reason = INVALID_PRECISION_ARGNO_0 (spec.directives); goto bad_format; } precision_number = m; format = ++f; } } if (precision_number) { /* Numbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (unnumbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); goto bad_format; } if (spec.allocated == spec.numbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[spec.numbered_arg_count].number = precision_number; spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER; spec.numbered_arg_count++; } else { /* Unnumbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (spec.numbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); goto bad_format; } if (spec.allocated == unnumbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1; spec.numbered[unnumbered_arg_count].type = FAT_INTEGER; unnumbered_arg_count++; } } else if (isdigit (*format)) { do format++; while (isdigit (*format)); } } /* Parse size. */ for (;;) { if (*format == 'h' || *format == 'l' || *format == 'L') format++; else break; } switch (*format++) { case 'c': case 'C': type = FAT_CHAR; break; case 's': case 'S': type = FAT_ANY; break; case 'i': case 'd': case 'o': case 'u': case 'x': case 'X': type = FAT_INTEGER; break; case 'e': case 'E': case 'f': case 'g': case 'G': type = FAT_DOUBLE; break; case 'p': type = FAT_POINTER; break; case 't': type = FAT_NONE; break; case 'T': if (*format == '\0') { *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE (); goto bad_format; } format++; type = FAT_NONE; break; case 'n': type = FAT_NONE; break; case '|': if (brackets) { --format; type = FAT_ANY; break; } /*FALLTHROUGH*/ default: --format; *invalid_reason = (*format == '\0' ? INVALID_UNTERMINATED_DIRECTIVE () : INVALID_CONVERSION_SPECIFIER (spec.directives, *format)); goto bad_format; } if (brackets) { if (*format != '|') { *invalid_reason = (*format == '\0' ? INVALID_UNTERMINATED_DIRECTIVE () : xasprintf (_("The directive number %u starts with | but does not end with |."), spec.directives)); goto bad_format; } format++; } } if (type != FAT_NONE) { if (number) { /* Numbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (unnumbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); goto bad_format; } if (spec.allocated == spec.numbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[spec.numbered_arg_count].number = number; spec.numbered[spec.numbered_arg_count].type = type; spec.numbered_arg_count++; } else { /* Unnumbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (spec.numbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); goto bad_format; } if (spec.allocated == unnumbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1; spec.numbered[unnumbered_arg_count].type = type; unnumbered_arg_count++; } } } } /* Convert the unnumbered argument array to numbered arguments. */ if (unnumbered_arg_count > 0) spec.numbered_arg_count = unnumbered_arg_count; /* Sort the numbered argument array, and eliminate duplicates. */ else if (spec.numbered_arg_count > 1) { unsigned int i, j; bool err; qsort (spec.numbered, spec.numbered_arg_count, sizeof (struct numbered_arg), numbered_arg_compare); /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */ err = false; for (i = j = 0; i < spec.numbered_arg_count; i++) if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number) { enum format_arg_type type1 = spec.numbered[i].type; enum format_arg_type type2 = spec.numbered[j-1].type; enum format_arg_type type_both; if (type1 == type2 || type2 == FAT_ANY) type_both = type1; else if (type1 == FAT_ANY) type_both = type2; else { /* Incompatible types. */ type_both = FAT_NONE; if (!err) *invalid_reason = INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number); err = true; } spec.numbered[j-1].type = type_both; } else { if (j < i) { spec.numbered[j].number = spec.numbered[i].number; spec.numbered[j].type = spec.numbered[i].type; } j++; } spec.numbered_arg_count = j; if (err) /* *invalid_reason has already been set above. */ goto bad_format; } result = (struct spec *) xmalloc (sizeof (struct spec)); *result = spec; return result; bad_format: if (spec.numbered != NULL) free (spec.numbered); return NULL; }
static void * format_parse (const char *format, bool translated, char *fdi, char **invalid_reason) { const char *const format_start = format; struct spec spec; unsigned int unnumbered_arg_count; struct spec *result; spec.directives = 0; spec.numbered_arg_count = 0; spec.allocated = 0; spec.numbered = NULL; unnumbered_arg_count = 0; for (; *format != '\0';) if (*format++ == '%') { /* A directive. */ unsigned int number = 0; enum format_arg_type type; FDI_SET (format - 1, FMTDIR_START); spec.directives++; if (isdigit (*format)) { const char *f = format; unsigned int m = 0; do { m = 10 * m + (*f - '0'); f++; } while (isdigit (*f)); if (*f == '$') { if (m == 0) { *invalid_reason = INVALID_ARGNO_0 (spec.directives); FDI_SET (f, FMTDIR_ERROR); goto bad_format; } number = m; format = ++f; } } /* Parse flags. */ while (*format == '-' || *format == '+' || *format == ' ' || *format == '0' || *format == 'I') format++; /* Parse width. */ while (isdigit (*format)) format++; if (*format == '.') { format++; while (isdigit (*format)) format++; } switch (*format) { case '%': type = FAT_NONE; break; case 'c': type = FAT_CHARACTER; break; case 's': type = FAT_STRING; break; case 'b': case 'd': case 'o': case 'x': case 'X': type = FAT_INTEGER; break; case 'f': type = FAT_FLOAT; break; case 'j': type = FAT_ANY; break; default: if (*format == '\0') { *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE (); FDI_SET (format - 1, FMTDIR_ERROR); } else { *invalid_reason = INVALID_CONVERSION_SPECIFIER (spec.directives, *format); FDI_SET (format, FMTDIR_ERROR); } goto bad_format; } if (type != FAT_NONE) { if (number) { /* Numbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (unnumbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); FDI_SET (format, FMTDIR_ERROR); goto bad_format; } if (spec.allocated == spec.numbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[spec.numbered_arg_count].number = number; spec.numbered[spec.numbered_arg_count].type = type; spec.numbered_arg_count++; } else { /* Unnumbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (spec.numbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); FDI_SET (format, FMTDIR_ERROR); goto bad_format; } if (spec.allocated == unnumbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1; spec.numbered[unnumbered_arg_count].type = type; unnumbered_arg_count++; } } FDI_SET (format, FMTDIR_END); format++; } /* Convert the unnumbered argument array to numbered arguments. */ if (unnumbered_arg_count > 0) spec.numbered_arg_count = unnumbered_arg_count; /* Sort the numbered argument array, and eliminate duplicates. */ else if (spec.numbered_arg_count > 1) { unsigned int i, j; bool err; qsort (spec.numbered, spec.numbered_arg_count, sizeof (struct numbered_arg), numbered_arg_compare); /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */ err = false; for (i = j = 0; i < spec.numbered_arg_count; i++) if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number) { enum format_arg_type type1 = spec.numbered[i].type; enum format_arg_type type2 = spec.numbered[j-1].type; enum format_arg_type type_both; if (type1 == type2) type_both = type1; else { /* Incompatible types. */ type_both = FAT_NONE; if (!err) *invalid_reason = INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number); err = true; } spec.numbered[j-1].type = type_both; } else { if (j < i) { spec.numbered[j].number = spec.numbered[i].number; spec.numbered[j].type = spec.numbered[i].type; } j++; } spec.numbered_arg_count = j; if (err) /* *invalid_reason has already been set above. */ goto bad_format; } result = XMALLOC (struct spec); *result = spec; return result; bad_format: if (spec.numbered != NULL) free (spec.numbered); return NULL; }
static void * format_parse (const char *format, bool translated, bool objc_extensions, char *fdi, char **invalid_reason) { const char *const format_start = format; struct spec spec; unsigned int numbered_arg_count; struct numbered_arg *numbered; struct spec *result; spec.directives = 0; numbered_arg_count = 0; spec.unnumbered_arg_count = 0; spec.allocated = 0; numbered = NULL; spec.unnumbered = NULL; spec.unlikely_intentional = false; spec.sysdep_directives_count = 0; spec.sysdep_directives = NULL; for (; *format != '\0';) if (*format++ == '%') { /* A directive. */ unsigned int number = 0; format_arg_type_t type; format_arg_type_t size; FDI_SET (format - 1, FMTDIR_START); spec.directives++; if (isdigit (*format)) { const char *f = format; unsigned int m = 0; do { m = 10 * m + (*f - '0'); f++; } while (isdigit (*f)); if (*f == '$') { if (m == 0) { *invalid_reason = INVALID_ARGNO_0 (spec.directives); FDI_SET (f, FMTDIR_ERROR); goto bad_format; } number = m; format = ++f; } } /* Parse flags. */ for (;;) { if (*format == ' ' || *format == '+' || *format == '-' || *format == '#' || *format == '0' || *format == '\'') format++; else if (translated && *format == 'I') { spec.sysdep_directives = (const char **) xrealloc (spec.sysdep_directives, 2 * (spec.sysdep_directives_count + 1) * sizeof (const char *)); spec.sysdep_directives[2 * spec.sysdep_directives_count] = format; spec.sysdep_directives[2 * spec.sysdep_directives_count + 1] = format + 1; spec.sysdep_directives_count++; format++; } else break; } /* Parse width. */ if (*format == '*') { unsigned int width_number = 0; format++; if (isdigit (*format)) { const char *f = format; unsigned int m = 0; do { m = 10 * m + (*f - '0'); f++; } while (isdigit (*f)); if (*f == '$') { if (m == 0) { *invalid_reason = INVALID_WIDTH_ARGNO_0 (spec.directives); FDI_SET (f, FMTDIR_ERROR); goto bad_format; } width_number = m; format = ++f; } } if (width_number) { /* Numbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (spec.unnumbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); FDI_SET (format - 1, FMTDIR_ERROR); goto bad_format; } if (spec.allocated == numbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; numbered = (struct numbered_arg *) xrealloc (numbered, spec.allocated * sizeof (struct numbered_arg)); } numbered[numbered_arg_count].number = width_number; numbered[numbered_arg_count].type = FAT_INTEGER; numbered_arg_count++; } else { /* Unnumbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (numbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); FDI_SET (format - 1, FMTDIR_ERROR); goto bad_format; } if (spec.allocated == spec.unnumbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg)); } spec.unnumbered[spec.unnumbered_arg_count].type = FAT_INTEGER; spec.unnumbered_arg_count++; } } else if (isdigit (*format)) { do format++; while (isdigit (*format)); } /* Parse precision. */ if (*format == '.') { format++; if (*format == '*') { unsigned int precision_number = 0; format++; if (isdigit (*format)) { const char *f = format; unsigned int m = 0; do { m = 10 * m + (*f - '0'); f++; } while (isdigit (*f)); if (*f == '$') { if (m == 0) { *invalid_reason = INVALID_PRECISION_ARGNO_0 (spec.directives); FDI_SET (f, FMTDIR_ERROR); goto bad_format; } precision_number = m; format = ++f; } } if (precision_number) { /* Numbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (spec.unnumbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); FDI_SET (format - 1, FMTDIR_ERROR); goto bad_format; } if (spec.allocated == numbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; numbered = (struct numbered_arg *) xrealloc (numbered, spec.allocated * sizeof (struct numbered_arg)); } numbered[numbered_arg_count].number = precision_number; numbered[numbered_arg_count].type = FAT_INTEGER; numbered_arg_count++; } else { /* Unnumbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (numbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); FDI_SET (format - 1, FMTDIR_ERROR); goto bad_format; } if (spec.allocated == spec.unnumbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg)); } spec.unnumbered[spec.unnumbered_arg_count].type = FAT_INTEGER; spec.unnumbered_arg_count++; } } else if (isdigit (*format)) { do format++; while (isdigit (*format)); } } if (*format == '<') { spec.sysdep_directives = (const char **) xrealloc (spec.sysdep_directives, 2 * (spec.sysdep_directives_count + 1) * sizeof (const char *)); spec.sysdep_directives[2 * spec.sysdep_directives_count] = format; format++; /* Parse ISO C 99 section 7.8.1 format string directive. Syntax: P R I { d | i | o | u | x | X } { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR } */ if (*format != 'P') { *invalid_reason = INVALID_C99_MACRO (spec.directives); FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR); goto bad_format; } format++; if (*format != 'R') { *invalid_reason = INVALID_C99_MACRO (spec.directives); FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR); goto bad_format; } format++; if (*format != 'I') { *invalid_reason = INVALID_C99_MACRO (spec.directives); FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR); goto bad_format; } format++; switch (*format) { case 'i': case 'd': type = FAT_INTEGER; break; case 'u': case 'o': case 'x': case 'X': type = FAT_INTEGER | FAT_UNSIGNED; break; default: *invalid_reason = INVALID_C99_MACRO (spec.directives); FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR); goto bad_format; } format++; if (format[0] == 'M' && format[1] == 'A' && format[2] == 'X') { type |= FAT_SIZE_INTMAX_T; format += 3; } else if (format[0] == 'P' && format[1] == 'T' && format[2] == 'R') { type |= FAT_SIZE_INTPTR_T; format += 3; } else { if (format[0] == 'L' && format[1] == 'E' && format[2] == 'A' && format[3] == 'S' && format[4] == 'T') { format += 5; if (format[0] == '8') { type |= FAT_SIZE_LEAST8_T; format++; } else if (format[0] == '1' && format[1] == '6') { type |= FAT_SIZE_LEAST16_T; format += 2; } else if (format[0] == '3' && format[1] == '2') { type |= FAT_SIZE_LEAST32_T; format += 2; } else if (format[0] == '6' && format[1] == '4') { type |= FAT_SIZE_LEAST64_T; format += 2; } else { *invalid_reason = INVALID_C99_MACRO (spec.directives); FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR); goto bad_format; } } else if (format[0] == 'F' && format[1] == 'A' && format[2] == 'S' && format[3] == 'T') { format += 4; if (format[0] == '8') { type |= FAT_SIZE_FAST8_T; format++; } else if (format[0] == '1' && format[1] == '6') { type |= FAT_SIZE_FAST16_T; format += 2; } else if (format[0] == '3' && format[1] == '2') { type |= FAT_SIZE_FAST32_T; format += 2; } else if (format[0] == '6' && format[1] == '4') { type |= FAT_SIZE_FAST64_T; format += 2; } else { *invalid_reason = INVALID_C99_MACRO (spec.directives); FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR); goto bad_format; } } else { if (format[0] == '8') { type |= FAT_SIZE_8_T; format++; } else if (format[0] == '1' && format[1] == '6') { type |= FAT_SIZE_16_T; format += 2; } else if (format[0] == '3' && format[1] == '2') { type |= FAT_SIZE_32_T; format += 2; } else if (format[0] == '6' && format[1] == '4') { type |= FAT_SIZE_64_T; format += 2; } else { *invalid_reason = INVALID_C99_MACRO (spec.directives); FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR); goto bad_format; } } } if (*format != '>') { *invalid_reason = xasprintf (_("In the directive number %u, the token after '<' is not followed by '>'."), spec.directives); FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR); goto bad_format; } spec.sysdep_directives[2 * spec.sysdep_directives_count + 1] = format + 1; spec.sysdep_directives_count++; } else { /* Parse size. */ size = 0; for (;; format++) { if (*format == 'h') { if (size & (FAT_SIZE_SHORT | FAT_SIZE_CHAR)) size = FAT_SIZE_CHAR; else size = FAT_SIZE_SHORT; } else if (*format == 'l') { if (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG)) size = FAT_SIZE_LONGLONG; else size = FAT_SIZE_LONG; } else if (*format == 'L') size = FAT_SIZE_LONGLONG; else if (*format == 'q') /* Old BSD 4.4 convention. */ size = FAT_SIZE_LONGLONG; else if (*format == 'j') size = FAT_SIZE_INTMAX_T; else if (*format == 'z' || *format == 'Z') /* 'z' is standardized in ISO C 99, but glibc uses 'Z' because the warning facility in gcc-2.95.2 understands only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */ size = FAT_SIZE_SIZE_T; else if (*format == 't') size = FAT_SIZE_PTRDIFF_T; else break; } switch (*format) { case '%': /* Programmers writing _("%2%") most often will not want to use this string as a c-format string, but rather as a literal or as a different kind of format string. */ if (format[-1] != '%') spec.unlikely_intentional = true; type = FAT_NONE; break; case 'm': /* glibc extension */ type = FAT_NONE; break; case 'c': type = FAT_CHAR; type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG) ? FAT_WIDE : 0); break; case 'C': /* obsolete */ type = FAT_CHAR | FAT_WIDE; break; case 's': type = FAT_STRING; type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG) ? FAT_WIDE : 0); break; case 'S': /* obsolete */ type = FAT_STRING | FAT_WIDE; break; case 'i': case 'd': type = FAT_INTEGER; type |= (size & FAT_SIZE_MASK); break; case 'u': case 'o': case 'x': case 'X': type = FAT_INTEGER | FAT_UNSIGNED; type |= (size & FAT_SIZE_MASK); break; case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'a': case 'A': type = FAT_DOUBLE; type |= (size & FAT_SIZE_LONGLONG); break; case '@': if (objc_extensions) { type = FAT_OBJC_OBJECT; break; } goto other; case 'p': type = FAT_POINTER; break; case 'n': type = FAT_COUNT_POINTER; type |= (size & FAT_SIZE_MASK); break; other: default: if (*format == '\0') { *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE (); FDI_SET (format - 1, FMTDIR_ERROR); } else { *invalid_reason = INVALID_CONVERSION_SPECIFIER (spec.directives, *format); FDI_SET (format, FMTDIR_ERROR); } goto bad_format; } } if (type != FAT_NONE) { if (number) { /* Numbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (spec.unnumbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); FDI_SET (format, FMTDIR_ERROR); goto bad_format; } if (spec.allocated == numbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; numbered = (struct numbered_arg *) xrealloc (numbered, spec.allocated * sizeof (struct numbered_arg)); } numbered[numbered_arg_count].number = number; numbered[numbered_arg_count].type = type; numbered_arg_count++; } else { /* Unnumbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (numbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); FDI_SET (format, FMTDIR_ERROR); goto bad_format; } if (spec.allocated == spec.unnumbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg)); } spec.unnumbered[spec.unnumbered_arg_count].type = type; spec.unnumbered_arg_count++; } } FDI_SET (format, FMTDIR_END); format++; } /* Sort the numbered argument array, and eliminate duplicates. */ if (numbered_arg_count > 1) { unsigned int i, j; bool err; qsort (numbered, numbered_arg_count, sizeof (struct numbered_arg), numbered_arg_compare); /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */ err = false; for (i = j = 0; i < numbered_arg_count; i++) if (j > 0 && numbered[i].number == numbered[j-1].number) { format_arg_type_t type1 = numbered[i].type; format_arg_type_t type2 = numbered[j-1].type; format_arg_type_t type_both; if (type1 == type2) type_both = type1; else { /* Incompatible types. */ type_both = FAT_NONE; if (!err) *invalid_reason = INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number); err = true; } numbered[j-1].type = type_both; } else { if (j < i) { numbered[j].number = numbered[i].number; numbered[j].type = numbered[i].type; } j++; } numbered_arg_count = j; if (err) /* *invalid_reason has already been set above. */ goto bad_format; } /* Verify that the format strings uses all arguments up to the highest numbered one. */ if (numbered_arg_count > 0) { unsigned int i; for (i = 0; i < numbered_arg_count; i++) if (numbered[i].number != i + 1) { *invalid_reason = xasprintf (_("The string refers to argument number %u but ignores argument number %u."), numbered[i].number, i + 1); goto bad_format; } /* So now the numbered arguments array is equivalent to a sequence of unnumbered arguments. */ spec.unnumbered_arg_count = numbered_arg_count; spec.allocated = spec.unnumbered_arg_count; spec.unnumbered = XNMALLOC (spec.allocated, struct unnumbered_arg); for (i = 0; i < spec.unnumbered_arg_count; i++) spec.unnumbered[i].type = numbered[i].type; free (numbered); numbered_arg_count = 0; }
static void * format_parse (const char *format, bool translated, char *fdi, char **invalid_reason) { const char *const format_start = format; struct spec spec; unsigned int unnumbered_arg_count; struct spec *result; spec.directives = 0; spec.numbered_arg_count = 0; spec.allocated = 0; spec.numbered = NULL; spec.uses_err_no = false; unnumbered_arg_count = 0; for (; *format != '\0';) if (*format++ == '%') { /* A directive. */ FDI_SET (format - 1, FMTDIR_START); spec.directives++; if (*format == '%' || *format == '<' || *format == '>' || *format == '\'') ; else if (*format == 'm') spec.uses_err_no = true; else { unsigned int number = 0; unsigned int flag_q = 0; unsigned int flag_l = 0; unsigned int flag_w = 0; unsigned int flag_plus = 0; unsigned int flag_sharp = 0; format_arg_type_t size; format_arg_type_t type; if (isdigit (*format)) { const char *f = format; unsigned int m = 0; do { m = 10 * m + (*f - '0'); f++; } while (isdigit (*f)); if (*f == '$') { if (m == 0) { *invalid_reason = INVALID_ARGNO_0 (spec.directives); FDI_SET (f, FMTDIR_ERROR); goto bad_format; } number = m; format = ++f; } } /* Parse flags and size. */ for (;; format++) { switch (*format) { case 'q': if (flag_q > 0) goto invalid_flags; flag_q = 1; continue; case 'l': if (flag_l > 1 || flag_w) goto invalid_flags; flag_l++; continue; case 'w': if (flag_w > 0 || flag_l) goto invalid_flags; flag_w = 1; continue; case '+': if (flag_plus > 0) goto invalid_flags; flag_plus = 1; continue; case '#': if (flag_sharp > 0) goto invalid_flags; flag_sharp = 1; continue; invalid_flags: *invalid_reason = xasprintf (_("In the directive number %u, the flags combination is invalid."), spec.directives); FDI_SET (format, FMTDIR_ERROR); goto bad_format; default: break; } break; } size = (flag_l == 2 ? FAT_SIZE_LONGLONG : flag_l == 1 ? FAT_SIZE_LONG : flag_w ? FAT_SIZE_WIDE : 0); if (*format == 'c') type = FAT_CHAR; else if (*format == 's') type = FAT_STRING; else if (*format == '.') { format++; if (isdigit (*format)) { do format++; while (isdigit (*format)); if (*format != 's') { if (*format == '\0') { *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE (); FDI_SET (format - 1, FMTDIR_ERROR); } else { *invalid_reason = xasprintf (_("In the directive number %u, a precision is not allowed before '%c'."), spec.directives, *format); FDI_SET (format, FMTDIR_ERROR); } goto bad_format; } type = FAT_STRING; } else if (*format == '*') { unsigned int precision_number = 0; format++; if (isdigit (*format)) { const char *f = format; unsigned int m = 0; do { m = 10 * m + (*f - '0'); f++; } while (isdigit (*f)); if (*f == '$') { if (m == 0) { *invalid_reason = INVALID_WIDTH_ARGNO_0 (spec.directives); FDI_SET (f, FMTDIR_ERROR); goto bad_format; } if (unnumbered_arg_count > 0 || number == 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); FDI_SET (f, FMTDIR_ERROR); goto bad_format; } if (m != number - 1) { *invalid_reason = xasprintf (_("In the directive number %u, the argument number for the precision must be equal to %u."), spec.directives, number - 1); FDI_SET (f, FMTDIR_ERROR); goto bad_format; } precision_number = m; format = ++f; } } if (precision_number) { /* Numbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (unnumbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); FDI_SET (format - 1, FMTDIR_ERROR); goto bad_format; } if (spec.allocated == spec.numbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[spec.numbered_arg_count].number = precision_number; spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER; spec.numbered_arg_count++; } else { /* Unnumbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (spec.numbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); FDI_SET (format - 1, FMTDIR_ERROR); goto bad_format; } if (spec.allocated == unnumbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1; spec.numbered[unnumbered_arg_count].type = FAT_INTEGER; unnumbered_arg_count++; } if (*format == 's') type = FAT_STRING; else { if (*format == '\0') { *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE (); FDI_SET (format - 1, FMTDIR_ERROR); } else { *invalid_reason = xasprintf (_("In the directive number %u, a precision specification is not allowed before '%c'."), spec.directives, *format); FDI_SET (format, FMTDIR_ERROR); } goto bad_format; } } else { *invalid_reason = xasprintf (_("In the directive number %u, the precision specification is invalid."), spec.directives); FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR); goto bad_format; } } else if (*format == 'i' || *format == 'd') type = FAT_INTEGER | size; else if (*format == 'o' || *format == 'u' || *format == 'x') type = FAT_INTEGER | FAT_UNSIGNED | size; else if (*format == 'p') type = FAT_POINTER; else if (*format == 'H') type = FAT_LOCATION; else if (*format == 'J') type = FAT_TREE | FAT_TREE_DECL; else if (*format == 'K') type = FAT_TREE | FAT_TREE_STATEMENT; else { if (*format == 'D') type = FAT_TREE | FAT_TREE_DECL; else if (*format == 'F') type = FAT_TREE | FAT_TREE_FUNCDECL; else if (*format == 'T') type = FAT_TREE | FAT_TREE_TYPE; else if (*format == 'E') type = FAT_TREE | FAT_TREE_EXPRESSION; else if (*format == 'A') type = FAT_TREE | FAT_TREE_ARGUMENT; else if (*format == 'C') type = FAT_TREE_CODE; else if (*format == 'L') type = FAT_LANGUAGES; else if (*format == 'O') type = FAT_TREE_CODE | FAT_TREE_CODE_BINOP; else if (*format == 'P') type = FAT_INTEGER | FAT_FUNCPARAM; else if (*format == 'Q') type = FAT_TREE_CODE | FAT_TREE_CODE_ASSOP; else if (*format == 'V') type = FAT_TREE | FAT_TREE_CV; else { if (*format == '\0') { *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE (); FDI_SET (format - 1, FMTDIR_ERROR); } else { *invalid_reason = (*format == 'c' || *format == 's' || *format == 'i' || *format == 'd' || *format == 'o' || *format == 'u' || *format == 'x' || *format == 'H' ? xasprintf (_("In the directive number %u, flags are not allowed before '%c'."), spec.directives, *format) : INVALID_CONVERSION_SPECIFIER (spec.directives, *format)); FDI_SET (format, FMTDIR_ERROR); } goto bad_format; } } if (number) { /* Numbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (unnumbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); FDI_SET (format, FMTDIR_ERROR); goto bad_format; } if (spec.allocated == spec.numbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[spec.numbered_arg_count].number = number; spec.numbered[spec.numbered_arg_count].type = type; spec.numbered_arg_count++; } else { /* Unnumbered argument. */ /* Numbered and unnumbered specifications are exclusive. */ if (spec.numbered_arg_count > 0) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); FDI_SET (format, FMTDIR_ERROR); goto bad_format; } if (spec.allocated == unnumbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1; spec.numbered[unnumbered_arg_count].type = type; unnumbered_arg_count++; } } FDI_SET (format, FMTDIR_END); format++; } /* Convert the unnumbered argument array to numbered arguments. */ if (unnumbered_arg_count > 0) spec.numbered_arg_count = unnumbered_arg_count; /* Sort the numbered argument array, and eliminate duplicates. */ else if (spec.numbered_arg_count > 1) { unsigned int i, j; bool err; qsort (spec.numbered, spec.numbered_arg_count, sizeof (struct numbered_arg), numbered_arg_compare); /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */ err = false; for (i = j = 0; i < spec.numbered_arg_count; i++) if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number) { format_arg_type_t type1 = spec.numbered[i].type; format_arg_type_t type2 = spec.numbered[j-1].type; format_arg_type_t type_both; if (type1 == type2) type_both = type1; else { /* Incompatible types. */ type_both = FAT_NONE; if (!err) *invalid_reason = INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number); err = true; } spec.numbered[j-1].type = type_both; } else { if (j < i) { spec.numbered[j].number = spec.numbered[i].number; spec.numbered[j].type = spec.numbered[i].type; } j++; } spec.numbered_arg_count = j; if (err) /* *invalid_reason has already been set above. */ goto bad_format; } result = XMALLOC (struct spec); *result = spec; return result; bad_format: if (spec.numbered != NULL) free (spec.numbered); return NULL; }
static void * format_parse (const char *format, bool translated, char **invalid_reason) { struct spec spec; struct spec *result; bool seen_numbered_arg; bool seen_unnumbered_arg; unsigned int number; spec.directives = 0; spec.numbered_arg_count = 0; spec.allocated = 0; spec.numbered = NULL; seen_numbered_arg = false; seen_unnumbered_arg = false; number = 1; for (; *format != '\0';) if (*format++ == '%') { /* A directive. */ spec.directives++; if (*format != '%') { bool is_numbered_arg; bool short_flag; enum format_arg_type type; is_numbered_arg = false; if (isdigit (*format)) { const char *f = format; unsigned int m = 0; do { m = 10 * m + (*f - '0'); f++; } while (isdigit (*f)); if (*f == '$') { if (m == 0) { *invalid_reason = INVALID_ARGNO_0 (spec.directives); goto bad_format; } number = m; format = ++f; /* Numbered and unnumbered specifications are exclusive. */ if (seen_unnumbered_arg) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); goto bad_format; } is_numbered_arg = true; seen_numbered_arg = true; } } /* Numbered and unnumbered specifications are exclusive. */ if (!is_numbered_arg) { if (seen_numbered_arg) { *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED (); goto bad_format; } seen_unnumbered_arg = true; } /* Parse flags. */ while (*format == ' ' || *format == '+' || *format == '-' || *format == '#' || *format == '0') format++; /* Parse width. */ if (*format == '*') { format++; if (spec.allocated == spec.numbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[spec.numbered_arg_count].number = number; spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER; spec.numbered_arg_count++; number++; } else if (isdigit (*format)) { do format++; while (isdigit (*format)); } /* Parse precision. */ if (*format == '.') { format++; if (*format == '*') { format++; if (spec.allocated == spec.numbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[spec.numbered_arg_count].number = number; spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER; spec.numbered_arg_count++; number++; } else if (isdigit (*format)) { do format++; while (isdigit (*format)); } } /* Parse optional size specification. */ short_flag = false; if (*format == 'h') short_flag = true, format++; else if (*format == 'l') format++; switch (*format) { case 'c': type = FAT_CHARACTER; break; case 's': type = FAT_STRING; break; case 'i': case 'd': type = (short_flag ? FAT_SHORT_INTEGER : FAT_INTEGER); break; case 'u': case 'o': case 'x': case 'X': type = (short_flag ? FAT_SHORT_UNSIGNED_INTEGER : FAT_UNSIGNED_INTEGER); break; case 'e': case 'E': case 'f': case 'g': case 'G': type = FAT_FLOAT; break; default: *invalid_reason = (*format == '\0' ? INVALID_UNTERMINATED_DIRECTIVE () : INVALID_CONVERSION_SPECIFIER (spec.directives, *format)); goto bad_format; } if (spec.allocated == spec.numbered_arg_count) { spec.allocated = 2 * spec.allocated + 1; spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg)); } spec.numbered[spec.numbered_arg_count].number = number; spec.numbered[spec.numbered_arg_count].type = type; spec.numbered_arg_count++; number++; } format++; } /* Sort the numbered argument array, and eliminate duplicates. */ if (spec.numbered_arg_count > 1) { unsigned int i, j; bool err; qsort (spec.numbered, spec.numbered_arg_count, sizeof (struct numbered_arg), numbered_arg_compare); /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */ err = false; for (i = j = 0; i < spec.numbered_arg_count; i++) if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number) { enum format_arg_type type1 = spec.numbered[i].type; enum format_arg_type type2 = spec.numbered[j-1].type; enum format_arg_type type_both; if (type1 == type2) type_both = type1; else { /* Incompatible types. */ type_both = FAT_NONE; if (!err) *invalid_reason = INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number); err = true; } spec.numbered[j-1].type = type_both; } else { if (j < i) { spec.numbered[j].number = spec.numbered[i].number; spec.numbered[j].type = spec.numbered[i].type; } j++; } spec.numbered_arg_count = j; if (err) /* *invalid_reason has already been set above. */ goto bad_format; } result = (struct spec *) xmalloc (sizeof (struct spec)); *result = spec; return result; bad_format: if (spec.numbered != NULL) free (spec.numbered); return NULL; }