From f91947f6735ab16e62becb9905ecdfcdb7a02f64 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Fri, 8 Aug 2003 17:51:04 +0000 Subject: [PATCH] Support for gcc-internal format strings. --- gettext-tools/doc/ChangeLog | 7 + gettext-tools/doc/gettext.texi | 81 +++- gettext-tools/doc/xgettext.texi | 2 +- gettext-tools/src/ChangeLog | 12 + gettext-tools/src/Makefile.am | 2 +- gettext-tools/src/format-gcc-internal.c | 431 +++++++++++++++++++++ gettext-tools/src/format.c | 3 +- gettext-tools/src/format.h | 1 + gettext-tools/src/message.c | 6 +- gettext-tools/src/message.h | 5 +- gettext-tools/src/x-c.h | 1 + gettext-tools/src/xgettext.c | 2 +- gettext-tools/tests/ChangeLog | 6 + gettext-tools/tests/Makefile.am | 1 + gettext-tools/tests/format-gcc-internal-1 | 107 ++++++ gettext-tools/tests/format-gcc-internal-2 | 439 ++++++++++++++++++++++ 16 files changed, 1096 insertions(+), 10 deletions(-) create mode 100644 gettext-tools/src/format-gcc-internal.c create mode 100755 gettext-tools/tests/format-gcc-internal-1 create mode 100755 gettext-tools/tests/format-gcc-internal-2 diff --git a/gettext-tools/doc/ChangeLog b/gettext-tools/doc/ChangeLog index 9cac878cb..0b22322d0 100644 --- a/gettext-tools/doc/ChangeLog +++ b/gettext-tools/doc/ChangeLog @@ -1,3 +1,10 @@ +2003-08-08 Bruno Haible + + * gettext.texi (PO Files): Mention gcc-internal-format. + (gcc-internal-format): New subsection. + (GCC-source): New subsection. + * xgettext.texi: Mention GCC-source language. + 2003-07-05 Bruno Haible * gettext.texi (perl-format): Use braces, not brackets, in format diff --git a/gettext-tools/doc/gettext.texi b/gettext-tools/doc/gettext.texi index 9e0fa748a..0bed79d45 100644 --- a/gettext-tools/doc/gettext.texi +++ b/gettext-tools/doc/gettext.texi @@ -339,6 +339,7 @@ The Translator's View * tcl-format:: Tcl Format Strings * perl-format:: Perl Format Strings * php-format:: PHP Format Strings +* gcc-internal-format:: GCC internal Format Strings Individual Programming Languages @@ -360,6 +361,7 @@ Individual Programming Languages * Perl:: Perl * PHP:: PHP Hypertext Preprocessor * Pike:: Pike +* GCC-source:: GNU Compiler Collection sources Internationalizable Data @@ -1207,6 +1209,12 @@ Likewise for Tcl, see @ref{tcl-format}. @kwindex no-php-format@r{ flag} Likewise for PHP, see @ref{php-format}. +@item gcc-internal-format +@kwindex gcc-internal-format@r{ flag} +@itemx no-gcc-internal-format +@kwindex no-gcc-internal-format@r{ flag} +Likewise for the GCC sources, see @ref{gcc-internal-format}. + @end table @kwindex msgid_plural @@ -7217,6 +7225,7 @@ strings. * tcl-format:: Tcl Format Strings * perl-format:: Perl Format Strings * php-format:: PHP Format Strings +* gcc-internal-format:: GCC internal Format Strings @end menu @node c-format, python-format, Translators for other Languages, Translators for other Languages @@ -7346,13 +7355,31 @@ libintl-perl. In brief, Perl format uses placeholders put between braces (@samp{@{} and @samp{@}}). The placeholder must have the syntax of simple identifiers. -@node php-format, , perl-format, Translators for other Languages +@node php-format, gcc-internal-format, perl-format, Translators for other Languages @subsection PHP Format Strings PHP format strings are described in the documentation of the PHP function @code{sprintf}, in @file{phpdoc/manual/function.sprintf.html} or @uref{http://www.php.net/manual/en/function.sprintf.php}. +@node gcc-internal-format, , php-format, Translators for other Languages +@subsection GCC internal Format Strings + +These format strings are used inside the GCC sources. In such a format +string, a directive starts with @samp{%}, is optionally followed by a +size specifier @samp{l}, an optional flag @samp{+}, another optional flag +@samp{#}, and is finished by a specifier: @samp{%} denotes a literal +percent sign, @samp{c} denotes a character, @samp{s} denotes a string, +@samp{i} and @samp{d} denote an integer, @samp{o}, @samp{u}, @samp{x} +denote an unsigned integer, @samp{.*s} denotes a string preceded by a +width specification, @samp{H} denotes a @samp{location_t *} pointer, +@samp{D} denotes a general declaration, @samp{F} denotes a function +declaration, @samp{T} denotes a type, @samp{A} denotes a function argument, +@samp{C} denotes a tree code, @samp{E} denotes an expression, @samp{L} +denotes a programming language, @samp{O} denotes a binary operator, +@samp{P} denotes a function parameter, @samp{Q} denotes an assignment +operator, @samp{V} denotes a const/volatile qualifier. + @node Maintainers for other Languages, List of Programming Languages, Translators for other Languages, Programming Languages @section The Maintainer's View @@ -7453,6 +7480,7 @@ that language, and to combine the resulting files using @code{msgcat}. * Perl:: Perl * PHP:: PHP Hypertext Preprocessor * Pike:: Pike +* GCC-source:: GNU Compiler Collection sources @end menu @node C, sh, List of Programming Languages, List of Programming Languages @@ -9116,7 +9144,7 @@ On platforms without gettext, the functions are not available. --- @end table -@node Pike, , PHP, List of Programming Languages +@node Pike, GCC-source, PHP, List of Programming Languages @subsection Pike @cindex Pike @@ -9164,6 +9192,55 @@ On platforms without gettext, the functions are not available. --- @end table +@node GCC-source, , Pike, List of Programming Languages +@subsection GNU Compiler Collection sources +@cindex GCC-source + +@table @asis +@item RPMs +gcc + +@item File extension +@code{c}, @code{h}. + +@item String syntax +@code{"abc"} + +@item gettext shorthand +@code{_("abc")} + +@item gettext/ngettext functions +@code{gettext}, @code{dgettext}, @code{dcgettext}, @code{ngettext}, +@code{dngettext}, @code{dcngettext} + +@item textdomain +@code{textdomain} function + +@item bindtextdomain +@code{bindtextdomain} function + +@item setlocale +Programmer must call @code{setlocale (LC_ALL, "")} + +@item Prerequisite +@code{#include "intl.h"} + +@item Use or emulate GNU gettext +Use + +@item Extractor +@code{xgettext -k_} + +@item Formatting with positions +--- + +@item Portability +Uses autoconf macros + +@item po-mode marking +yes +@end table + @c This is the template for new languages. @ignore diff --git a/gettext-tools/doc/xgettext.texi b/gettext-tools/doc/xgettext.texi index f29a5a662..7e4593fd3 100644 --- a/gettext-tools/doc/xgettext.texi +++ b/gettext-tools/doc/xgettext.texi @@ -72,7 +72,7 @@ Specifies the language of the input files. The supported languages are @code{C}, @code{C++}, @code{ObjectiveC}, @code{PO}, @code{Python}, @code{Lisp}, @code{EmacsLisp}, @code{librep}, @code{Smalltalk}, @code{Java}, @code{JavaProperties}, @code{awk}, @code{YCP}, @code{Tcl}, @code{Perl}, -@code{PHP}, @code{RST}, @code{Glade}. +@code{PHP}, @code{GCC-source}, @code{RST}, @code{Glade}. @item -C @itemx --c++ diff --git a/gettext-tools/src/ChangeLog b/gettext-tools/src/ChangeLog index 50c9043af..6a082a3b6 100644 --- a/gettext-tools/src/ChangeLog +++ b/gettext-tools/src/ChangeLog @@ -1,3 +1,15 @@ +2003-08-08 Bruno Haible + + * format-gcc-internal.c: New file. + * message.h (format_gcc_internal): New enum value. + (NFORMATS): Increment. + * format.h (formatstring_gcc_internal): New declaration. + * format.c (formatstring_parsers): Add entry for gcc_internal. + * message.c (format_language, format_language_pretty): Likewise. + * x-c.h (SCANNERS_C): Add an entry for GCC-source. + * xgettext.c (usage): Mention GCC-source language. + * Makefile.am (FORMAT_SOURCE): Add format-gcc-internal.c. + 2003-08-04 Bruno Haible * x-perl.c (extract_quotelike_pass3): Fix \x handling. diff --git a/gettext-tools/src/Makefile.am b/gettext-tools/src/Makefile.am index 305ab8c7a..9c747090a 100644 --- a/gettext-tools/src/Makefile.am +++ b/gettext-tools/src/Makefile.am @@ -93,7 +93,7 @@ read-properties.c open-po.c dir-list.c str-list.c FORMAT_SOURCE = format.c format-invalid.h \ format-c.c format-python.c format-lisp.c format-elisp.c format-librep.c \ format-java.c format-awk.c format-pascal.c format-ycp.c format-tcl.c \ -format-perl.c format-perl-brace.c format-php.c +format-perl.c format-perl-brace.c format-php.c format-gcc-internal.c # libgettextsrc contains all code that is needed by at least two programs. libgettextsrc_la_SOURCES = \ diff --git a/gettext-tools/src/format-gcc-internal.c b/gettext-tools/src/format-gcc-internal.c new file mode 100644 index 000000000..5441af3a1 --- /dev/null +++ b/gettext-tools/src/format-gcc-internal.c @@ -0,0 +1,431 @@ +/* GCC internal format strings. + Copyright (C) 2003 Free Software Foundation, Inc. + Written by Bruno Haible , 2003. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "format.h" +#include "c-ctype.h" +#include "xmalloc.h" +#include "xerror.h" +#include "format-invalid.h" +#include "error.h" +#include "progname.h" +#include "gettext.h" + +#define _(str) gettext (str) + +/* GCC internal format strings consist of language frontend independent + format directives, implemented in gcc-3.3/gcc/diagnostic.c (function + output_format), plus some frontend dependent extensions: + - for the C/ObjC frontend in gcc-3.3/gcc/c-objc-common.c + - for the C++ frontend in gcc-3.3/gcc/cp/error.c + Taking these together, GCC internal format strings are specified as follows. + A directive + - starts with '%', + - is optionally followed by a size specifier 'l', + - is optionally followed by '+' (only the specifiers of gcc/cp/error.c), + - is optionally followed by '#' (only the specifiers of gcc/cp/error.c), + - is finished by a specifier + + - '%', that needs no argument, + - 'c', that needs a character argument, + - 's', that needs a string argument, + - 'i', 'd', that need a signed integer argument, + - 'o', 'u', 'x', that need an unsigned integer argument, + - '.*s', that needs a signed integer argument and a string argument, + - 'H', that needs a 'location_t *' argument, + [see gcc/diagnostic.c] + + - 'D', that needs a general declaration argument, + - 'F', that needs a function declaration argument, + - 'T', that needs a type argument, + [see gcc/c-objc-common.c and gcc/cp/error.c] + + - 'A', that needs a function argument list argument, + - 'C', that needs a tree code argument, + - 'E', that needs an expression argument, + - 'L', that needs a language argument, + - 'O', that needs a binary operator argument, + - 'P', that needs a function parameter argument, + - 'Q', that needs an assignment operator argument, + - 'V', that needs a const/volatile qualifier argument. + [see gcc/cp/error.c] + */ + +enum format_arg_type +{ + FAT_NONE = 0, + /* Basic types */ + FAT_INTEGER = 1, + FAT_CHAR = 2, + FAT_STRING = 3, + FAT_LOCATION = 4, + FAT_TREE = 5, + FAT_TREE_CODE = 6, + FAT_LANGUAGES = 7, + /* Flags */ + 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; +}; + +/* Locale independent test for a decimal digit. + Argument can be 'char' or 'unsigned char'. (Whereas the argument of + isdigit must be an 'unsigned char'.) */ +#undef isdigit +#define isdigit(c) ((unsigned int) ((c) - '0') < 10) + + +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++ == '%') + { + /* A directive. */ + enum format_arg_type size; + + spec.directives++; + + /* Parse size. */ + 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; + + /* Check the argument types are the same. */ + 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 + +/* Test program: Print the argument list specification returned by + format_parse for strings read from standard input. */ + +#include +#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; +} + +/* + * For Emacs M-x compile + * Local Variables: + * compile-command: "/bin/sh ../libtool --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../lib -I../intl -DHAVE_CONFIG_H -DTEST format-gcc-internal.c ../lib/libgettextlib.la" + * End: + */ + +#endif /* TEST */ diff --git a/gettext-tools/src/format.c b/gettext-tools/src/format.c index 17423f340..b89185b15 100644 --- a/gettext-tools/src/format.c +++ b/gettext-tools/src/format.c @@ -39,5 +39,6 @@ struct formatstring_parser *formatstring_parsers[NFORMATS] = /* format_tcl */ &formatstring_tcl, /* format_perl */ &formatstring_perl, /* format_perl_brace */ &formatstring_perl_brace, - /* format_php */ &formatstring_php + /* format_php */ &formatstring_php, + /* format_gcc_internal */ &formatstring_gcc_internal }; diff --git a/gettext-tools/src/format.h b/gettext-tools/src/format.h index 6cd241be1..b62ec2ccf 100644 --- a/gettext-tools/src/format.h +++ b/gettext-tools/src/format.h @@ -69,6 +69,7 @@ extern struct formatstring_parser formatstring_tcl; extern struct formatstring_parser formatstring_perl; extern struct formatstring_parser formatstring_perl_brace; extern struct formatstring_parser formatstring_php; +extern struct formatstring_parser formatstring_gcc_internal; /* Table of all format string parsers. */ extern struct formatstring_parser *formatstring_parsers[NFORMATS]; diff --git a/gettext-tools/src/message.c b/gettext-tools/src/message.c index 294506303..f8c32e964 100644 --- a/gettext-tools/src/message.c +++ b/gettext-tools/src/message.c @@ -47,7 +47,8 @@ const char *const format_language[NFORMATS] = /* format_tcl */ "tcl", /* format_perl */ "perl", /* format_perl_brace */ "perl-brace", - /* format_php */ "php" + /* format_php */ "php", + /* format_gcc_internal */ "gcc-internal" }; const char *const format_language_pretty[NFORMATS] = @@ -65,7 +66,8 @@ const char *const format_language_pretty[NFORMATS] = /* format_tcl */ "Tcl", /* format_perl */ "Perl", /* format_perl_brace */ "Perl brace", - /* format_php */ "PHP" + /* format_php */ "PHP", + /* format_gcc_internal */ "GCC internal" }; diff --git a/gettext-tools/src/message.h b/gettext-tools/src/message.h index a64c86ce2..482198744 100644 --- a/gettext-tools/src/message.h +++ b/gettext-tools/src/message.h @@ -47,9 +47,10 @@ enum format_type format_tcl, format_perl, format_perl_brace, - format_php + format_php, + format_gcc_internal }; -#define NFORMATS 14 /* Number of format_type enum values. */ +#define NFORMATS 15 /* Number of format_type enum values. */ extern const char *const format_language[NFORMATS]; extern const char *const format_language_pretty[NFORMATS]; diff --git a/gettext-tools/src/x-c.h b/gettext-tools/src/x-c.h index ed1fd3058..f79e79646 100644 --- a/gettext-tools/src/x-c.h +++ b/gettext-tools/src/x-c.h @@ -34,6 +34,7 @@ { "C", extract_c, &formatstring_c, NULL }, \ { "C++", extract_c, &formatstring_c, NULL }, \ { "ObjectiveC", extract_c, &formatstring_c, NULL }, \ + { "GCC-source", extract_c, &formatstring_gcc_internal, NULL }, \ /* Scan a C/C++/ObjectiveC file and add its translatable strings to mdlp. */ extern void extract_c (FILE *fp, const char *real_filename, diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c index 81d085d94..0c73607f1 100644 --- a/gettext-tools/src/xgettext.c +++ b/gettext-tools/src/xgettext.c @@ -658,7 +658,7 @@ Choice of input file language:\n")); (C, C++, ObjectiveC, PO, Python, Lisp,\n\ EmacsLisp, librep, Smalltalk, Java,\n\ JavaProperties, awk, YCP, Tcl, Perl, PHP,\n\ - RST, Glade)\n")); + GCC-source, RST, Glade)\n")); printf (_("\ -C, --c++ shorthand for --language=C++\n")); printf (_("\ diff --git a/gettext-tools/tests/ChangeLog b/gettext-tools/tests/ChangeLog index 0956d26bd..eb5ae4fb6 100644 --- a/gettext-tools/tests/ChangeLog +++ b/gettext-tools/tests/ChangeLog @@ -1,3 +1,9 @@ +2003-08-08 Bruno Haible + + * format-gcc-internal-1: New file. + * format-gcc-internal-2: New file. + * Makefile.am (TESTS): Add them. + 2003-08-04 Bruno Haible * lang-perl-1: Small tweaks. diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am index ad29fc329..205d0147f 100644 --- a/gettext-tools/tests/Makefile.am +++ b/gettext-tools/tests/Makefile.am @@ -54,6 +54,7 @@ TESTS = gettext-1 gettext-2 \ format-awk-1 format-awk-2 \ format-c-1 format-c-2 format-c-3 format-c-4 \ format-elisp-1 format-elisp-2 \ + format-gcc-internal-1 format-gcc-internal-2 \ format-java-1 format-java-2 \ format-librep-1 format-librep-2 \ format-lisp-1 format-lisp-2 \ diff --git a/gettext-tools/tests/format-gcc-internal-1 b/gettext-tools/tests/format-gcc-internal-1 new file mode 100755 index 000000000..e47626e73 --- /dev/null +++ b/gettext-tools/tests/format-gcc-internal-1 @@ -0,0 +1,107 @@ +#! /bin/sh + +# Test recognition of GCC internal format strings. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles f-gi-1.data" +cat <<\EOF > f-gi-1.data +# Valid: no argument +"abc%%" +# Valid: one character argument +"abc%c" +# Valid: one string argument +"abc%s" +# Valid: one integer argument +"abc%i" +# Valid: one integer argument +"abc%d" +# Valid: one integer argument +"abc%o" +# Valid: one integer argument +"abc%u" +# Valid: one integer argument +"abc%x" +# Valid: one integer and one string argument +"abc%.*s" +# Valid: one pointer argument +"abc%H" +# Valid: one pointer argument +"abc%D" +# Valid: one pointer argument +"abc%F" +# Valid: one pointer argument +"abc%T" +# Valid: one pointer argument +"abc%A" +# Valid: one pointer argument +"abc%C" +# Valid: one pointer argument +"abc%E" +# Valid: one pointer argument +"abc%L" +# Valid: one pointer argument +"abc%O" +# Valid: one pointer argument +"abc%P" +# Valid: one pointer argument +"abc%Q" +# Valid: one pointer argument +"abc%V" +# Valid: one argument with flags +"abc%+#Ag" +# Valid: one argument with size specifier +"abc%li" +# Invalid: unterminated +"abc%" +# Invalid: unknown format specifier +"abc%y" +# Invalid: precision with non-string +"abc%.*c" +# Invalid: twice precision +"abc%.*.*s" +# Valid: three arguments +"abc%d%u%u" +# Invalid: a numbered argument +"abc%1$d" +EOF + +: ${XGETTEXT=xgettext} +n=0 +while read comment; do + read string + n=`expr $n + 1` + tmpfiles="$tmpfiles f-gi-1-$n.in f-gi-1-$n.po" + cat < f-gi-1-$n.in +gettext(${string}); +EOF + ${XGETTEXT} -L GCC-source -o f-gi-1-$n.po f-gi-1-$n.in || exit 1 + test -f f-gi-1-$n.po || exit 1 + fail= + if echo "$comment" | grep 'Valid:' > /dev/null; then + if grep gcc-internal-format f-gi-1-$n.po > /dev/null; then + : + else + fail=yes + fi + else + if grep gcc-internal-format f-gi-1-$n.po > /dev/null; then + fail=yes + else + : + fi + fi + if test -n "$fail"; then + echo "Format string recognition error:" 1>&2 + cat f-gi-1-$n.in 1>&2 + echo "Got:" 1>&2 + cat f-gi-1-$n.po 1>&2 + exit 1 + fi + rm -f f-gi-1-$n.in f-gi-1-$n.po +done < f-gi-1.data + +rm -fr $tmpfiles + +exit 0 diff --git a/gettext-tools/tests/format-gcc-internal-2 b/gettext-tools/tests/format-gcc-internal-2 new file mode 100755 index 000000000..a4b7d5d05 --- /dev/null +++ b/gettext-tools/tests/format-gcc-internal-2 @@ -0,0 +1,439 @@ +#! /bin/sh + +# Test checking of C format strings. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles f-gi-2.data" +cat <<\EOF > f-gi-2.data +# Valid: %% doesn't count +msgid "abc%%def" +msgstr "xyz" +# Invalid: invalid msgstr +msgid "abc%%def" +msgstr "xyz%" +# Valid: same arguments +msgid "abc%s%Hdef" +msgstr "xyz%s%H" +# Valid: same arguments, with different widths +msgid "abc%.*sdef" +msgstr "xyz%i%s" +# Invalid: too few arguments +msgid "abc%sdef%u" +msgstr "xyz%s" +# Invalid: too many arguments +msgid "abc%udef" +msgstr "xyz%uvw%c" +# Valid: type compatibility +msgid "abc%i" +msgstr "xyz%d" +# Valid: type compatibility +msgid "abc%o" +msgstr "xyz%u" +# Valid: type compatibility +msgid "abc%u" +msgstr "xyz%x" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%s" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%i" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%u" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%H" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%D" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%F" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%T" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%A" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%C" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%E" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%L" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%O" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%P" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%c" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%i" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%u" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%H" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%D" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%F" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%T" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%A" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%C" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%E" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%L" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%O" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%P" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%s" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%i" +msgstr "xyz%u" +# Invalid: type incompatibility +msgid "abc%i" +msgstr "xyz%H" +# Invalid: type incompatibility +msgid "abc%i" +msgstr "xyz%D" +# Invalid: type incompatibility +msgid "abc%i" +msgstr "xyz%F" +# Invalid: type incompatibility +msgid "abc%i" +msgstr "xyz%T" +# Invalid: type incompatibility +msgid "abc%i" +msgstr "xyz%A" +# Invalid: type incompatibility +msgid "abc%i" +msgstr "xyz%C" +# Invalid: type incompatibility +msgid "abc%i" +msgstr "xyz%E" +# Invalid: type incompatibility +msgid "abc%i" +msgstr "xyz%L" +# Invalid: type incompatibility +msgid "abc%i" +msgstr "xyz%O" +# Invalid: type incompatibility +msgid "abc%i" +msgstr "xyz%P" +# Invalid: type incompatibility +msgid "abc%i" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%i" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%u" +msgstr "xyz%H" +# Invalid: type incompatibility +msgid "abc%u" +msgstr "xyz%D" +# Invalid: type incompatibility +msgid "abc%u" +msgstr "xyz%F" +# Invalid: type incompatibility +msgid "abc%u" +msgstr "xyz%T" +# Invalid: type incompatibility +msgid "abc%u" +msgstr "xyz%A" +# Invalid: type incompatibility +msgid "abc%u" +msgstr "xyz%C" +# Invalid: type incompatibility +msgid "abc%u" +msgstr "xyz%E" +# Invalid: type incompatibility +msgid "abc%u" +msgstr "xyz%L" +# Invalid: type incompatibility +msgid "abc%u" +msgstr "xyz%O" +# Invalid: type incompatibility +msgid "abc%u" +msgstr "xyz%P" +# Invalid: type incompatibility +msgid "abc%u" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%u" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%H" +msgstr "xyz%D" +# Invalid: type incompatibility +msgid "abc%H" +msgstr "xyz%F" +# Invalid: type incompatibility +msgid "abc%H" +msgstr "xyz%T" +# Invalid: type incompatibility +msgid "abc%H" +msgstr "xyz%A" +# Invalid: type incompatibility +msgid "abc%H" +msgstr "xyz%C" +# Invalid: type incompatibility +msgid "abc%H" +msgstr "xyz%E" +# Invalid: type incompatibility +msgid "abc%H" +msgstr "xyz%L" +# Invalid: type incompatibility +msgid "abc%H" +msgstr "xyz%O" +# Invalid: type incompatibility +msgid "abc%H" +msgstr "xyz%P" +# Invalid: type incompatibility +msgid "abc%H" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%H" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%D" +msgstr "xyz%F" +# Invalid: type incompatibility +msgid "abc%D" +msgstr "xyz%T" +# Invalid: type incompatibility +msgid "abc%D" +msgstr "xyz%A" +# Invalid: type incompatibility +msgid "abc%D" +msgstr "xyz%C" +# Invalid: type incompatibility +msgid "abc%D" +msgstr "xyz%E" +# Invalid: type incompatibility +msgid "abc%D" +msgstr "xyz%L" +# Invalid: type incompatibility +msgid "abc%D" +msgstr "xyz%O" +# Invalid: type incompatibility +msgid "abc%D" +msgstr "xyz%P" +# Invalid: type incompatibility +msgid "abc%D" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%D" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%F" +msgstr "xyz%T" +# Invalid: type incompatibility +msgid "abc%F" +msgstr "xyz%A" +# Invalid: type incompatibility +msgid "abc%F" +msgstr "xyz%C" +# Invalid: type incompatibility +msgid "abc%F" +msgstr "xyz%E" +# Invalid: type incompatibility +msgid "abc%F" +msgstr "xyz%L" +# Invalid: type incompatibility +msgid "abc%F" +msgstr "xyz%O" +# Invalid: type incompatibility +msgid "abc%F" +msgstr "xyz%P" +# Invalid: type incompatibility +msgid "abc%F" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%F" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%T" +msgstr "xyz%A" +# Invalid: type incompatibility +msgid "abc%T" +msgstr "xyz%C" +# Invalid: type incompatibility +msgid "abc%T" +msgstr "xyz%E" +# Invalid: type incompatibility +msgid "abc%T" +msgstr "xyz%L" +# Invalid: type incompatibility +msgid "abc%T" +msgstr "xyz%O" +# Invalid: type incompatibility +msgid "abc%T" +msgstr "xyz%P" +# Invalid: type incompatibility +msgid "abc%T" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%T" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%A" +msgstr "xyz%C" +# Invalid: type incompatibility +msgid "abc%A" +msgstr "xyz%E" +# Invalid: type incompatibility +msgid "abc%A" +msgstr "xyz%L" +# Invalid: type incompatibility +msgid "abc%A" +msgstr "xyz%O" +# Invalid: type incompatibility +msgid "abc%A" +msgstr "xyz%P" +# Invalid: type incompatibility +msgid "abc%A" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%A" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%C" +msgstr "xyz%E" +# Invalid: type incompatibility +msgid "abc%C" +msgstr "xyz%L" +# Invalid: type incompatibility +msgid "abc%C" +msgstr "xyz%O" +# Invalid: type incompatibility +msgid "abc%C" +msgstr "xyz%P" +# Invalid: type incompatibility +msgid "abc%C" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%C" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%E" +msgstr "xyz%L" +# Invalid: type incompatibility +msgid "abc%E" +msgstr "xyz%O" +# Invalid: type incompatibility +msgid "abc%E" +msgstr "xyz%P" +# Invalid: type incompatibility +msgid "abc%E" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%E" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%L" +msgstr "xyz%O" +# Invalid: type incompatibility +msgid "abc%L" +msgstr "xyz%P" +# Invalid: type incompatibility +msgid "abc%L" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%L" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%O" +msgstr "xyz%P" +# Invalid: type incompatibility +msgid "abc%O" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%O" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%P" +msgstr "xyz%Q" +# Invalid: type incompatibility +msgid "abc%P" +msgstr "xyz%V" +# Invalid: type incompatibility +msgid "abc%Q" +msgstr "xyz%V" +# Invalid: type incompatibility for width +msgid "abc%.*s" +msgstr "xyz%u%s" +EOF + +: ${MSGFMT=msgfmt} +n=0 +while read comment; do + read msgid_line + read msgstr_line + n=`expr $n + 1` + tmpfiles="$tmpfiles f-gi-2-$n.po f-gi-2-$n.mo" + cat < f-gi-2-$n.po +#, gcc-internal-format +${msgid_line} +${msgstr_line} +EOF + fail= + if echo "$comment" | grep 'Valid:' > /dev/null; then + if ${MSGFMT} --check-format -o f-gi-2-$n.mo f-gi-2-$n.po; then + : + else + fail=yes + fi + else + ${MSGFMT} --check-format -o f-gi-2-$n.mo f-gi-2-$n.po 2> /dev/null + if test $? = 1; then + : + else + fail=yes + fi + fi + if test -n "$fail"; then + echo "Format string checking error:" 1>&2 + cat f-gi-2-$n.po 1>&2 + exit 1 + fi + rm -f f-gi-2-$n.po f-gi-2-$n.mo +done < f-gi-2.data + +rm -fr $tmpfiles + +exit 0 -- 2.47.3