format-gcc-internal.c [plain text]
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdbool.h>
#include <stdlib.h>
#include "format.h"
#include "c-ctype.h"
#include "xalloc.h"
#include "xerror.h"
#include "format-invalid.h"
#include "error.h"
#include "error-progname.h"
#include "gettext.h"
#define _(str) gettext (str)
enum format_arg_type
{
FAT_NONE = 0,
FAT_INTEGER = 1,
FAT_CHAR = 2,
FAT_STRING = 3,
FAT_LOCATION = 4,
FAT_TREE = 5,
FAT_TREE_CODE = 6,
FAT_LANGUAGES = 7,
FAT_UNSIGNED = 1 << 3,
FAT_SIZE_LONG = 1 << 4,
FAT_TREE_DECL = 1 << 5,
FAT_TREE_FUNCDECL = 2 << 5,
FAT_TREE_TYPE = 3 << 5,
FAT_TREE_ARGUMENT = 4 << 5,
FAT_TREE_EXPRESSION = 5 << 5,
FAT_TREE_CV = 6 << 5,
FAT_TREE_CODE_BINOP = 1 << 8,
FAT_TREE_CODE_ASSOP = 2 << 8,
FAT_FUNCPARAM = 1 << 10
};
struct unnumbered_arg
{
enum format_arg_type type;
};
struct spec
{
unsigned int directives;
unsigned int unnumbered_arg_count;
unsigned int allocated;
struct unnumbered_arg *unnumbered;
};
static void *
format_parse (const char *format, char **invalid_reason)
{
struct spec spec;
struct spec *result;
spec.directives = 0;
spec.unnumbered_arg_count = 0;
spec.allocated = 0;
spec.unnumbered = NULL;
for (; *format != '\0';)
if (*format++ == '%')
{
enum format_arg_type size;
spec.directives++;
size = 0;
if (*format == 'l')
{
format++;
size = FAT_SIZE_LONG;
}
if (*format != '%')
{
enum format_arg_type type;
if (*format == 'c')
type = FAT_CHAR;
else if (*format == 's')
type = FAT_STRING;
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 == '.' && format[1] == '*' && format[2] == 's')
{
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++;
type = FAT_STRING;
}
else if (*format == 'H')
type = FAT_LOCATION;
else
{
if (*format == '+')
format++;
if (*format == '#')
format++;
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 == 'A')
type = FAT_TREE | FAT_TREE_ARGUMENT;
else if (*format == 'C')
type = FAT_TREE_CODE;
else if (*format == 'E')
type = FAT_TREE | FAT_TREE_EXPRESSION;
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
{
*invalid_reason =
(*format == '\0'
? INVALID_UNTERMINATED_DIRECTIVE ()
: (*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)));
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++;
}
format++;
}
result = (struct spec *) xmalloc (sizeof (struct spec));
*result = spec;
return result;
bad_format:
if (spec.unnumbered != NULL)
free (spec.unnumbered);
return NULL;
}
static void
format_free (void *descr)
{
struct spec *spec = (struct spec *) descr;
if (spec->unnumbered != NULL)
free (spec->unnumbered);
free (spec);
}
static int
format_get_number_of_directives (void *descr)
{
struct spec *spec = (struct spec *) descr;
return spec->directives;
}
static bool
format_check (const lex_pos_ty *pos, void *msgid_descr, void *msgstr_descr,
bool equality, bool noisy, const char *pretty_msgstr)
{
struct spec *spec1 = (struct spec *) msgid_descr;
struct spec *spec2 = (struct spec *) msgstr_descr;
bool err = false;
unsigned int i;
if (equality
? spec1->unnumbered_arg_count != spec2->unnumbered_arg_count
: spec1->unnumbered_arg_count < spec2->unnumbered_arg_count)
{
if (noisy)
{
error_with_progname = false;
error_at_line (0, 0, pos->file_name, pos->line_number,
_("number of format specifications in 'msgid' and '%s' does not match"),
pretty_msgstr);
error_with_progname = true;
}
err = true;
}
else
for (i = 0; i < spec2->unnumbered_arg_count; i++)
if (spec1->unnumbered[i].type != spec2->unnumbered[i].type)
{
if (noisy)
{
error_with_progname = false;
error_at_line (0, 0, pos->file_name, pos->line_number,
_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
pretty_msgstr, i + 1);
error_with_progname = true;
}
err = true;
}
return err;
}
struct formatstring_parser formatstring_gcc_internal =
{
format_parse,
format_free,
format_get_number_of_directives,
format_check
};
#ifdef TEST
#include <stdio.h>
#include "getline.h"
static void
format_print (void *descr)
{
struct spec *spec = (struct spec *) descr;
unsigned int i;
if (spec == NULL)
{
printf ("INVALID");
return;
}
printf ("(");
for (i = 0; i < spec->unnumbered_arg_count; i++)
{
if (i > 0)
printf (" ");
if (spec->unnumbered[i].type & FAT_UNSIGNED)
printf ("[unsigned]");
if (spec->unnumbered[i].type & FAT_SIZE_LONG)
printf ("[long]");
switch (spec->unnumbered[i].type & ~(FAT_UNSIGNED | FAT_SIZE_LONG))
{
case FAT_INTEGER:
printf ("i");
break;
case FAT_INTEGER | FAT_FUNCPARAM:
printf ("P");
break;
case FAT_CHAR:
printf ("c");
break;
case FAT_STRING:
printf ("s");
break;
case FAT_LOCATION:
printf ("H");
break;
case FAT_TREE | FAT_TREE_DECL:
printf ("D");
break;
case FAT_TREE | FAT_TREE_FUNCDECL:
printf ("F");
break;
case FAT_TREE | FAT_TREE_TYPE:
printf ("T");
break;
case FAT_TREE | FAT_TREE_ARGUMENT:
printf ("A");
break;
case FAT_TREE | FAT_TREE_EXPRESSION:
printf ("E");
break;
case FAT_TREE | FAT_TREE_CV:
printf ("V");
break;
case FAT_TREE_CODE:
printf ("C");
break;
case FAT_TREE_CODE | FAT_TREE_CODE_BINOP:
printf ("O");
break;
case FAT_TREE_CODE | FAT_TREE_CODE_ASSOP:
printf ("Q");
break;
case FAT_LANGUAGES:
printf ("L");
break;
default:
abort ();
}
}
printf (")");
}
int
main ()
{
for (;;)
{
char *line = NULL;
size_t line_size = 0;
int line_len;
char *invalid_reason;
void *descr;
line_len = getline (&line, &line_size, stdin);
if (line_len < 0)
break;
if (line_len > 0 && line[line_len - 1] == '\n')
line[--line_len] = '\0';
invalid_reason = NULL;
descr = format_parse (line, &invalid_reason);
format_print (descr);
printf ("\n");
if (descr == NULL)
printf ("%s\n", invalid_reason);
free (invalid_reason);
free (line);
}
return 0;
}
#endif