From: Bruno Haible Date: Fri, 10 Oct 2003 10:45:55 +0000 (+0000) Subject: Recognize format strings depending on their position. X-Git-Tag: v0.13~217 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6604a4d8b35235225b43900a0fdcfce0d9b899d0;p=thirdparty%2Fgettext.git Recognize format strings depending on their position. --- diff --git a/NEWS b/NEWS index 2ae339296..334e735ab 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,12 @@ Version 0.12.2 - September 2003 - Glade: xgettext now also supports Glade version 2. +* xgettext has a more reliable detection of format strings. It now + recognizes format strings depending on their position, for example as the + second argument of fprintf(), regardless whether the literal string contains + format directives. This behaviour can be customized through the --flag + option. + * libgettextpo library: - New functions for testing the obsolete/fuzzy/*-format flags of a message. diff --git a/gettext-runtime/po/ChangeLog b/gettext-runtime/po/ChangeLog index a84a9aec7..630a1eb21 100644 --- a/gettext-runtime/po/ChangeLog +++ b/gettext-runtime/po/ChangeLog @@ -1,3 +1,7 @@ +2003-10-05 Bruno Haible + + * Makevars (XGETTEXT_OPTIONS): Add --flag options. + 2003-09-14 Bruno Haible * POTFILES.in: Add ../gettext-tools/lib/closeout.c, src/envsubst.c. diff --git a/gettext-runtime/po/Makevars b/gettext-runtime/po/Makevars index 5c118c507..7b66638bf 100644 --- a/gettext-runtime/po/Makevars +++ b/gettext-runtime/po/Makevars @@ -8,7 +8,10 @@ subdir = po top_builddir = .. # These options get passed to xgettext. -XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ +XGETTEXT_OPTIONS = \ + --keyword=_ --flag=_:1:pass-c-format \ + --keyword=N_ --flag=N_:1:pass-c-format \ + --flag=error:3:c-format --flag=error_at_line:5:c-format # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding diff --git a/gettext-tools/doc/ChangeLog b/gettext-tools/doc/ChangeLog index 5058d68d4..4e679c8b8 100644 --- a/gettext-tools/doc/ChangeLog +++ b/gettext-tools/doc/ChangeLog @@ -1,3 +1,9 @@ +2003-10-05 Bruno Haible + + * xgettext.texi (Language specific options): Renamed section. Document + the languages to which --extract-all, --keyword, --trigraphs are + applicable. Document option --flag. + 2003-09-13 Bruno Haible * gettext.texi: Update menus. diff --git a/gettext-tools/doc/xgettext.texi b/gettext-tools/doc/xgettext.texi index 7e4593fd3..a29cbc76f 100644 --- a/gettext-tools/doc/xgettext.texi +++ b/gettext-tools/doc/xgettext.texi @@ -124,7 +124,7 @@ in output file. @end table -@subsection Language=C/C++ specific options +@subsection Language specific options @table @samp @item -a @@ -133,6 +133,9 @@ in output file. @opindex --extract-all@r{, @code{xgettext} option} Extract all strings. +This option has an effect with most languages, namely C, C++, ObjectiveC, Shell, +Python, Lisp, EmacsLisp, librep, Java, awk, Tcl, Perl, PHP, GCC-source, Glade. + @item -k @var{keywordspec} @itemx --keyword[=@var{keywordspec}] @opindex -k@r{, @code{xgettext} option} @@ -150,11 +153,58 @@ for strings in the first argument of each call to the function or macro strings in the @var{argnum1}st argument and in the @var{argnum2}nd argument of the call, and treats them as singular/plural variants for a message with plural handling. - +@* The default keyword specifications, which are always looked for if not explicitly disabled, are @code{gettext}, @code{dgettext:2}, @code{dcgettext:2}, @code{ngettext:1,2}, @code{dngettext:2,3}, @code{dcngettext:2,3}, and @code{gettext_noop}. +@* +This option has an effect with most languages, namely C, C++, ObjectiveC, Shell, +Python, Lisp, EmacsLisp, librep, Java, awk, Tcl, Perl, PHP, GCC-source, Glade. + +@item --flag=@var{word}:@var{arg}:@var{flag} +@opindex --flag@r{, @code{xgettext} option} +Specifies additional flags for strings occurring as part of the @var{arg}th +argument of the function @var{word}. The possible flags are the possible +format string indicators, such as @samp{c-format}, and their negations, +such as @samp{no-c-format}, possibly prefixed with @samp{pass-}. +@* +@cindex function attribute, __format__ +The meaning of @code{--flag=@var{function}:@var{arg}:@var{lang}-format} +is that in language @var{lang}, the specified @var{function} expects as +@var{arg}th argument a format string. (For those of you familiar with +GCC function attributes, @code{--flag=@var{function}:@var{arg}:c-format} is +roughly equivalent to the declaration +@samp{__attribute__ ((__format__ (__printf__, @var{arg}, ...)))} attached +to @var{function} in a C source file.) +For example, if you use the @samp{error} function from GNU libc, you can +specify its behaviour through @code{--flag=error:3:c-format}. The effect of +this specification is that @code{xgettext} will mark as format strings all +@code{gettext} invocations that occur as @var{arg}th argument of +@var{function}. +This is useful when such strings contain no format string directives: +together with the checks done by @samp{msgfmt -c} it will ensure that +translators cannot accidentally use format string directives that would +lead to a crash at runtime. +@* +@cindex function attribute, __format_arg__ +The meaning of @code{--flag=@var{function}:@var{arg}:pass-@var{lang}-format} +is that in language @var{lang}, if the @var{function} call occurs in a +position that must yield a format string, then its @var{arg}th argument +must yield a format string of the same type as well. (If you know GCC +function attributes, the @code{--flag=@var{function}:@var{arg}:pass-c-format} +option is roughly equivalent to the declaration +@samp{__attribute__ ((__format_arg__ (@var{arg})))} attached to @var{function} +in a C source file.) +For example, if you use the @samp{_} shortcut for the @code{gettext} function, +you should use @code{--flag=_:1:pass-c-format}. The effect of this +specification is that @code{xgettext} will propagate a format string +requirement for a @code{_("string")} call to its first argument, the literal +@code{"string"}, and thus mark it as a format string. +This is useful when such strings contain no format string directives: +together with the checks done by @samp{msgfmt -c} it will ensure that +translators cannot accidentally use format string directives that would +lead to a crash at runtime. @item -T @itemx --trigraphs @@ -162,6 +212,8 @@ explicitly disabled, are @code{gettext}, @code{dgettext:2}, @opindex --trigraphs@r{, @code{xgettext} option} @cindex C trigraphs Understand ANSI C trigraphs for input. +@* +This option has an effect only with the languages C, C++, ObjectiveC. @itemx --debug @opindex --debug@r{, @code{xgettext} option} diff --git a/gettext-tools/po/ChangeLog b/gettext-tools/po/ChangeLog index 7cf8f895c..c1370dbed 100644 --- a/gettext-tools/po/ChangeLog +++ b/gettext-tools/po/ChangeLog @@ -1,3 +1,7 @@ +2003-10-05 Bruno Haible + + * Makevars (XGETTEXT_OPTIONS): Add --flag options. + 2003-09-14 Bruno Haible * POTFILES.in: Add src/format-sh.c, src/x-sh.c, lib/closeout.c. diff --git a/gettext-tools/po/Makevars b/gettext-tools/po/Makevars index 5c118c507..17be10a4e 100644 --- a/gettext-tools/po/Makevars +++ b/gettext-tools/po/Makevars @@ -8,7 +8,13 @@ subdir = po top_builddir = .. # These options get passed to xgettext. -XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ +XGETTEXT_OPTIONS = \ + --keyword=_ --flag=_:1:pass-c-format \ + --keyword=N_ --flag=N_:1:pass-c-format \ + --flag=error:3:c-format --flag=error_at_line:5:c-format \ + --flag=asprintf:2:c-format --flag=vasprintf:2:c-format \ + --flag=xasprintf:1:c-format \ + --flag=po_gram_error:1:c-format --flag=po_gram_error_at_line:2:c-format # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding diff --git a/gettext-tools/src/ChangeLog b/gettext-tools/src/ChangeLog index 2217514ec..b7bf53bc4 100644 --- a/gettext-tools/src/ChangeLog +++ b/gettext-tools/src/ChangeLog @@ -1,3 +1,225 @@ +2003-10-05 Bruno Haible + + * message.h (enum is_format): New item yes_according_to_context. + * message.c (possible_format_p): Handle also yes_according_to_context. + * write-po.c (make_format_description_string): Likewise. + + * xgettext.h (struct flag_context_ty): New type. + (null_context, passthrough_context): New declarations. + (inherited_context): New declaration. + (struct flag_context_list_ty): New type. + (struct flag_context_list_iterator_ty): New type. + (null_context_list_iterator, passthrough_context_list_iterator): New + declarations. + (flag_context_list_iterator): New declaration. + (flag_context_list_iterator_advance): New declaration. + (flag_context_list_table_ty): New type. + (flag_context_list_table_lookup): New declaration. + (xgettext_record_flag): New declaration. + (remember_a_message, remember_a_message_plural): Add context argument. + * xgettext.c: Include alloca.h. + (flag_table_c, flag_table_gcc_internal, flag_table_sh, + flag_table_python, flag_table_lisp, flag_table_elisp, + flag_table_librep, flag_table_java, flag_table_awk, flag_table_ycp, + flag_table_tcl, flag_table_perl, flag_table_php): New variables. + (long_options): Add option --flag. + (extractor_func): Add argument flag_table. + (struct extractor_ty): New type. + (main): Use type 'extractor_ty' instead of 'extractor_func'. + Invoke init_flag_table_c(), init_flag_table_gcc_internal(), + init_flag_table_sh(), init_flag_table_python(), init_flag_table_lisp(), + init_flag_table_elisp(), init_flag_table_librep(), + init_flag_table_java(), init_flag_table_awk(), init_flag_table_ycp(), + init_flag_table_tcl(), init_flag_table_perl(), init_flag_table_php(). + Implement option --flag. + (usage): Rename a section to "Language specific options". Document + the languages to which --extract-all, --keyword, --trigraphs are + applicable. Document option --flag. + (null_context): New variable. + (passthrough_context): New variable. + (inherited_context): New function. + (null_context_list_iterator): New variable. + (passthrough_context_circular_list, passthrough_context_list_iterator): + New variables. + (flag_context_list_iterator): New function. + (flag_context_list_iterator_advance): New function. + (flag_context_list_table_lookup): New function. + (xgettext_record_flag): New function. + (extract_from_file): Change argument type to 'extractor_ty' instead of + 'extractor_func'. Set current_formatstring_parser{1,2} before invoking + the extractor. + (set_format_flags_from_context): New function. + (remember_a_message): Add context argument. Set some *-format flag if + the context specifies it. + (remember_a_message_plural): Likewise. + (language_to_extractor): Change return type to 'extractor_ty' instead + of 'extractor_func'. + + * x-awk.h (SCANNERS_AWK): Refer to flag_table_awk. + (extract_awk): Add argument flag_table. + (init_flag_table_awk): New declaration. + * x-awk.c (init_flag_table_awk): New function. + (enum token_type_ty): New enum item token_type_semicolon. + (x_awk_lex): Recognize semicolon. + (flag_context_list_table): New variable. + (extract_parenthesized): Add arguments outer_context, context_iter. + Implement context handling depending on symbol before '('. Also + recognize argument lists that start without '(', but only up to the + next semicolon. + (extract_awk): Add argument flag_table. Initialize + flag_context_list_table. + + * x-c.h (SCANNERS_C): Refer to flag_table_c, flag_table_gcc_internal. + (extract_c): Add argument flag_table. + (init_flag_table_c): New declaration. + (init_flag_table_gcc_internal): New declaration. + * x-c.c (init_flag_table_c): New function. + (init_flag_table_gcc_internal): New function. + (enum xgettext_token_type_ty): New item xgettext_token_type_other. + (x_c_lex): For token_type_name, put the string into the resulting + token instead of freeing it. Return token type + xgettext_token_type_other instead of xgettext_token_type_symbol in + some cases. + (flag_context_list_table): New variable. + (extract_parenthesized): Add arguments outer_context, context_iter. + Implement context handling depending on symbol before '('. + (extract_c): Add argument flag_table. Initialize + flag_context_list_table. + + * x-elisp.h (SCANNERS_ELISP): Refer to flag_table_elisp. + (extract_elisp): Add argument flag_table. + (init_flag_table_elisp): New declaration. + * x-elisp.c (init_flag_table_elisp): New function. + (flag_context_list_table): New variable. + (read_object): Add argument outer_context. Implement context handling + depending on first symbol after '('. + (extract_elisp): Add argument flag_table. Initialize + flag_context_list_table. + + * x-glade.h (SCANNERS_GLADE): Update. + (extract_glade): Add argument flag_table. + * x-glade.c (start_element_handler, end_element_handler): Pass null + context to remember_a_message. + (extract_glade): Add argument flag_table. + + * x-java.h (SCANNERS_JAVA): Refer to flag_table_java. + (extract_java): Add argument flag_table. + (init_flag_table_java): New declaration. + * x-java.c (init_flag_table_java): New function. + (flag_context_list_table): New variable. + (extract_parenthesized): Add arguments outer_context, context_iter. + Implement context handling depending on symbol before '('. + (extract_java): Add argument flag_table. Initialize + flag_context_list_table. + + * x-librep.h (SCANNERS_ELISP): Refer to flag_table_librep. + (extract_librep): Add argument flag_table. + (init_flag_table_librep): New declaration. + * x-librep.c (init_flag_table_librep): New function. + (flag_context_list_table): New variable. + (read_object): Add argument outer_context. Implement context handling + depending on first symbol after '('. + (extract_librep): Add argument flag_table. Initialize + flag_context_list_table. + + * x-lisp.h (SCANNERS_LISP): Refer to flag_table_lisp. + (extract_lisp): Add argument flag_table. + (init_flag_table_lisp): New declaration. + * x-lisp.c (init_flag_table_lisp): New function. + (flag_context_list_table): New variable. + (read_object): Add argument outer_context. Implement context handling + depending on first symbol after '('. + (extract_lisp): Add argument flag_table. Initialize + flag_context_list_table. + + * x-perl.h (SCANNERS_PERL): Refer to flag_table_perl. + (extract_perl): Add argument flag_table. + (init_flag_table_perl): New declaration. + * x-perl.c (init_flag_table_java): New function. + (flag_context_list_table): New variable. + (extract_variable): Update. Implement context handling depending on + symbol before '{'...'}'. + (interpolate_keywords): Implement context handling depending on symbol + before '->' or '{'...'}'. + (extract_balanced): Add arguments outer_context, context_iter. + Implement context handling depending on symbol before '('. Also + recognize argument lists that start without '('. + (extract_perl): Add argument flag_table. Initialize + flag_context_list_table. + + * x-php.h (SCANNERS_PHP): Refer to flag_table_php. + (extract_php): Add argument flag_table. + (init_flag_table_php): New declaration. + * x-php.c (init_flag_table_php): New function. + (flag_context_list_table): New variable. + (extract_parenthesized): Add arguments outer_context, context_iter. + Implement context handling depending on symbol before '('. + (extract_php): Add argument flag_table. Initialize + flag_context_list_table. + + * x-po.h (SCANNERS_PO): Update. + (extract_po): Add argument flag_table. + * x-properties.h (SCANNERS_PROPERTIES): Update. + (extract_properties): Add argument flag_table. + * x-po.c (extract_po): Add argument flag_table. + (extract_properties): Add argument flag_table. + + * x-python.h (SCANNERS_PYTHON): Refer to flag_table_python. + (extract_python): Add argument flag_table. + (init_flag_table_python): New declaration. + * x-python.c (init_flag_table_python): New function. + (flag_context_list_table): New variable. + (extract_parenthesized): Add arguments outer_context, context_iter. + Implement context handling depending on symbol before '('. + (extract_python): Add argument flag_table. Initialize + flag_context_list_table. + + * x-rst.h (SCANNERS_RST): Update. + (extract_rst): Add argument flag_table. + * x-rst.c (extract_rst): Add argument flag_table. + + * x-sh.h (SCANNERS_SH): Refer to flag_table_sh. + (extract_sh): Add argument flag_table. + (init_flag_table_sh): New declaration. + * x-sh.c (init_flag_table_sh): New function. + (flag_context_list_table): New variable. + (read_word): Add context argument. + (read_command): Add outer_context argument. Implement context handling + depending on first symbol of command. + (read_command_list): Add outer_context argument. + (extract_sh): Add argument flag_table. Initialize + flag_context_list_table. + + * x-smalltalk.h (SCANNERS_SMALLTALK): Update. + (extract_smalltalk): Add argument flag_table. + * x-smalltalk.c (extract_smalltalk): Add argument flag_table. + + * x-tcl.h (SCANNERS_TCL): Refer to flag_table_tcl. + (extract_tcl): Add argument flag_table. + (init_flag_table_tcl): New declaration. + * x-tcl.c (init_flag_table_tcl): New function. + (flag_context_list_table): New variable. + (accumulate_word): Add context argument. + (read_word): Add context argument. + (read_command): Add outer_context argument. Implement context handling + depending on first symbol of command. + (read_command_list): Add outer_context argument. + (extract_tcl): Add argument flag_table. Initialize + flag_context_list_table. + + * x-ycp.h (SCANNERS_YCP): Refer to flag_table_ycp. + (extract_ycp): Add argument flag_table. + (init_flag_table_ycp): New declaration. + * x-ycp.c (init_flag_table_ycp): New function. + (flag_context_list_table): New variable. + (extract_parenthesized): New function, split off from extract_ycp. + Implement context handling depending on symbol before '('. + (extract_ycp): Add argument flag_table. Initialize + flag_context_list_table. Call extract_parenthesized to do the work. + + * po-lex.c (mbfile_getc, control_sequence): Remove explicit marking of + strings as c-format, now done by xgettext. + 2003-09-23 Bruno Haible * x-awk.c (extract_parenthesized): Remove optimization of the diff --git a/gettext-tools/src/message.c b/gettext-tools/src/message.c index f3845a0ee..505567aec 100644 --- a/gettext-tools/src/message.c +++ b/gettext-tools/src/message.c @@ -76,7 +76,9 @@ const char *const format_language_pretty[NFORMATS] = bool possible_format_p (enum is_format is_format) { - return is_format == possible || is_format == yes; + return is_format == possible + || is_format == yes_according_to_context + || is_format == yes; } diff --git a/gettext-tools/src/message.h b/gettext-tools/src/message.h index 46a2f2d3f..9d9dbe58c 100644 --- a/gettext-tools/src/message.h +++ b/gettext-tools/src/message.h @@ -67,6 +67,7 @@ enum is_format undecided, yes, no, + yes_according_to_context, possible, impossible }; diff --git a/gettext-tools/src/po-lex.c b/gettext-tools/src/po-lex.c index 7e7f2503d..a7d58015a 100644 --- a/gettext-tools/src/po-lex.c +++ b/gettext-tools/src/po-lex.c @@ -455,7 +455,6 @@ mbfile_getc (mbchar_t mbc, mbfile_t mbf) /* An invalid multibyte sequence was encountered. */ /* Return a single byte. */ if (signal_eilseq) - /* xgettext: c-format */ po_gram_error (_("invalid multibyte sequence")); bytes = 1; mbc->uc_valid = false; @@ -484,7 +483,6 @@ mbfile_getc (mbchar_t mbc, mbfile_t mbf) if (ferror (mbf->fp)) goto eof; if (signal_eilseq) - /* xgettext: c-format */ po_gram_error (_("\ incomplete multibyte sequence at end of file")); bytes = mbf->bufcount; @@ -495,7 +493,6 @@ incomplete multibyte sequence at end of file")); if (c == '\n') { if (signal_eilseq) - /* xgettext: c-format */ po_gram_error (_("\ incomplete multibyte sequence at end of line")); bytes = mbf->bufcount - 1; @@ -847,7 +844,6 @@ control_sequence () /* FIXME: \u and \U are not handled. */ } lex_ungetc (mbc); - /* xgettext: c-format */ po_gram_error (_("invalid control sequence")); return ' '; } diff --git a/gettext-tools/src/write-po.c b/gettext-tools/src/write-po.c index 9992ddbf7..ef5a04fe9 100644 --- a/gettext-tools/src/write-po.c +++ b/gettext-tools/src/write-po.c @@ -79,6 +79,7 @@ make_format_description_string (enum is_format is_format, const char *lang, break; } /* FALLTHROUGH */ + case yes_according_to_context: case yes: sprintf (result, " %s-format", lang); break; diff --git a/gettext-tools/src/x-awk.c b/gettext-tools/src/x-awk.c index 5d5dfefa4..b3296210a 100644 --- a/gettext-tools/src/x-awk.c +++ b/gettext-tools/src/x-awk.c @@ -28,8 +28,8 @@ #include #include "message.h" -#include "x-awk.h" #include "xgettext.h" +#include "x-awk.h" #include "error.h" #include "error-progname.h" #include "xmalloc.h" @@ -102,6 +102,15 @@ init_keywords () } } +void +init_flag_table_awk () +{ + xgettext_record_flag ("dcgettext:1:pass-awk-format"); + xgettext_record_flag ("dcngettext:1:pass-awk-format"); + xgettext_record_flag ("dcngettext:2:pass-awk-format"); + xgettext_record_flag ("printf:1:awk-format"); +} + /* ======================== Reading of characters. ======================== */ @@ -217,6 +226,7 @@ enum token_type_ty token_type_string, /* "abc" */ token_type_i18nstring, /* _"abc" */ token_type_symbol, /* symbol, number */ + token_type_semicolon, /* ; */ token_type_other /* regexp, misc. operator */ }; typedef enum token_type_ty token_type_ty; @@ -386,7 +396,9 @@ x_awk_lex (token_ty *tp) if (last_non_comment_line > last_comment_line) xgettext_comment_reset (); /* Newline is not allowed inside expressions. It usually - introduces a fresh statement. */ + introduces a fresh statement. + FIXME: Newlines after any of ',' '{' '?' ':' '||' '&&' 'do' 'else' + does *not* introduce a fresh statement. */ prefer_division_over_regexp = false; /* FALLTHROUGH */ case '\t': @@ -538,6 +550,11 @@ x_awk_lex (token_ty *tp) prefer_division_over_regexp = false; return; + case ';': + tp->type = token_type_semicolon; + prefer_division_over_regexp = false; + return; + case ']': tp->type = token_type_other; prefer_division_over_regexp = true; @@ -643,6 +660,11 @@ x_awk_lex (token_ty *tp) /* ========================= Extracting strings. ========================== */ + +/* Context lookup table. */ +static flag_context_list_table_ty *flag_context_list_table; + + /* The file is broken into tokens. Scan the token stream, looking for a keyword, followed by a left paren, followed by a string. When we see this sequence, we have something to remember. We assume we are @@ -667,6 +689,8 @@ x_awk_lex (token_ty *tp) Return true upon eof, false upon closing parenthesis. */ static bool extract_parenthesized (message_list_ty *mlp, + flag_context_ty outer_context, + flag_context_list_iterator_ty context_iter, int commas_to_skip, int plural_commas) { /* Remember the message containing the msgid, for msgid_plural. */ @@ -677,6 +701,16 @@ extract_parenthesized (message_list_ty *mlp, /* Parameters of the keyword just seen. Defined only in state 1. */ int next_commas_to_skip = -1; int next_plural_commas = 0; + /* Whether to implicitly assume the next tokens are arguments even without + a '('. */ + bool next_is_argument = false; + /* Context iterator that will be used if the next token is a '('. */ + flag_context_list_iterator_ty next_context_iter = + passthrough_context_list_iterator; + /* Current context. */ + flag_context_ty inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance (&context_iter)); /* Start state is 0. */ state = 0; @@ -686,6 +720,18 @@ extract_parenthesized (message_list_ty *mlp, token_ty token; x_awk_lex (&token); + + if (next_is_argument && token.type != token_type_lparen) + { + /* An argument list starts, even though there is no '('. */ + context_iter = next_context_iter; + outer_context = inner_context; + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); + } + switch (token.type) { case token_type_symbol: @@ -706,15 +752,24 @@ extract_parenthesized (message_list_ty *mlp, else state = 0; } + next_is_argument = + (strcmp (token.string, "print") == 0 + || strcmp (token.string, "printf") == 0); + next_context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + token.string, strlen (token.string))); free (token.string); continue; case token_type_lparen: - if (state - ? extract_parenthesized (mlp, next_commas_to_skip, - next_plural_commas) - : extract_parenthesized (mlp, -1, 0)) + if (extract_parenthesized (mlp, inner_context, next_context_iter, + state ? next_commas_to_skip : -1, + state ? next_plural_commas : 0)) return true; + next_is_argument = false; + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -735,6 +790,12 @@ extract_parenthesized (message_list_ty *mlp, else commas_to_skip = -1; } + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); + next_is_argument = false; + next_context_iter = passthrough_context_list_iterator; state = 0; continue; @@ -745,7 +806,7 @@ extract_parenthesized (message_list_ty *mlp, pos.line_number = token.line_number; if (extract_all) - remember_a_message (mlp, token.string, &pos); + remember_a_message (mlp, token.string, inner_context, &pos); else { if (commas_to_skip == 0) @@ -753,8 +814,9 @@ extract_parenthesized (message_list_ty *mlp, if (plural_mp == NULL) { /* Seen an msgid. */ - message_ty *mp = remember_a_message (mlp, token.string, - &pos); + message_ty *mp = + remember_a_message (mlp, token.string, + inner_context, &pos); if (plural_commas > 0) plural_mp = mp; } @@ -762,7 +824,7 @@ extract_parenthesized (message_list_ty *mlp, { /* Seen an msgid_plural. */ remember_a_message_plural (plural_mp, token.string, - &pos); + inner_context, &pos); plural_mp = NULL; } } @@ -770,6 +832,8 @@ extract_parenthesized (message_list_ty *mlp, free (token.string); } } + next_is_argument = false; + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -779,8 +843,28 @@ extract_parenthesized (message_list_ty *mlp, pos.file_name = logical_file_name; pos.line_number = token.line_number; - remember_a_message (mlp, token.string, &pos); + remember_a_message (mlp, token.string, inner_context, &pos); } + next_is_argument = false; + next_context_iter = null_context_list_iterator; + state = 0; + continue; + + case token_type_semicolon: + /* An argument list ends, and a new statement begins. */ + /* FIXME: Should handle newline that acts as statement separator + in the same way. */ + /* FIXME: Instead of resetting outer_context here, it may be better + to recurse in the next_is_argument handling above, waiting for + the next semicolon or other statement terminator. */ + outer_context = null_context; + context_iter = null_context_list_iterator; + next_is_argument = false; + next_context_iter = passthrough_context_list_iterator; + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); state = 0; continue; @@ -788,6 +872,8 @@ extract_parenthesized (message_list_ty *mlp, return true; case token_type_other: + next_is_argument = false; + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -801,6 +887,7 @@ extract_parenthesized (message_list_ty *mlp, void extract_awk (FILE *f, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { message_list_ty *mlp = mdlp->item[0]->messages; @@ -815,11 +902,14 @@ extract_awk (FILE *f, prefer_division_over_regexp = false; + flag_context_list_table = flag_table; + init_keywords (); /* Eat tokens until eof is seen. When extract_parenthesized returns due to an unbalanced closing parenthesis, just restart it. */ - while (!extract_parenthesized (mlp, -1, 0)) + while (!extract_parenthesized (mlp, null_context, null_context_list_iterator, + -1, 0)) ; fp = NULL; diff --git a/gettext-tools/src/x-awk.h b/gettext-tools/src/x-awk.h index 44ca341db..3838cbd71 100644 --- a/gettext-tools/src/x-awk.h +++ b/gettext-tools/src/x-awk.h @@ -21,12 +21,16 @@ { "awk", "awk" }, \ #define SCANNERS_AWK \ - { "awk", extract_awk, &formatstring_awk, NULL }, \ + { "awk", extract_awk, \ + &flag_table_awk, &formatstring_awk, NULL }, \ /* Scan an awk file and add its translatable strings to mdlp. */ extern void extract_awk (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); extern void x_awk_keyword (const char *keyword); extern void x_awk_extract_all (void); + +extern void init_flag_table_awk (void); diff --git a/gettext-tools/src/x-c.c b/gettext-tools/src/x-c.c index cb0d548d9..b7f83446a 100644 --- a/gettext-tools/src/x-c.c +++ b/gettext-tools/src/x-c.c @@ -28,8 +28,8 @@ #include #include "message.h" -#include "x-c.h" #include "xgettext.h" +#include "x-c.h" #include "error.h" #include "error-progname.h" #include "xmalloc.h" @@ -155,6 +155,120 @@ init_keywords () } } +void +init_flag_table_c () +{ + xgettext_record_flag ("gettext:1:pass-c-format"); + xgettext_record_flag ("dgettext:2:pass-c-format"); + xgettext_record_flag ("dcgettext:2:pass-c-format"); + xgettext_record_flag ("ngettext:1:pass-c-format"); + xgettext_record_flag ("ngettext:2:pass-c-format"); + xgettext_record_flag ("dngettext:2:pass-c-format"); + xgettext_record_flag ("dngettext:3:pass-c-format"); + xgettext_record_flag ("dcngettext:2:pass-c-format"); + xgettext_record_flag ("dcngettext:3:pass-c-format"); + xgettext_record_flag ("gettext_noop:1:pass-c-format"); + /* */ + xgettext_record_flag ("fprintf:2:c-format"); + xgettext_record_flag ("vfprintf:2:c-format"); + xgettext_record_flag ("printf:1:c-format"); + xgettext_record_flag ("vprintf:1:c-format"); + xgettext_record_flag ("sprintf:2:c-format"); + xgettext_record_flag ("vsprintf:2:c-format"); + xgettext_record_flag ("snprintf:3:c-format"); + xgettext_record_flag ("vsnprintf:3:c-format"); +#if 0 /* These functions are not standard. */ + /* */ + xgettext_record_flag ("asprintf:2:c-format"); + xgettext_record_flag ("vasprintf:2:c-format"); + xgettext_record_flag ("dprintf:2:c-format"); + xgettext_record_flag ("vdprintf:2:c-format"); + xgettext_record_flag ("obstack_printf:2:c-format"); + xgettext_record_flag ("obstack_vprintf:2:c-format"); + /* */ + xgettext_record_flag ("error:3:c-format"); + xgettext_record_flag ("error_at_line:5:c-format"); + /* */ + xgettext_record_flag ("argp_error:2:c-format"); + xgettext_record_flag ("argp_failure:2:c-format"); +#endif +} + +void +init_flag_table_gcc_internal () +{ + xgettext_record_flag ("gettext:1:pass-gcc-internal-format"); + xgettext_record_flag ("dgettext:2:pass-gcc-internal-format"); + xgettext_record_flag ("dcgettext:2:pass-gcc-internal-format"); + xgettext_record_flag ("ngettext:1:pass-gcc-internal-format"); + xgettext_record_flag ("ngettext:2:pass-gcc-internal-format"); + xgettext_record_flag ("dngettext:2:pass-gcc-internal-format"); + xgettext_record_flag ("dngettext:3:pass-gcc-internal-format"); + xgettext_record_flag ("dcngettext:2:pass-gcc-internal-format"); + xgettext_record_flag ("dcngettext:3:pass-gcc-internal-format"); + xgettext_record_flag ("gettext_noop:1:pass-gcc-internal-format"); +#if 0 /* This should better be done inside GCC. */ + /* grepping for ATTRIBUTE_PRINTF in gcc-3.3/gcc/?*.h */ + /* c-format.c */ + xgettext_record_flag ("status_warning:2:gcc-internal-format"); + /* c-tree.h */ + xgettext_record_flag ("pedwarn_c99:1:pass-gcc-internal-format"); + /* collect2.h */ + //xgettext_record_flag ("error:1:c-format"); // 3 different versions + xgettext_record_flag ("notice:1:c-format"); + //xgettext_record_flag ("fatal:1:c-format"); // 2 different versions + xgettext_record_flag ("fatal_perror:1:c-format"); + /* cpplib.h */ + xgettext_record_flag ("cpp_error:3:c-format"); + xgettext_record_flag ("cpp_error_with_line:5:c-format"); + /* diagnostic.h */ + xgettext_record_flag ("diagnostic_set_info:2:pass-gcc-internal-format"); + xgettext_record_flag ("output_printf:2:gcc-internal-format"); + xgettext_record_flag ("output_verbatim:2:pass-gcc-internal-format"); + xgettext_record_flag ("verbatim:1:gcc-internal-format"); + xgettext_record_flag ("inform:1:pass-gcc-internal-format"); + /* gcc.h */ + //xgettext_record_flag ("fatal:1:c-format"); // 2 different versions + //xgettext_record_flag ("error:1:c-format"); // 3 different versions + /* genattrtab.h */ + xgettext_record_flag ("attr_printf:2:pass-c-format"); + /* gengtype.h */ + xgettext_record_flag ("error_at_line:2:pass-c-format"); + xgettext_record_flag ("xvasprintf:2:pass-c-format"); + xgettext_record_flag ("xasprintf:1:pass-c-format"); + xgettext_record_flag ("oprintf:2:pass-c-format"); + /* gensupport.h */ + xgettext_record_flag ("message_with_line:2:pass-c-format"); + /* output.h */ + xgettext_record_flag ("output_operand_lossage:1:c-format"); + /* ra.h */ + xgettext_record_flag ("ra_debug_msg:2:pass-c-format"); + /* toplev.h */ + xgettext_record_flag ("fnotice:2:c-format"); + xgettext_record_flag ("fatal_io_error:2:gcc-internal-format"); + xgettext_record_flag ("error_for_asm:2:pass-gcc-internal-format"); + xgettext_record_flag ("warning_for_asm:2:pass-gcc-internal-format"); + xgettext_record_flag ("error_with_file_and_line:3:pass-gcc-internal-format"); + xgettext_record_flag ("error_with_decl:2:pass-gcc-internal-format"); + xgettext_record_flag ("pedwarn:1:gcc-internal-format"); + xgettext_record_flag ("pedwarn_with_file_and_line:3:gcc-internal-format"); + xgettext_record_flag ("pedwarn_with_decl:2:gcc-internal-format"); + xgettext_record_flag ("sorry:1:gcc-internal-format"); + xgettext_record_flag ("error:1:pass-gcc-internal-format"); + xgettext_record_flag ("fatal_error:1:pass-gcc-internal-format"); + xgettext_record_flag ("internal_error:1:pass-gcc-internal-format"); + xgettext_record_flag ("warning:1:pass-gcc-internal-format"); + xgettext_record_flag ("warning_with_file_and_line:3:pass-gcc-internal-format"); + xgettext_record_flag ("warning_with_decl:2:pass-gcc-internal-format"); + /* f/com.h */ + xgettext_record_flag ("ffecom_get_invented_identifier:1:pass-c-format"); + /* f/sts.h */ + xgettext_record_flag ("ffests_printf:2:pass-c-format"); + /* java/java-tree.h */ + xgettext_record_flag ("parse_error_context:2:pass-c-format"); +#endif +} + /* ======================== Reading of characters. ======================== */ @@ -1208,11 +1322,12 @@ enum xgettext_token_type_ty { xgettext_token_type_eof, xgettext_token_type_keyword, + xgettext_token_type_symbol, xgettext_token_type_lparen, xgettext_token_type_rparen, xgettext_token_type_comma, xgettext_token_type_string_literal, - xgettext_token_type_symbol + xgettext_token_type_other }; typedef enum xgettext_token_type_ty xgettext_token_type_ty; @@ -1225,7 +1340,8 @@ struct xgettext_token_ty int argnum1; int argnum2; - /* This field is used only for xgettext_token_type_string_literal. */ + /* This field is used only for xgettext_token_type_string_literal, + xgettext_token_type_keyword, xgettext_token_type_symbol. */ char *string; /* These fields are only for @@ -1285,7 +1401,7 @@ x_c_lex (xgettext_token_ty *tp) } else tp->type = xgettext_token_type_symbol; - free (token.string); + tp->string = token.string; return; case token_type_lparen: @@ -1318,7 +1434,7 @@ x_c_lex (xgettext_token_ty *tp) default: last_non_comment_line = newline_count; - tp->type = xgettext_token_type_symbol; + tp->type = xgettext_token_type_other; return; } } @@ -1327,6 +1443,11 @@ x_c_lex (xgettext_token_ty *tp) /* ========================= Extracting strings. ========================== */ + +/* Context lookup table. */ +static flag_context_list_table_ty *flag_context_list_table; + + /* The file is broken into tokens. Scan the token stream, looking for a keyword, followed by a left paren, followed by a string. When we see this sequence, we have something to remember. We assume we are @@ -1351,6 +1472,8 @@ x_c_lex (xgettext_token_ty *tp) Return true upon eof, false upon closing parenthesis. */ static bool extract_parenthesized (message_list_ty *mlp, + flag_context_ty outer_context, + flag_context_list_iterator_ty context_iter, int commas_to_skip, int plural_commas) { /* Remember the message containing the msgid, for msgid_plural. */ @@ -1361,6 +1484,13 @@ extract_parenthesized (message_list_ty *mlp, /* Parameters of the keyword just seen. Defined only in state 1. */ int next_commas_to_skip = -1; int next_plural_commas = 0; + /* Context iterator that will be used if the next token is a '('. */ + flag_context_list_iterator_ty next_context_iter = + passthrough_context_list_iterator; + /* Current context. */ + flag_context_ty inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance (&context_iter)); /* Start state is 0. */ state = 0; @@ -1376,15 +1506,31 @@ extract_parenthesized (message_list_ty *mlp, next_commas_to_skip = token.argnum1 - 1; next_plural_commas = (token.argnum2 > token.argnum1 ? token.argnum2 - token.argnum1 : 0); + next_context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + token.string, strlen (token.string))); + free (token.string); state = 1; continue; + case xgettext_token_type_symbol: + next_context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + token.string, strlen (token.string))); + free (token.string); + state = 0; + continue; + case xgettext_token_type_lparen: - if (state - ? extract_parenthesized (mlp, next_commas_to_skip, - next_plural_commas) - : extract_parenthesized (mlp, -1, 0)) + if (extract_parenthesized (mlp, inner_context, next_context_iter, + state ? next_commas_to_skip : -1, + state ? next_plural_commas : 0)) return true; + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -1405,12 +1551,17 @@ extract_parenthesized (message_list_ty *mlp, else commas_to_skip = -1; } + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); + next_context_iter = passthrough_context_list_iterator; state = 0; continue; case xgettext_token_type_string_literal: if (extract_all) - remember_a_message (mlp, token.string, &token.pos); + remember_a_message (mlp, token.string, inner_context, &token.pos); else { if (commas_to_skip == 0) @@ -1418,8 +1569,9 @@ extract_parenthesized (message_list_ty *mlp, if (plural_mp == NULL) { /* Seen an msgid. */ - message_ty *mp = remember_a_message (mlp, token.string, - &token.pos); + message_ty *mp = + remember_a_message (mlp, token.string, + inner_context, &token.pos); if (plural_commas > 0) plural_mp = mp; } @@ -1427,17 +1579,19 @@ extract_parenthesized (message_list_ty *mlp, { /* Seen an msgid_plural. */ remember_a_message_plural (plural_mp, token.string, - &token.pos); + inner_context, &token.pos); plural_mp = NULL; } } else free (token.string); } + next_context_iter = null_context_list_iterator; state = 0; continue; - case xgettext_token_type_symbol: + case xgettext_token_type_other: + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -1454,6 +1608,7 @@ extract_parenthesized (message_list_ty *mlp, void extract_c (FILE *f, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { message_list_ty *mlp = mdlp->item[0]->messages; @@ -1467,11 +1622,14 @@ extract_c (FILE *f, last_comment_line = -1; last_non_comment_line = -1; + flag_context_list_table = flag_table; + init_keywords (); /* Eat tokens until eof is seen. When extract_parenthesized returns due to an unbalanced closing parenthesis, just restart it. */ - while (!extract_parenthesized (mlp, -1, 0)) + while (!extract_parenthesized (mlp, null_context, null_context_list_iterator, + -1, 0)) ; /* Close scanner. */ diff --git a/gettext-tools/src/x-c.h b/gettext-tools/src/x-c.h index f79e79646..df6ff9a62 100644 --- a/gettext-tools/src/x-c.h +++ b/gettext-tools/src/x-c.h @@ -31,14 +31,19 @@ { "m", "ObjectiveC" }, \ #define SCANNERS_C \ - { "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 }, \ + { "C", extract_c, \ + &flag_table_c, &formatstring_c, NULL }, \ + { "C++", extract_c, \ + &flag_table_c, &formatstring_c, NULL }, \ + { "ObjectiveC", extract_c, \ + &flag_table_c, &formatstring_c, NULL }, \ + { "GCC-source", extract_c, \ + &flag_table_gcc_internal, &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, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); @@ -50,3 +55,6 @@ extern void x_c_keyword (const char *name); extern bool x_c_any_keywords (void); extern void x_c_trigraphs (void); + +extern void init_flag_table_c (void); +extern void init_flag_table_gcc_internal (void); diff --git a/gettext-tools/src/x-elisp.c b/gettext-tools/src/x-elisp.c index 7bd7b3e10..bde71c1d4 100644 --- a/gettext-tools/src/x-elisp.c +++ b/gettext-tools/src/x-elisp.c @@ -28,8 +28,8 @@ #include #include "message.h" -#include "x-elisp.h" #include "xgettext.h" +#include "x-elisp.h" #include "error.h" #include "xmalloc.h" #include "exit.h" @@ -116,6 +116,13 @@ init_keywords () } } +void +init_flag_table_elisp () +{ + xgettext_record_flag ("_:1:pass-elisp-format"); + xgettext_record_flag ("format:1:elisp-format"); +} + /* ======================== Reading of characters. ======================== */ @@ -424,6 +431,9 @@ string_of_object (const struct object *op) return str; } +/* Context lookup table. */ +static flag_context_list_table_ty *flag_context_list_table; + /* Returns the character represented by an escape sequence. */ #define IGNORABLE_ESCAPE (EOF - 1) static int @@ -609,7 +619,8 @@ do_getc_escaped (int c, bool in_string) 'first_in_list' and 'new_backquote_flag' are used for reading old backquote syntax and new backquote syntax. */ static void -read_object (struct object *op, bool first_in_list, bool new_backquote_flag) +read_object (struct object *op, bool first_in_list, bool new_backquote_flag, + flag_context_ty outer_context) { for (;;) { @@ -634,6 +645,7 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) case '(': { int arg = 0; /* Current argument number. */ + flag_context_list_iterator_ty context_iter; int argnum1 = 0; /* First string position. */ int argnum2 = 0; /* Plural string position. */ message_ty *plural_mp = NULL; /* Remember the msgid. */ @@ -641,8 +653,18 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) for (;; arg++) { struct object inner; + flag_context_ty inner_context; - read_object (&inner, arg == 0, new_backquote_flag); + if (arg == 0) + inner_context = null_context; + else + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); + + read_object (&inner, arg == 0, new_backquote_flag, + inner_context); /* Recognize end of list. */ if (inner.type == t_listclose) @@ -677,8 +699,16 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) argnum2 = (int) (long) keyword_value >> 10; } + context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + symbol_name, strlen (symbol_name))); + free (symbol_name); } + else + context_iter = null_context_list_iterator; } else { @@ -694,7 +724,8 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) pos.file_name = logical_file_name; pos.line_number = inner.line_number_at_start; - mp = remember_a_message (mlp, string_of_object (&inner), &pos); + mp = remember_a_message (mlp, string_of_object (&inner), + inner_context, &pos); if (argnum2 > 0) plural_mp = mp; } @@ -707,7 +738,8 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) pos.file_name = logical_file_name; pos.line_number = inner.line_number_at_start; - remember_a_message_plural (plural_mp, string_of_object (&inner), &pos); + remember_a_message_plural (plural_mp, string_of_object (&inner), + inner_context, &pos); } } } @@ -732,7 +764,7 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) { struct object inner; - read_object (&inner, false, new_backquote_flag); + read_object (&inner, false, new_backquote_flag, null_context); /* Recognize end of vector. */ if (inner.type == t_vectorclose) @@ -766,7 +798,7 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) { struct object inner; - read_object (&inner, false, new_backquote_flag); + read_object (&inner, false, new_backquote_flag, null_context); /* Dots and EOF are not allowed here. But be tolerant. */ @@ -783,7 +815,7 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) { struct object inner; - read_object (&inner, false, true); + read_object (&inner, false, true, null_context); /* Dots and EOF are not allowed here. But be tolerant. */ @@ -807,7 +839,7 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) { struct object inner; - read_object (&inner, false, false); + read_object (&inner, false, false, null_context); /* Dots and EOF are not allowed here. But be tolerant. */ @@ -884,7 +916,8 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) pos.file_name = logical_file_name; pos.line_number = op->line_number_at_start; - remember_a_message (mlp, string_of_object (op), &pos); + remember_a_message (mlp, string_of_object (op), + null_context, &pos); } last_non_comment_line = line_number; return; @@ -937,7 +970,8 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) { struct object inner; - read_object (&inner, false, new_backquote_flag); + read_object (&inner, false, new_backquote_flag, + null_context); /* Recognize end of vector. */ if (inner.type == t_vectorclose) @@ -972,7 +1006,8 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) /* Read a bit vector. */ { struct object length; - read_object (&length, first_in_list, new_backquote_flag); + read_object (&length, first_in_list, new_backquote_flag, + null_context); /* Dots and EOF are not allowed here. But be tolerant. */ free_object (&length); @@ -981,7 +1016,8 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) if (c == '"') { struct object string; - read_object (&string, first_in_list, new_backquote_flag); + read_object (&string, first_in_list, new_backquote_flag, + null_context); free_object (&string); } else @@ -998,7 +1034,7 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) { struct object inner; do_ungetc (c); - read_object (&inner, false, new_backquote_flag); + read_object (&inner, false, new_backquote_flag, null_context); /* Dots and EOF are not allowed here. But be tolerant. */ free_object (&inner); @@ -1039,7 +1075,7 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) case 'S': case 's': /* XEmacs only */ { struct object inner; - read_object (&inner, false, new_backquote_flag); + read_object (&inner, false, new_backquote_flag, null_context); /* Dots and EOF are not allowed here. But be tolerant. */ free_object (&inner); @@ -1065,7 +1101,7 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) } if (c == '=') { - read_object (op, false, new_backquote_flag); + read_object (op, false, new_backquote_flag, outer_context); last_non_comment_line = line_number; return; } @@ -1133,7 +1169,7 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) /* Simply assume every feature expression is true. */ { struct object inner; - read_object (&inner, false, new_backquote_flag); + read_object (&inner, false, new_backquote_flag, null_context); /* Dots and EOF are not allowed here. But be tolerant. */ free_object (&inner); @@ -1198,6 +1234,7 @@ read_object (struct object *op, bool first_in_list, bool new_backquote_flag) void extract_elisp (FILE *f, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { mlp = mdlp->item[0]->messages; @@ -1210,6 +1247,8 @@ extract_elisp (FILE *f, last_comment_line = -1; last_non_comment_line = -1; + flag_context_list_table = flag_table; + init_keywords (); /* Eat tokens until eof is seen. When read_object returns @@ -1218,7 +1257,7 @@ extract_elisp (FILE *f, { struct object toplevel_object; - read_object (&toplevel_object, false, false); + read_object (&toplevel_object, false, false, null_context); if (toplevel_object.type == t_eof) break; diff --git a/gettext-tools/src/x-elisp.h b/gettext-tools/src/x-elisp.h index a9d338e9d..e9059ad42 100644 --- a/gettext-tools/src/x-elisp.h +++ b/gettext-tools/src/x-elisp.h @@ -21,11 +21,13 @@ { "el", "EmacsLisp" }, \ #define SCANNERS_ELISP \ - { "EmacsLisp", extract_elisp, &formatstring_elisp, NULL }, \ + { "EmacsLisp", extract_elisp, \ + &flag_table_elisp, &formatstring_elisp, NULL }, \ /* Scan an Emacs Lisp file and add its translatable strings to mdlp. */ extern void extract_elisp (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); @@ -33,3 +35,5 @@ extern void extract_elisp (FILE *fp, const char *real_filename, extern void x_elisp_extract_all (void); extern void x_elisp_keyword (const char *name); + +extern void init_flag_table_elisp (void); diff --git a/gettext-tools/src/x-glade.c b/gettext-tools/src/x-glade.c index 72e4e5d72..38a2a903c 100644 --- a/gettext-tools/src/x-glade.c +++ b/gettext-tools/src/x-glade.c @@ -35,8 +35,8 @@ #endif #include "message.h" -#include "x-glade.h" #include "xgettext.h" +#include "x-glade.h" #include "error.h" #include "xerror.h" #include "basename.h" @@ -268,7 +268,8 @@ start_element_handler (void *userData, const char *name, pos.file_name = logical_file_name; pos.line_number = XML_GetCurrentLineNumber (parser); - remember_a_message (mlp, xstrdup (attp[1]), &pos); + remember_a_message (mlp, xstrdup (attp[1]), + null_context, &pos); } break; } @@ -304,7 +305,7 @@ end_element_handler (void *userData, const char *name) pos.file_name = logical_file_name; pos.line_number = p->lineno; - remember_a_message (mlp, p->buffer, &pos); + remember_a_message (mlp, p->buffer, null_context, &pos); p->buffer = NULL; } } @@ -433,6 +434,7 @@ error while reading \"%s\""), real_filename); void extract_glade (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { #if DYNLOAD_LIBEXPAT || HAVE_LIBEXPAT diff --git a/gettext-tools/src/x-glade.h b/gettext-tools/src/x-glade.h index b7872fd0d..37b446498 100644 --- a/gettext-tools/src/x-glade.h +++ b/gettext-tools/src/x-glade.h @@ -22,11 +22,12 @@ { "glade2", "glade" }, \ #define SCANNERS_GLADE \ - { "glade", extract_glade, NULL, NULL }, \ + { "glade", extract_glade, NULL, NULL, NULL }, \ /* Scan a glade XML file and add its translatable strings to mdlp. */ extern void extract_glade (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); diff --git a/gettext-tools/src/x-java.c b/gettext-tools/src/x-java.c index dd0f9cd27..68452fc9f 100644 --- a/gettext-tools/src/x-java.c +++ b/gettext-tools/src/x-java.c @@ -27,8 +27,8 @@ #include #include "message.h" -#include "x-java.h" #include "xgettext.h" +#include "x-java.h" #include "error.h" #include "xmalloc.h" #include "exit.h" @@ -110,6 +110,20 @@ init_keywords () } } +void +init_flag_table_java () +{ + xgettext_record_flag ("GettextResource.gettext:2:pass-java-format"); + xgettext_record_flag ("GettextResource.ngettext:2:pass-java-format"); + xgettext_record_flag ("GettextResource.ngettext:3:pass-java-format"); + xgettext_record_flag ("gettext:1:pass-java-format"); + xgettext_record_flag ("ngettext:1:pass-java-format"); + xgettext_record_flag ("ngettext:2:pass-java-format"); + xgettext_record_flag ("getString:1:pass-java-format"); + xgettext_record_flag ("MessageFormat:1:java-format"); + xgettext_record_flag ("MessageFormat.format:1:java-format"); +} + /* ======================== Reading of characters. ======================== */ @@ -1202,6 +1216,11 @@ x_java_unlex (token_ty *tp) /* ========================= Extracting strings. ========================== */ + +/* Context lookup table. */ +static flag_context_list_table_ty *flag_context_list_table; + + /* The file is broken into tokens. Scan the token stream, looking for a keyword, followed by a left paren, followed by a string. When we see this sequence, we have something to remember. We assume we are @@ -1227,6 +1246,8 @@ x_java_unlex (token_ty *tp) Return true upon eof, false upon closing parenthesis or brace. */ static bool extract_parenthesized (message_list_ty *mlp, token_type_ty terminator, + flag_context_ty outer_context, + flag_context_list_iterator_ty context_iter, int commas_to_skip, int plural_commas) { /* Remember the message containing the msgid, for msgid_plural. */ @@ -1237,6 +1258,13 @@ extract_parenthesized (message_list_ty *mlp, token_type_ty terminator, /* Parameters of the keyword just seen. Defined only in state 1. */ int next_commas_to_skip = -1; int next_plural_commas = 0; + /* Context iterator that will be used if the next token is a '('. */ + flag_context_list_iterator_ty next_context_iter = + passthrough_context_list_iterator; + /* Current context. */ + flag_context_ty inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance (&context_iter)); /* Start state is 0. */ state = 0; @@ -1258,6 +1286,7 @@ extract_parenthesized (message_list_ty *mlp, token_type_ty terminator, char *sum = token.string; size_t sum_len = strlen (sum); const char *dottedname; + flag_context_list_ty *context_list; for (;;) { @@ -1315,15 +1344,34 @@ extract_parenthesized (message_list_ty *mlp, token_type_ty terminator, } dottedname++; } + + for (dottedname = sum;;) + { + context_list = + flag_context_list_table_lookup ( + flag_context_list_table, + dottedname, strlen (dottedname)); + if (context_list != NULL) + break; + + dottedname = strchr (dottedname, '.'); + if (dottedname == NULL) + break; + dottedname++; + } + next_context_iter = flag_context_list_iterator (context_list); + free (sum); continue; } case token_type_lparen: if (extract_parenthesized (mlp, token_type_rparen, + inner_context, next_context_iter, state ? next_commas_to_skip : -1, state ? next_plural_commas : 0)) return true; + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -1338,12 +1386,16 @@ extract_parenthesized (message_list_ty *mlp, token_type_ty terminator, logical_file_name, token.line_number); error_with_progname = true; } + next_context_iter = null_context_list_iterator; state = 0; continue; case token_type_lbrace: - if (extract_parenthesized (mlp, token_type_rbrace, -1, 0)) + if (extract_parenthesized (mlp, token_type_rbrace, + null_context, null_context_list_iterator, + -1, 0)) return true; + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -1358,6 +1410,7 @@ extract_parenthesized (message_list_ty *mlp, token_type_ty terminator, logical_file_name, token.line_number); error_with_progname = true; } + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -1375,6 +1428,11 @@ extract_parenthesized (message_list_ty *mlp, token_type_ty terminator, else commas_to_skip = -1; } + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); + next_context_iter = passthrough_context_list_iterator; state = 0; continue; @@ -1388,7 +1446,7 @@ extract_parenthesized (message_list_ty *mlp, token_type_ty terminator, { xgettext_current_source_encoding = po_charset_utf8; x_java_comment_to_xgettext_comment (token.comment); - remember_a_message (mlp, token.string, &pos); + remember_a_message (mlp, token.string, inner_context, &pos); x_java_comment_reset (); xgettext_current_source_encoding = xgettext_global_source_encoding; } @@ -1403,7 +1461,8 @@ extract_parenthesized (message_list_ty *mlp, token_type_ty terminator, xgettext_current_source_encoding = po_charset_utf8; x_java_comment_to_xgettext_comment (token.comment); - mp = remember_a_message (mlp, token.string, &pos); + mp = remember_a_message (mlp, token.string, + inner_context, &pos); x_java_comment_reset (); xgettext_current_source_encoding = xgettext_global_source_encoding; if (plural_commas > 0) @@ -1414,7 +1473,7 @@ extract_parenthesized (message_list_ty *mlp, token_type_ty terminator, /* Seen an msgid_plural. */ xgettext_current_source_encoding = po_charset_utf8; remember_a_message_plural (plural_mp, token.string, - &pos); + inner_context, &pos); xgettext_current_source_encoding = xgettext_global_source_encoding; plural_mp = NULL; } @@ -1424,6 +1483,7 @@ extract_parenthesized (message_list_ty *mlp, token_type_ty terminator, } } drop_reference (token.comment); + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -1434,6 +1494,7 @@ extract_parenthesized (message_list_ty *mlp, token_type_ty terminator, case token_type_number: case token_type_plus: case token_type_other: + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -1447,6 +1508,7 @@ extract_parenthesized (message_list_ty *mlp, token_type_ty terminator, void extract_java (FILE *f, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { message_list_ty *mlp = mdlp->item[0]->messages; @@ -1461,11 +1523,15 @@ extract_java (FILE *f, phase6_last = token_type_eof; + flag_context_list_table = flag_table; + init_keywords (); /* Eat tokens until eof is seen. When extract_parenthesized returns due to an unbalanced closing parenthesis, just restart it. */ - while (!extract_parenthesized (mlp, token_type_eof, -1, 0)) + while (!extract_parenthesized (mlp, token_type_eof, + null_context, null_context_list_iterator, + -1, 0)) ; fp = NULL; diff --git a/gettext-tools/src/x-java.h b/gettext-tools/src/x-java.h index 50d17f6ab..1aa61f50a 100644 --- a/gettext-tools/src/x-java.h +++ b/gettext-tools/src/x-java.h @@ -20,11 +20,15 @@ { "java", "Java" }, \ #define SCANNERS_JAVA \ - { "Java", extract_java, &formatstring_java, NULL }, \ + { "Java", extract_java, \ + &flag_table_java, &formatstring_java, NULL }, \ extern void extract_java (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); extern void x_java_keyword (const char *keyword); extern void x_java_extract_all (void); + +extern void init_flag_table_java (void); diff --git a/gettext-tools/src/x-librep.c b/gettext-tools/src/x-librep.c index 036c3686f..78858929e 100644 --- a/gettext-tools/src/x-librep.c +++ b/gettext-tools/src/x-librep.c @@ -29,8 +29,8 @@ #include #include "message.h" -#include "x-librep.h" #include "xgettext.h" +#include "x-librep.h" #include "error.h" #include "xmalloc.h" #include "exit.h" @@ -118,6 +118,13 @@ init_keywords () } } +void +init_flag_table_librep () +{ + xgettext_record_flag ("_:1:pass-librep-format"); + xgettext_record_flag ("format:2:librep-format"); +} + /* ======================== Reading of characters. ======================== */ @@ -504,6 +511,9 @@ string_of_object (const struct object *op) return str; } +/* Context lookup table. */ +static flag_context_list_table_ty *flag_context_list_table; + /* Returns the character represented by an escape sequence. */ static int do_getc_escaped (int c) @@ -582,7 +592,7 @@ do_getc_escaped (int c) /* Read the next object. */ static void -read_object (struct object *op) +read_object (struct object *op, flag_context_ty outer_context) { for (;;) { @@ -610,6 +620,7 @@ read_object (struct object *op) case '(': { int arg = 0; /* Current argument number. */ + flag_context_list_iterator_ty context_iter; int argnum1 = 0; /* First string position. */ int argnum2 = 0; /* Plural string position. */ message_ty *plural_mp = NULL; /* Remember the msgid. */ @@ -617,8 +628,17 @@ read_object (struct object *op) for (;; arg++) { struct object inner; + flag_context_ty inner_context; - read_object (&inner); + if (arg == 0) + inner_context = null_context; + else + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); + + read_object (&inner, inner_context); /* Recognize end of list. */ if (inner.type == t_close) @@ -653,8 +673,16 @@ read_object (struct object *op) argnum2 = (int) (long) keyword_value >> 10; } + context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + symbol_name, strlen (symbol_name))); + free (symbol_name); } + else + context_iter = null_context_list_iterator; } else { @@ -670,7 +698,8 @@ read_object (struct object *op) pos.file_name = logical_file_name; pos.line_number = inner.line_number_at_start; - mp = remember_a_message (mlp, string_of_object (&inner), &pos); + mp = remember_a_message (mlp, string_of_object (&inner), + inner_context, &pos); if (argnum2 > 0) plural_mp = mp; } @@ -683,7 +712,8 @@ read_object (struct object *op) pos.file_name = logical_file_name; pos.line_number = inner.line_number_at_start; - remember_a_message_plural (plural_mp, string_of_object (&inner), &pos); + remember_a_message_plural (plural_mp, string_of_object (&inner), + inner_context, &pos); } } } @@ -701,7 +731,7 @@ read_object (struct object *op) { struct object inner; - read_object (&inner); + read_object (&inner, null_context); /* Recognize end of vector. */ if (inner.type == t_close) @@ -745,7 +775,7 @@ read_object (struct object *op) { struct object inner; - read_object (&inner); + read_object (&inner, null_context); /* Dots and EOF are not allowed here. But be tolerant. */ @@ -822,7 +852,8 @@ read_object (struct object *op) pos.file_name = logical_file_name; pos.line_number = op->line_number_at_start; - remember_a_message (mlp, string_of_object (op), &pos); + remember_a_message (mlp, string_of_object (op), + null_context, &pos); } last_non_comment_line = line_number; return; @@ -894,7 +925,7 @@ read_object (struct object *op) case ':': { struct object inner; - read_object (&inner); + read_object (&inner, null_context); /* Dots and EOF are not allowed here. But be tolerant. */ free_object (&inner); @@ -908,7 +939,7 @@ read_object (struct object *op) { struct object inner; do_ungetc (c); - read_object (&inner); + read_object (&inner, null_context); /* Dots and EOF are not allowed here. But be tolerant. */ free_object (&inner); @@ -1081,6 +1112,7 @@ read_object (struct object *op) void extract_librep (FILE *f, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { mlp = mdlp->item[0]->messages; @@ -1093,6 +1125,8 @@ extract_librep (FILE *f, last_comment_line = -1; last_non_comment_line = -1; + flag_context_list_table = flag_table; + init_keywords (); /* Eat tokens until eof is seen. When read_object returns @@ -1101,7 +1135,7 @@ extract_librep (FILE *f, { struct object toplevel_object; - read_object (&toplevel_object); + read_object (&toplevel_object, null_context); if (toplevel_object.type == t_eof) break; diff --git a/gettext-tools/src/x-librep.h b/gettext-tools/src/x-librep.h index 2dc4cc5f5..aaa766402 100644 --- a/gettext-tools/src/x-librep.h +++ b/gettext-tools/src/x-librep.h @@ -21,11 +21,13 @@ { "jl", "librep" }, \ #define SCANNERS_LIBREP \ - { "librep", extract_librep, &formatstring_librep, NULL }, \ + { "librep", extract_librep, \ + &flag_table_librep, &formatstring_librep, NULL }, \ /* Scan a librep file and add its translatable strings to mdlp. */ extern void extract_librep (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); @@ -33,3 +35,5 @@ extern void extract_librep (FILE *fp, const char *real_filename, extern void x_librep_extract_all (void); extern void x_librep_keyword (const char *name); + +extern void init_flag_table_librep (void); diff --git a/gettext-tools/src/x-lisp.c b/gettext-tools/src/x-lisp.c index 14cf7db46..4a1e7bff9 100644 --- a/gettext-tools/src/x-lisp.c +++ b/gettext-tools/src/x-lisp.c @@ -28,8 +28,8 @@ #include #include "message.h" -#include "x-lisp.h" #include "xgettext.h" +#include "x-lisp.h" #include "error.h" #include "xmalloc.h" #include "exit.h" @@ -178,6 +178,16 @@ init_keywords () } } +void +init_flag_table_lisp () +{ + xgettext_record_flag ("gettext:1:pass-lisp-format"); + xgettext_record_flag ("ngettext:1:pass-lisp-format"); + xgettext_record_flag ("ngettext:2:pass-lisp-format"); + xgettext_record_flag ("gettext-noop:1:pass-lisp-format"); + xgettext_record_flag ("format:2:lisp-format"); +} + /* ======================== Reading of characters. ======================== */ @@ -919,9 +929,12 @@ string_of_object (const struct object *op) return str; } +/* Context lookup table. */ +static flag_context_list_table_ty *flag_context_list_table; + /* Read the next object. */ static void -read_object (struct object *op) +read_object (struct object *op, flag_context_ty outer_context) { for (;;) { @@ -998,6 +1011,7 @@ read_object (struct object *op) case '(': { int arg = 0; /* Current argument number. */ + flag_context_list_iterator_ty context_iter; int argnum1 = 0; /* First string position. */ int argnum2 = 0; /* Plural string position. */ message_ty *plural_mp = NULL; /* Remember the msgid. */ @@ -1005,8 +1019,17 @@ read_object (struct object *op) for (;; arg++) { struct object inner; + flag_context_ty inner_context; + + if (arg == 0) + inner_context = null_context; + else + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); - read_object (&inner); + read_object (&inner, inner_context); /* Recognize end of list. */ if (inner.type == t_close) @@ -1052,8 +1075,16 @@ read_object (struct object *op) argnum2 = (int) (long) keyword_value >> 10; } + context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + symbol_name, strlen (symbol_name))); + free (symbol_name); } + else + context_iter = null_context_list_iterator; } else { @@ -1069,7 +1100,8 @@ read_object (struct object *op) pos.file_name = logical_file_name; pos.line_number = inner.line_number_at_start; - mp = remember_a_message (mlp, string_of_object (&inner), &pos); + mp = remember_a_message (mlp, string_of_object (&inner), + inner_context, &pos); if (argnum2 > 0) plural_mp = mp; } @@ -1082,7 +1114,8 @@ read_object (struct object *op) pos.file_name = logical_file_name; pos.line_number = inner.line_number_at_start; - remember_a_message_plural (plural_mp, string_of_object (&inner), &pos); + remember_a_message_plural (plural_mp, string_of_object (&inner), + inner_context, &pos); } } } @@ -1116,7 +1149,7 @@ read_object (struct object *op) { struct object inner; - read_object (&inner); + read_object (&inner, null_context); /* Dots and EOF are not allowed here. But be tolerant. */ @@ -1178,7 +1211,8 @@ read_object (struct object *op) pos.file_name = logical_file_name; pos.line_number = op->line_number_at_start; - remember_a_message (mlp, string_of_object (op), &pos); + remember_a_message (mlp, string_of_object (op), + null_context, &pos); } last_non_comment_line = line_number; return; @@ -1218,7 +1252,7 @@ read_object (struct object *op) case 'S': case 's': { struct object inner; - read_object (&inner); + read_object (&inner, null_context); /* Dots and EOF are not allowed here. But be tolerant. */ free_object (&inner); @@ -1336,7 +1370,7 @@ read_object (struct object *op) /* Simply assume every feature expression is true. */ { struct object inner; - read_object (&inner); + read_object (&inner, null_context); /* Dots and EOF are not allowed here. But be tolerant. */ free_object (&inner); @@ -1368,6 +1402,7 @@ read_object (struct object *op) void extract_lisp (FILE *f, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { mlp = mdlp->item[0]->messages; @@ -1380,6 +1415,8 @@ extract_lisp (FILE *f, last_comment_line = -1; last_non_comment_line = -1; + flag_context_list_table = flag_table; + init_keywords (); /* Eat tokens until eof is seen. When read_object returns @@ -1388,7 +1425,7 @@ extract_lisp (FILE *f, { struct object toplevel_object; - read_object (&toplevel_object); + read_object (&toplevel_object, null_context); if (toplevel_object.type == t_eof) break; diff --git a/gettext-tools/src/x-lisp.h b/gettext-tools/src/x-lisp.h index 63c447cd2..fb919a140 100644 --- a/gettext-tools/src/x-lisp.h +++ b/gettext-tools/src/x-lisp.h @@ -21,11 +21,13 @@ { "lisp", "Lisp" }, \ #define SCANNERS_LISP \ - { "Lisp", extract_lisp, &formatstring_lisp, NULL }, \ + { "Lisp", extract_lisp, \ + &flag_table_lisp, &formatstring_lisp, NULL }, \ /* Scan a Lisp file and add its translatable strings to mdlp. */ extern void extract_lisp (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); @@ -33,3 +35,5 @@ extern void extract_lisp (FILE *fp, const char *real_filename, extern void x_lisp_extract_all (void); extern void x_lisp_keyword (const char *name); + +extern void init_flag_table_lisp (void); diff --git a/gettext-tools/src/x-perl.c b/gettext-tools/src/x-perl.c index 03a8b1cfe..3928cf6ed 100644 --- a/gettext-tools/src/x-perl.c +++ b/gettext-tools/src/x-perl.c @@ -28,8 +28,8 @@ #include #include "message.h" -#include "x-perl.h" #include "xgettext.h" +#include "x-perl.h" #include "error.h" #include "error-progname.h" #include "xmalloc.h" @@ -112,10 +112,70 @@ init_keywords () x_perl_keyword ("dngettext:2,3"); x_perl_keyword ("dcngettext:2,3"); x_perl_keyword ("gettext_noop"); +#if 0 + x_perl_keyword ("__"); + x_perl_keyword ("$__"); + x_perl_keyword ("%__"); + x_perl_keyword ("__x"); + x_perl_keyword ("__n:1,2"); + x_perl_keyword ("__nx:1,2"); + x_perl_keyword ("__xn:1,2"); + x_perl_keyword ("N__"); +#endif default_keywords = false; } } +void +init_flag_table_perl () +{ + xgettext_record_flag ("gettext:1:pass-perl-format"); + xgettext_record_flag ("gettext:1:pass-perl-brace-format"); + xgettext_record_flag ("%gettext:1:pass-perl-format"); + xgettext_record_flag ("%gettext:1:pass-perl-brace-format"); + xgettext_record_flag ("$gettext:1:pass-perl-format"); + xgettext_record_flag ("$gettext:1:pass-perl-brace-format"); + xgettext_record_flag ("dgettext:2:pass-perl-format"); + xgettext_record_flag ("dgettext:2:pass-perl-brace-format"); + xgettext_record_flag ("dcgettext:2:pass-perl-format"); + xgettext_record_flag ("dcgettext:2:pass-perl-brace-format"); + xgettext_record_flag ("ngettext:1:pass-perl-format"); + xgettext_record_flag ("ngettext:2:pass-perl-format"); + xgettext_record_flag ("ngettext:1:pass-perl-brace-format"); + xgettext_record_flag ("ngettext:2:pass-perl-brace-format"); + xgettext_record_flag ("dngettext:2:pass-perl-format"); + xgettext_record_flag ("dngettext:3:pass-perl-format"); + xgettext_record_flag ("dngettext:2:pass-perl-brace-format"); + xgettext_record_flag ("dngettext:3:pass-perl-brace-format"); + xgettext_record_flag ("dcngettext:2:pass-perl-format"); + xgettext_record_flag ("dcngettext:3:pass-perl-format"); + xgettext_record_flag ("dcngettext:2:pass-perl-brace-format"); + xgettext_record_flag ("dcngettext:3:pass-perl-brace-format"); + xgettext_record_flag ("gettext_noop:1:pass-perl-format"); + xgettext_record_flag ("gettext_noop:1:pass-perl-brace-format"); + xgettext_record_flag ("printf:1:perl-format"); /* argument 1 or 2 ?? */ + xgettext_record_flag ("sprintf:1:perl-format"); +#if 0 + xgettext_record_flag ("__:1:pass-perl-format"); + xgettext_record_flag ("__:1:pass-perl-brace-format"); + xgettext_record_flag ("%__:1:pass-perl-format"); + xgettext_record_flag ("%__:1:pass-perl-brace-format"); + xgettext_record_flag ("$__:1:pass-perl-format"); + xgettext_record_flag ("$__:1:pass-perl-brace-format"); + xgettext_record_flag ("__x:1:perl-brace-format"); + xgettext_record_flag ("__n:1:pass-perl-format"); + xgettext_record_flag ("__n:2:pass-perl-format"); + xgettext_record_flag ("__n:1:pass-perl-brace-format"); + xgettext_record_flag ("__n:2:pass-perl-brace-format"); + xgettext_record_flag ("__nx:1:perl-brace-format"); + xgettext_record_flag ("__nx:2:perl-brace-format"); + xgettext_record_flag ("__xn:1:perl-brace-format"); + xgettext_record_flag ("__xn:2:perl-brace-format"); + xgettext_record_flag ("N__:1:pass-perl-format"); + xgettext_record_flag ("N__:1:pass-perl-brace-format"); +#endif +} + /* ======================== Reading of characters. ======================== */ @@ -694,12 +754,20 @@ extract_quotelike_pass1_utf8 (int delim) of an expression, it's a regexp. */ static bool prefer_division_over_regexp; +/* Context lookup table. */ +static flag_context_list_table_ty *flag_context_list_table; + /* Forward declaration of local functions. */ -static void interpolate_keywords (message_list_ty *mlp, const char *string, int lineno); +static void interpolate_keywords (message_list_ty *mlp, const char *string, + int lineno); static token_ty *x_perl_lex (message_list_ty *mlp); static void x_perl_unlex (token_ty *tp); -static bool extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, token_type_ty delim); +static bool extract_balanced (message_list_ty *mlp, int state, + token_type_ty delim, + flag_context_ty outer_context, + flag_context_list_iterator_ty context_iter, + int arg_sg, int arg_pl); /* Extract an unsigned hexadecimal number from STRING, considering at @@ -1305,7 +1373,8 @@ extract_variable (message_list_ty *mlp, token_ty *tp, int first) real_file_name, line_number); #endif - if (extract_balanced (mlp, -1, -1, 0, token_type_rbrace)) + if (extract_balanced (mlp, 0, token_type_rbrace, + null_context, null_context_list_iterator, -1, -1)) return; buffer[bufpos++] = c; } @@ -1432,6 +1501,11 @@ extract_variable (message_list_ty *mlp, token_ty *tp, int first) /* Extract a possible string from the key. Before proceeding we check whether the open curly is followed by a symbol and then by a right curly. */ + flag_context_list_iterator_ty context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + tp->string, strlen (tp->string))); token_ty *t1 = x_perl_lex (mlp); #if DEBUG_PERL @@ -1445,11 +1519,19 @@ extract_variable (message_list_ty *mlp, token_ty *tp, int first) token_ty *t2 = x_perl_lex (mlp); if (t2->type == token_type_rbrace) { + flag_context_ty context; lex_pos_ty pos; + + context = + inherited_context (null_context, + flag_context_list_iterator_advance ( + &context_iter)); + pos.line_number = line_number; pos.file_name = logical_file_name; + xgettext_current_source_encoding = po_charset_utf8; - remember_a_message (mlp, xstrdup (t1->string), &pos); + remember_a_message (mlp, xstrdup (t1->string), context, &pos); xgettext_current_source_encoding = xgettext_global_source_encoding; free_token (t2); free_token (t1); @@ -1462,7 +1544,8 @@ extract_variable (message_list_ty *mlp, token_ty *tp, int first) else { x_perl_unlex (t1); - if (extract_balanced (mlp, 1, -1, 1, token_type_rbrace)) + if (extract_balanced (mlp, 1, token_type_rbrace, + null_context, context_iter, 1, -1)) return; } } @@ -1490,7 +1573,8 @@ extract_variable (message_list_ty *mlp, token_ty *tp, int first) fprintf (stderr, "%s:%d: extracting balanced '{' after varname\n", real_file_name, line_number); #endif - extract_balanced (mlp, -1, -1, 0, token_type_rbrace); + extract_balanced (mlp, 0, token_type_rbrace, + null_context, null_context_list_iterator, -1, -1); break; case '[': @@ -1498,7 +1582,8 @@ extract_variable (message_list_ty *mlp, token_ty *tp, int first) fprintf (stderr, "%s:%d: extracting balanced '[' after varname\n", real_file_name, line_number); #endif - extract_balanced (mlp, -1, -1, 0, token_type_rbracket); + extract_balanced (mlp, 0, token_type_rbracket, + null_context, null_context_list_iterator, -1, -1); break; case '-': @@ -1541,7 +1626,8 @@ interpolate_keywords (message_list_ty *mlp, const char *string, int lineno) static char *buffer; static int bufmax = 0; int bufpos = 0; - int c = (unsigned char) string[0]; + flag_context_ty context; + int c; bool maybe_hash_deref = false; enum parser_state { @@ -1576,8 +1662,13 @@ interpolate_keywords (message_list_ty *mlp, const char *string, int lineno) * squote: a single-quote has been seen in state WAIT_QUOTE * barekey: an bareword character has been seen in state WAIT_QUOTE * wait_rbrace: closing quote has been seen in state DQUOTE or SQUOTE + * + * In the states initial...identifier the context is null_context; in the + * states minus...wait_rbrace the context is the one suitable for the first + * argument of the last seen identifier. */ state = initial; + context = null_context; token.type = token_type_string; token.sub_type = string_type_qq; @@ -1653,19 +1744,27 @@ interpolate_keywords (message_list_ty *mlp, const char *string, int lineno) { buffer[bufpos++] = c; state = identifier; - break; } else - { - state = initial; - } + state = initial; break; case identifier: switch (c) { case '-': if (find_entry (&keywords, buffer, bufpos, &keyword_value) == 0) - state = minus; + { + flag_context_list_iterator_ty context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + buffer, bufpos)); + context = + inherited_context (null_context, + flag_context_list_iterator_advance ( + &context_iter)); + state = minus; + } else state = initial; break; @@ -1673,7 +1772,18 @@ interpolate_keywords (message_list_ty *mlp, const char *string, int lineno) if (!maybe_hash_deref) buffer[0] = '%'; if (find_entry (&keywords, buffer, bufpos, &keyword_value) == 0) - state = wait_quote; + { + flag_context_list_iterator_ty context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + buffer, bufpos)); + context = + inherited_context (null_context, + flag_context_list_iterator_advance ( + &context_iter)); + state = wait_quote; + } else state = initial; break; @@ -1697,6 +1807,7 @@ interpolate_keywords (message_list_ty *mlp, const char *string, int lineno) state = wait_lbrace; break; default: + context = null_context; state = initial; break; } @@ -1708,6 +1819,7 @@ interpolate_keywords (message_list_ty *mlp, const char *string, int lineno) state = wait_quote; break; default: + context = null_context; state = initial; break; } @@ -1731,13 +1843,16 @@ interpolate_keywords (message_list_ty *mlp, const char *string, int lineno) if (c == '_' || (c >= '0' && c <= '9') || c >= 0x80 || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { - state = barekey; pos.line_number = lineno; bufpos = 0; buffer[bufpos++] = c; + state = barekey; } else - state = initial; + { + context = null_context; + state = initial; + } break; } break; @@ -1768,7 +1883,10 @@ interpolate_keywords (message_list_ty *mlp, const char *string, int lineno) buffer[bufpos++] = string++[0]; } else - state = initial; + { + context = null_context; + state = initial; + } break; default: buffer[bufpos++] = c; @@ -1792,7 +1910,10 @@ interpolate_keywords (message_list_ty *mlp, const char *string, int lineno) buffer[bufpos++] = string++[0]; } else - state = initial; + { + context = null_context; + state = initial; + } break; default: buffer[bufpos++] = c; @@ -1800,25 +1921,24 @@ interpolate_keywords (message_list_ty *mlp, const char *string, int lineno) } break; case barekey: - { - if (c == '_' || (c >= '0' && c <= '9') || c >= 0x80 - || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) - { - buffer[bufpos++] = c; - break; - } - else if (is_whitespace (c)) - { - state = wait_rbrace; - break; - } - else if (c != '}') - { - state = initial; - break; - } - /* Must be right brace. */ - } + if (c == '_' || (c >= '0' && c <= '9') || c >= 0x80 + || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) + { + buffer[bufpos++] = c; + break; + } + else if (is_whitespace (c)) + { + state = wait_rbrace; + break; + } + else if (c != '}') + { + context = null_context; + state = initial; + break; + } + /* Must be right brace. */ /* FALLTHROUGH */ case wait_rbrace: switch (c) @@ -1830,10 +1950,11 @@ interpolate_keywords (message_list_ty *mlp, const char *string, int lineno) token.string = xstrdup (buffer); extract_quotelike_pass3 (&token, EXIT_FAILURE); xgettext_current_source_encoding = po_charset_utf8; - remember_a_message (mlp, token.string, &pos); + remember_a_message (mlp, token.string, context, &pos); xgettext_current_source_encoding = xgettext_global_source_encoding; /* FALLTHROUGH */ default: + context = null_context; state = initial; break; } @@ -2648,10 +2769,7 @@ collect_message (message_list_ty *mlp, token_ty *tp, int error_level) When specific arguments shall be extracted, ARG_SG and ARG_PL are set to the corresponding argument number or -1 if not applicable. - Returns the number of requested arguments consumed or -1 for eof. - If - instead of consuming requested arguments - a complete message - has been extracted, the return value will be sufficiently high to - avoid any mis-interpretation. + Returns true for EOF, false otherwise. States are: @@ -2673,8 +2791,10 @@ collect_message (message_list_ty *mlp, token_ty *tp, int error_level) tokens will cause a warning. */ static bool -extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, - token_type_ty delim) +extract_balanced (message_list_ty *mlp, int state, token_type_ty delim, + flag_context_ty outer_context, + flag_context_list_iterator_ty context_iter, + int arg_sg, int arg_pl) { /* Remember the message containing the msgid, for msgid_plural. */ message_ty *plural_mp = NULL; @@ -2686,6 +2806,18 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, /* Number of left parentheses seen. */ int paren_seen = 0; + /* Whether to implicitly assume the next tokens are arguments even without + a '('. */ + bool next_is_argument = false; + + /* Context iterator that will be used if the next token is a '('. */ + flag_context_list_iterator_ty next_context_iter = + passthrough_context_list_iterator; + /* Current context. */ + flag_context_ty inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance (&context_iter)); + #if DEBUG_PERL static int nesting_level = 0; @@ -2715,6 +2847,17 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, return false; } + if (next_is_argument && tp->type != token_type_lparen) + { + /* An argument list starts, even though there is no '('. */ + context_iter = next_context_iter; + outer_context = inner_context; + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); + } + switch (tp->type) { case token_type_symbol: @@ -2739,6 +2882,12 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, state = 2; } } + next_is_argument = true; + next_context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + tp->string, strlen (tp->string))); break; case token_type_variable: @@ -2747,6 +2896,8 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, logical_file_name, tp->line_number, nesting_level, tp->string); #endif prefer_division_over_regexp = true; + next_is_argument = false; + next_context_iter = null_context_list_iterator; break; case token_type_lparen: @@ -2756,15 +2907,17 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, #endif ++paren_seen; - if (extract_balanced (mlp, arg_sg - arg_count + 1, - arg_pl - arg_count + 1, state, - token_type_rparen)) + if (extract_balanced (mlp, state, token_type_rparen, + inner_context, next_context_iter, + arg_sg - arg_count + 1, arg_pl - arg_count + 1)) { free_token (tp); return true; } if (my_last_token == token_type_keyword_symbol) arg_sg = arg_pl = -1; + next_is_argument = false; + next_context_iter = null_context_list_iterator; break; case token_type_rparen: @@ -2773,6 +2926,8 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, logical_file_name, tp->line_number, nesting_level); #endif --paren_seen; + next_is_argument = false; + next_context_iter = null_context_list_iterator; break; case token_type_comma: @@ -2789,10 +2944,16 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, arg_count = 0; } #if DEBUG_PERL - fprintf (stderr, "%s:%d: arg_count: %d, arg_sg: %d, arg_pl: %d\n", - real_file_name, tp->line_number, - arg_count, arg_sg, arg_pl); + fprintf (stderr, "%s:%d: arg_count: %d, arg_sg: %d, arg_pl: %d\n", + real_file_name, tp->line_number, + arg_count, arg_sg, arg_pl); #endif + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); + next_is_argument = false; + next_context_iter = passthrough_context_list_iterator; break; case token_type_string: @@ -2811,7 +2972,7 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, pos.line_number = tp->line_number; string = collect_message (mlp, tp, EXIT_SUCCESS); xgettext_current_source_encoding = po_charset_utf8; - remember_a_message (mlp, string, &pos); + remember_a_message (mlp, string, inner_context, &pos); xgettext_current_source_encoding = xgettext_global_source_encoding; } else if (state) @@ -2826,22 +2987,20 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, { string = collect_message (mlp, tp, EXIT_FAILURE); xgettext_current_source_encoding = po_charset_utf8; - plural_mp = remember_a_message (mlp, string, &pos); + plural_mp = remember_a_message (mlp, string, inner_context, &pos); xgettext_current_source_encoding = xgettext_global_source_encoding; arg_sg = -1; } - else if (arg_count == arg_pl && plural_mp == NULL) + else if (arg_count == arg_pl) { if (plural_mp == NULL) error (EXIT_FAILURE, 0, _("\ %s:%d: fatal: plural message seen before singular message\n"), real_file_name, tp->line_number); - } - else if (arg_count == arg_pl) - { + string = collect_message (mlp, tp, EXIT_FAILURE); xgettext_current_source_encoding = po_charset_utf8; - remember_a_message_plural (plural_mp, string, &pos); + remember_a_message_plural (plural_mp, string, inner_context, &pos); xgettext_current_source_encoding = xgettext_global_source_encoding; arg_pl = -1; } @@ -2853,6 +3012,8 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, plural_mp = NULL; } + next_is_argument = false; + next_context_iter = null_context_list_iterator; break; case token_type_eof: @@ -2868,11 +3029,15 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, fprintf (stderr, "%s:%d: type lbrace (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif - if (extract_balanced (mlp, -1, -1, 0, token_type_rbrace)) + if (extract_balanced (mlp, 0, token_type_rbrace, + null_context, null_context_list_iterator, + -1, -1)) { free_token (tp); return true; } + next_is_argument = false; + next_context_iter = null_context_list_iterator; break; case token_type_rbrace: @@ -2880,6 +3045,8 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, fprintf (stderr, "%s:%d: type rbrace (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif + next_is_argument = false; + next_context_iter = null_context_list_iterator; state = 0; break; @@ -2888,11 +3055,15 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, fprintf (stderr, "%s:%d: type lbracket (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif - if (extract_balanced (mlp, -1, -1, 0, token_type_rbracket)) + if (extract_balanced (mlp, 0, token_type_rbracket, + null_context, null_context_list_iterator, + -1, -1)) { free_token (tp); return true; } + next_is_argument = false; + next_context_iter = null_context_list_iterator; break; case token_type_rbracket: @@ -2900,6 +3071,8 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, fprintf (stderr, "%s:%d: type rbracket (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif + next_is_argument = false; + next_context_iter = null_context_list_iterator; state = 0; break; @@ -2913,6 +3086,17 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, /* The ultimate sign. */ arg_sg = arg_pl = -1; + /* FIXME: Instead of resetting outer_context here, it may be better + to recurse in the next_is_argument handling above, waiting for + the next semicolon or other statement terminator. */ + outer_context = null_context; + context_iter = null_context_list_iterator; + next_is_argument = false; + next_context_iter = passthrough_context_list_iterator; + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); break; case token_type_dereference: @@ -2920,6 +3104,8 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, fprintf (stderr, "%s:%d: type dereference (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif + next_is_argument = false; + next_context_iter = null_context_list_iterator; break; case token_type_dot: @@ -2927,6 +3113,8 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, fprintf (stderr, "%s:%d: type dot (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif + next_is_argument = false; + next_context_iter = null_context_list_iterator; state = 0; break; @@ -2936,6 +3124,8 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, logical_file_name, tp->line_number, nesting_level, tp->string); #endif + next_is_argument = false; + next_context_iter = null_context_list_iterator; state = 0; break; @@ -2944,6 +3134,8 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, fprintf (stderr, "%s:%d: type regex operator (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif + next_is_argument = false; + next_context_iter = null_context_list_iterator; break; case token_type_other: @@ -2951,6 +3143,8 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, fprintf (stderr, "%s:%d: type other (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif + next_is_argument = false; + next_context_iter = null_context_list_iterator; state = 0; break; @@ -2966,6 +3160,7 @@ extract_balanced (message_list_ty *mlp, int arg_sg, int arg_pl, int state, void extract_perl (FILE *f, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { message_list_ty *mlp = mdlp->item[0]->messages; @@ -2978,6 +3173,8 @@ extract_perl (FILE *f, const char *real_filename, const char *logical_filename, last_comment_line = -1; last_non_comment_line = -1; + flag_context_list_table = flag_table; + init_keywords (); token_stack.items = NULL; @@ -2990,7 +3187,9 @@ extract_perl (FILE *f, const char *real_filename, const char *logical_filename, /* Eat tokens until eof is seen. When extract_balanced returns due to an unbalanced closing brace, just restart it. */ - while (!extract_balanced (mlp, -1, -1, 0, token_type_rbrace)) + while (!extract_balanced (mlp, 0, token_type_rbrace, + null_context, null_context_list_iterator, + -1, -1)) ; fp = NULL; diff --git a/gettext-tools/src/x-perl.h b/gettext-tools/src/x-perl.h index 9c28df66b..95135016c 100644 --- a/gettext-tools/src/x-perl.h +++ b/gettext-tools/src/x-perl.h @@ -24,12 +24,16 @@ { "cgi", "perl" }, \ #define SCANNERS_PERL \ - { "perl", extract_perl, &formatstring_perl, &formatstring_perl_brace }, \ + { "perl", extract_perl, \ + &flag_table_perl, &formatstring_perl, &formatstring_perl_brace }, \ /* Scan a Perl file and add its translatable strings to mdlp. */ extern void extract_perl (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); extern void x_perl_keyword (const char *keyword); extern void x_perl_extract_all (void); + +extern void init_flag_table_perl (void); diff --git a/gettext-tools/src/x-php.c b/gettext-tools/src/x-php.c index 027b74e1c..f45806f6f 100644 --- a/gettext-tools/src/x-php.c +++ b/gettext-tools/src/x-php.c @@ -27,8 +27,8 @@ #include #include "message.h" -#include "x-php.h" #include "xgettext.h" +#include "x-php.h" #include "error.h" #include "xmalloc.h" #include "exit.h" @@ -106,6 +106,23 @@ init_keywords () } } +void +init_flag_table_php () +{ + xgettext_record_flag ("_:1:pass-php-format"); + xgettext_record_flag ("gettext:1:pass-php-format"); + xgettext_record_flag ("dgettext:2:pass-php-format"); + xgettext_record_flag ("dcgettext:2:pass-php-format"); + xgettext_record_flag ("ngettext:1:pass-php-format"); + xgettext_record_flag ("ngettext:2:pass-php-format"); + xgettext_record_flag ("dngettext:2:pass-php-format"); + xgettext_record_flag ("dngettext:3:pass-php-format"); + xgettext_record_flag ("dcngettext:2:pass-php-format"); + xgettext_record_flag ("dcngettext:3:pass-php-format"); + xgettext_record_flag ("sprintf:1:php-format"); + xgettext_record_flag ("printf:1:php-format"); +} + /* ======================== Reading of characters. ======================== */ @@ -1165,6 +1182,11 @@ x_php_lex (token_ty *tp) /* ========================= Extracting strings. ========================== */ + +/* Context lookup table. */ +static flag_context_list_table_ty *flag_context_list_table; + + /* The file is broken into tokens. Scan the token stream, looking for a keyword, followed by a left paren, followed by a string. When we see this sequence, we have something to remember. We assume we are @@ -1189,6 +1211,8 @@ x_php_lex (token_ty *tp) Return true upon eof, false upon closing parenthesis. */ static bool extract_parenthesized (message_list_ty *mlp, + flag_context_ty outer_context, + flag_context_list_iterator_ty context_iter, int commas_to_skip, int plural_commas) { /* Remember the message containing the msgid, for msgid_plural. */ @@ -1199,6 +1223,13 @@ extract_parenthesized (message_list_ty *mlp, /* Parameters of the keyword just seen. Defined only in state 1. */ int next_commas_to_skip = -1; int next_plural_commas = 0; + /* Context iterator that will be used if the next token is a '('. */ + flag_context_list_iterator_ty next_context_iter = + passthrough_context_list_iterator; + /* Current context. */ + flag_context_ty inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance (&context_iter)); /* Start state is 0. */ state = 0; @@ -1228,15 +1259,20 @@ extract_parenthesized (message_list_ty *mlp, else state = 0; } + next_context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + token.string, strlen (token.string))); free (token.string); continue; case token_type_lparen: - if (state - ? extract_parenthesized (mlp, next_commas_to_skip, - next_plural_commas) - : extract_parenthesized (mlp, -1, 0)) + if (extract_parenthesized (mlp, inner_context, next_context_iter, + state ? next_commas_to_skip : -1, + state ? next_plural_commas: 0)) return true; + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -1257,6 +1293,11 @@ extract_parenthesized (message_list_ty *mlp, else commas_to_skip = -1; } + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); + next_context_iter = passthrough_context_list_iterator; state = 0; continue; @@ -1267,7 +1308,7 @@ extract_parenthesized (message_list_ty *mlp, pos.line_number = token.line_number; if (extract_all) - remember_a_message (mlp, token.string, &pos); + remember_a_message (mlp, token.string, inner_context, &pos); else { if (commas_to_skip == 0) @@ -1275,8 +1316,9 @@ extract_parenthesized (message_list_ty *mlp, if (plural_mp == NULL) { /* Seen an msgid. */ - message_ty *mp = remember_a_message (mlp, token.string, - &pos); + message_ty *mp = + remember_a_message (mlp, token.string, + inner_context, &pos); if (plural_commas > 0) plural_mp = mp; } @@ -1284,7 +1326,7 @@ extract_parenthesized (message_list_ty *mlp, { /* Seen an msgid_plural. */ remember_a_message_plural (plural_mp, token.string, - &pos); + inner_context, &pos); plural_mp = NULL; } } @@ -1292,10 +1334,12 @@ extract_parenthesized (message_list_ty *mlp, free (token.string); } } + next_context_iter = null_context_list_iterator; state = 0; continue; case token_type_other: + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -1312,6 +1356,7 @@ extract_parenthesized (message_list_ty *mlp, void extract_php (FILE *f, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { message_list_ty *mlp = mdlp->item[0]->messages; @@ -1324,6 +1369,8 @@ extract_php (FILE *f, last_comment_line = -1; last_non_comment_line = -1; + flag_context_list_table = flag_table; + init_keywords (); /* Initial mode is HTML mode, not PHP mode. */ @@ -1331,7 +1378,8 @@ extract_php (FILE *f, /* Eat tokens until eof is seen. When extract_parenthesized returns due to an unbalanced closing parenthesis, just restart it. */ - while (!extract_parenthesized (mlp, -1, 0)) + while (!extract_parenthesized (mlp, null_context, null_context_list_iterator, + -1, 0)) ; /* Close scanner. */ diff --git a/gettext-tools/src/x-php.h b/gettext-tools/src/x-php.h index 7ec6cddaf..309e679db 100644 --- a/gettext-tools/src/x-php.h +++ b/gettext-tools/src/x-php.h @@ -23,12 +23,16 @@ { "php4", "PHP" }, \ #define SCANNERS_PHP \ - { "PHP", extract_php, &formatstring_php, NULL }, \ + { "PHP", extract_php, \ + &flag_table_php, &formatstring_php, NULL }, \ /* Scan a PHP file and add its translatable strings to mdlp. */ extern void extract_php (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); extern void x_php_keyword (const char *keyword); extern void x_php_extract_all (void); + +extern void init_flag_table_php (void); diff --git a/gettext-tools/src/x-po.c b/gettext-tools/src/x-po.c index b51f6d564..3c55a476a 100644 --- a/gettext-tools/src/x-po.c +++ b/gettext-tools/src/x-po.c @@ -27,9 +27,9 @@ #include #include "message.h" +#include "xgettext.h" #include "x-po.h" #include "x-properties.h" -#include "xgettext.h" #include "xmalloc.h" #include "read-po.h" #include "po-lex.h" @@ -124,6 +124,7 @@ extract (FILE *fp, void extract_po (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { extract (fp, real_filename, logical_filename, syntax_po, mdlp); @@ -133,6 +134,7 @@ extract_po (FILE *fp, void extract_properties (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { extract (fp, real_filename, logical_filename, syntax_properties, mdlp); diff --git a/gettext-tools/src/x-po.h b/gettext-tools/src/x-po.h index 72ae8b05a..7a096ce6f 100644 --- a/gettext-tools/src/x-po.h +++ b/gettext-tools/src/x-po.h @@ -22,9 +22,10 @@ { "pot", "PO" }, \ #define SCANNERS_PO \ - { "PO", extract_po, NULL, NULL }, \ + { "PO", extract_po, NULL, NULL, NULL }, \ /* Scan a PO file and add its translatable strings to mdlp. */ extern void extract_po (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); diff --git a/gettext-tools/src/x-properties.h b/gettext-tools/src/x-properties.h index 10c1acb04..360229a98 100644 --- a/gettext-tools/src/x-properties.h +++ b/gettext-tools/src/x-properties.h @@ -21,9 +21,10 @@ { "properties", "JavaProperties" }, \ #define SCANNERS_PROPERTIES \ - { "JavaProperties", extract_properties, NULL, NULL }, \ + { "JavaProperties", extract_properties, NULL, NULL, NULL }, \ /* Scan a JavaProperties file and add its translatable strings to mdlp. */ extern void extract_properties (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); diff --git a/gettext-tools/src/x-python.c b/gettext-tools/src/x-python.c index e59ee750c..c007eae1b 100644 --- a/gettext-tools/src/x-python.c +++ b/gettext-tools/src/x-python.c @@ -29,8 +29,8 @@ #include #include "message.h" -#include "x-python.h" #include "xgettext.h" +#include "x-python.h" #include "error.h" #include "error-progname.h" #include "xmalloc.h" @@ -114,6 +114,22 @@ init_keywords () } } +void +init_flag_table_python () +{ + xgettext_record_flag ("gettext:1:pass-python-format"); + xgettext_record_flag ("ugettext:1:pass-python-format"); + xgettext_record_flag ("dgettext:2:pass-python-format"); + xgettext_record_flag ("ngettext:1:pass-python-format"); + xgettext_record_flag ("ngettext:2:pass-python-format"); + xgettext_record_flag ("ungettext:1:pass-python-format"); + xgettext_record_flag ("ungettext:2:pass-python-format"); + xgettext_record_flag ("dngettext:2:pass-python-format"); + xgettext_record_flag ("dngettext:3:pass-python-format"); + xgettext_record_flag ("_:1:pass-python-format"); + /* xgettext_record_flag ("%:1:python-format"); // % is an infix operator! */ +} + /* ======================== Reading of characters. ======================== */ @@ -968,6 +984,11 @@ x_python_lex (token_ty *tp) /* ========================= Extracting strings. ========================== */ + +/* Context lookup table. */ +static flag_context_list_table_ty *flag_context_list_table; + + /* The file is broken into tokens. Scan the token stream, looking for a keyword, followed by a left paren, followed by a string. When we see this sequence, we have something to remember. We assume we are @@ -992,6 +1013,8 @@ x_python_lex (token_ty *tp) Return true upon eof, false upon closing parenthesis. */ static bool extract_parenthesized (message_list_ty *mlp, + flag_context_ty outer_context, + flag_context_list_iterator_ty context_iter, int commas_to_skip, int plural_commas) { /* Remember the message containing the msgid, for msgid_plural. */ @@ -1002,6 +1025,13 @@ extract_parenthesized (message_list_ty *mlp, /* Parameters of the keyword just seen. Defined only in state 1. */ int next_commas_to_skip = -1; int next_plural_commas = 0; + /* Context iterator that will be used if the next token is a '('. */ + flag_context_list_iterator_ty next_context_iter = + passthrough_context_list_iterator; + /* Current context. */ + flag_context_ty inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance (&context_iter)); /* Start state is 0. */ state = 0; @@ -1031,15 +1061,20 @@ extract_parenthesized (message_list_ty *mlp, else state = 0; } + next_context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + token.string, strlen (token.string))); free (token.string); continue; case token_type_lparen: - if (state - ? extract_parenthesized (mlp, next_commas_to_skip, - next_plural_commas) - : extract_parenthesized (mlp, -1, 0)) + if (extract_parenthesized (mlp, inner_context, next_context_iter, + state ? next_commas_to_skip : -1, + state ? next_plural_commas : 0)) return true; + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -1060,6 +1095,11 @@ extract_parenthesized (message_list_ty *mlp, else commas_to_skip = -1; } + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); + next_context_iter = passthrough_context_list_iterator; state = 0; continue; @@ -1070,7 +1110,7 @@ extract_parenthesized (message_list_ty *mlp, pos.line_number = token.line_number; if (extract_all) - remember_a_message (mlp, token.string, &pos); + remember_a_message (mlp, token.string, inner_context, &pos); else { if (commas_to_skip == 0) @@ -1078,8 +1118,9 @@ extract_parenthesized (message_list_ty *mlp, if (plural_mp == NULL) { /* Seen an msgid. */ - message_ty *mp = remember_a_message (mlp, token.string, - &pos); + message_ty *mp = + remember_a_message (mlp, token.string, + inner_context, &pos); if (plural_commas > 0) plural_mp = mp; } @@ -1087,7 +1128,7 @@ extract_parenthesized (message_list_ty *mlp, { /* Seen an msgid_plural. */ remember_a_message_plural (plural_mp, token.string, - &pos); + inner_context, &pos); plural_mp = NULL; } } @@ -1095,6 +1136,7 @@ extract_parenthesized (message_list_ty *mlp, free (token.string); } } + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -1102,6 +1144,7 @@ extract_parenthesized (message_list_ty *mlp, return true; case token_type_other: + next_context_iter = null_context_list_iterator; state = 0; continue; @@ -1115,6 +1158,7 @@ extract_parenthesized (message_list_ty *mlp, void extract_python (FILE *f, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { message_list_ty *mlp = mdlp->item[0]->messages; @@ -1132,11 +1176,14 @@ extract_python (FILE *f, open_pbb = 0; + flag_context_list_table = flag_table; + init_keywords (); /* Eat tokens until eof is seen. When extract_parenthesized returns due to an unbalanced closing parenthesis, just restart it. */ - while (!extract_parenthesized (mlp, -1, 0)) + while (!extract_parenthesized (mlp, null_context, null_context_list_iterator, + -1, 0)) ; fp = NULL; diff --git a/gettext-tools/src/x-python.h b/gettext-tools/src/x-python.h index af96927d0..eeaac8b5b 100644 --- a/gettext-tools/src/x-python.h +++ b/gettext-tools/src/x-python.h @@ -21,12 +21,16 @@ { "py", "Python" }, \ #define SCANNERS_PYTHON \ - { "Python", extract_python, &formatstring_python, NULL }, \ + { "Python", extract_python, \ + &flag_table_python, &formatstring_python, NULL }, \ /* Scan a Python file and add its translatable strings to mdlp. */ extern void extract_python (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); extern void x_python_keyword (const char *keyword); extern void x_python_extract_all (void); + +extern void init_flag_table_python (void); diff --git a/gettext-tools/src/x-rst.c b/gettext-tools/src/x-rst.c index 15bf6b501..2e4dc8b6c 100644 --- a/gettext-tools/src/x-rst.c +++ b/gettext-tools/src/x-rst.c @@ -27,8 +27,8 @@ #include #include "message.h" -#include "x-rst.h" #include "xgettext.h" +#include "x-rst.h" #include "error.h" #include "error-progname.h" #include "xmalloc.h" @@ -57,6 +57,7 @@ void extract_rst (FILE *f, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { static char *buffer; @@ -218,7 +219,7 @@ extract_rst (FILE *f, pos.file_name = location; pos.line_number = (size_t)(-1); - remember_a_message (mlp, msgid, &pos); + remember_a_message (mlp, msgid, null_context, &pos); /* Here c is the last read character: EOF or '\n'. */ if (c == EOF) diff --git a/gettext-tools/src/x-rst.h b/gettext-tools/src/x-rst.h index 4dfe206e7..329e68b44 100644 --- a/gettext-tools/src/x-rst.h +++ b/gettext-tools/src/x-rst.h @@ -21,9 +21,11 @@ { "rst", "RST" }, \ #define SCANNERS_RST \ - { "RST", extract_rst, &formatstring_pascal, NULL }, \ + { "RST", extract_rst, \ + NULL, &formatstring_pascal, NULL }, \ /* Scan an RST file and add its translatable strings to mdlp. */ extern void extract_rst (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); diff --git a/gettext-tools/src/x-sh.c b/gettext-tools/src/x-sh.c index 3cef49f95..129f53214 100644 --- a/gettext-tools/src/x-sh.c +++ b/gettext-tools/src/x-sh.c @@ -28,8 +28,8 @@ #include #include "message.h" -#include "x-sh.h" #include "xgettext.h" +#include "x-sh.h" #include "error.h" #include "xmalloc.h" #include "exit.h" @@ -119,6 +119,17 @@ init_keywords () } } +void +init_flag_table_sh () +{ + xgettext_record_flag ("gettext:1:pass-sh-format"); + xgettext_record_flag ("ngettext:1:pass-sh-format"); + xgettext_record_flag ("ngettext:2:pass-sh-format"); + xgettext_record_flag ("eval_gettext:1:sh-format"); + xgettext_record_flag ("eval_ngettext:1:sh-format"); + xgettext_record_flag ("eval_ngettext:2:sh-format"); +} + /* ======================== Reading of characters. ======================== */ @@ -671,8 +682,13 @@ phase2_ungetc (int c) } +/* Context lookup table. */ +static flag_context_list_table_ty *flag_context_list_table; + + /* Forward declaration of local functions. */ -static enum word_type read_command_list (int looking_for); +static enum word_type read_command_list (int looking_for, + flag_context_ty outer_context); @@ -680,7 +696,7 @@ static enum word_type read_command_list (int looking_for); 'looking_for' denotes a parse terminator, either CLOSING_BACKQUOTE, ')' or '\0'. */ static void -read_word (struct word *wp, int looking_for) +read_word (struct word *wp, int looking_for, flag_context_ty context) { int c; bool all_unquoted_digits; @@ -827,7 +843,7 @@ read_word (struct word *wp, int looking_for) { /* Command substitution. */ phase2_ungetc (c3); - read_command_list (')'); + read_command_list (')', context); } } else if (c2 == '\'' && !open_singlequote) @@ -986,7 +1002,8 @@ read_word (struct word *wp, int looking_for) grow_token (&string); string.chars[string.charcount++] = (unsigned char) c; } - remember_a_message (mlp, string_of_token (&string), &pos); + remember_a_message (mlp, string_of_token (&string), + context, &pos); free_token (&string); error_with_progname = false; @@ -1038,7 +1055,7 @@ read_word (struct word *wp, int looking_for) /* Handle an opening backquote. */ saw_opening_backquote (); - read_command_list (CLOSING_BACKQUOTE); + read_command_list (CLOSING_BACKQUOTE, context); wp->type = t_other; continue; @@ -1073,7 +1090,7 @@ read_word (struct word *wp, int looking_for) or '\0'. Returns the type of the word that terminated the command. */ static enum word_type -read_command (int looking_for) +read_command (int looking_for, flag_context_ty outer_context) { /* Read the words that make up the command. Here we completely ignore field splitting at whitespace and wildcard @@ -1087,6 +1104,7 @@ read_command (int looking_for) command. */ int arg = 0; /* Current argument number. */ bool arg_of_redirect = false; /* True right after a redirection operator. */ + flag_context_list_iterator_ty context_iter; int argnum1 = -1; /* First string position. */ int argnum2 = -1; /* Plural string position. */ message_ty *plural_mp = NULL; /* Remember the msgid. */ @@ -1094,8 +1112,17 @@ read_command (int looking_for) for (;;) { struct word inner; + flag_context_ty inner_context; + + if (arg == 0) + inner_context = null_context; + else + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); - read_word (&inner, looking_for); + read_word (&inner, looking_for, inner_context); /* Recognize end of command. */ if (inner.type == t_separator @@ -1111,7 +1138,8 @@ read_command (int looking_for) pos.file_name = logical_file_name; pos.line_number = inner.line_number_at_start; - remember_a_message (mlp, string_of_word (&inner), &pos); + remember_a_message (mlp, string_of_word (&inner), + inner_context, &pos); } } @@ -1145,8 +1173,16 @@ read_command (int looking_for) argnum2 = (int) (long) keyword_value >> 10; } + context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + function_name, strlen (function_name))); + free (function_name); } + else + context_iter = null_context_list_iterator; } else { @@ -1162,7 +1198,8 @@ read_command (int looking_for) pos.file_name = logical_file_name; pos.line_number = inner.line_number_at_start; - mp = remember_a_message (mlp, string_of_word (&inner), &pos); + mp = remember_a_message (mlp, string_of_word (&inner), + inner_context, &pos); if (argnum2 > 0) plural_mp = mp; } @@ -1175,13 +1212,15 @@ read_command (int looking_for) pos.file_name = logical_file_name; pos.line_number = inner.line_number_at_start; - remember_a_message_plural (plural_mp, string_of_word (&inner), &pos); + remember_a_message_plural (plural_mp, string_of_word (&inner), + inner_context, &pos); } } if (arg >= argnum1 && arg >= argnum2) { /* Stop looking for arguments of the last function_name. */ + /* FIXME: What about context_iter? */ argnum1 = -1; argnum2 = -1; plural_mp = NULL; @@ -1201,13 +1240,13 @@ read_command (int looking_for) or '\0'. Returns the type of the word that terminated the command list. */ static enum word_type -read_command_list (int looking_for) +read_command_list (int looking_for, flag_context_ty outer_context) { for (;;) { enum word_type terminator; - terminator = read_command (looking_for); + terminator = read_command (looking_for, outer_context); if (terminator != t_separator) return terminator; } @@ -1217,6 +1256,7 @@ read_command_list (int looking_for) void extract_sh (FILE *f, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { mlp = mdlp->item[0]->messages; @@ -1234,10 +1274,12 @@ extract_sh (FILE *f, open_doublequote = false; open_singlequote = false; + flag_context_list_table = flag_table; + init_keywords (); /* Eat tokens until eof is seen. */ - read_command_list ('\0'); + read_command_list ('\0', null_context); fp = NULL; real_file_name = NULL; diff --git a/gettext-tools/src/x-sh.h b/gettext-tools/src/x-sh.h index 84b8b2793..cc80981a7 100644 --- a/gettext-tools/src/x-sh.h +++ b/gettext-tools/src/x-sh.h @@ -22,12 +22,16 @@ { "bash", "Shell" }, \ #define SCANNERS_SH \ - { "Shell", extract_sh, &formatstring_sh, NULL }, \ + { "Shell", extract_sh, \ + &flag_table_sh, &formatstring_sh, NULL }, \ /* Scan a shell script file and add its translatable strings to mdlp. */ extern void extract_sh (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); extern void x_sh_keyword (const char *keyword); extern void x_sh_extract_all (void); + +extern void init_flag_table_sh (void); diff --git a/gettext-tools/src/x-smalltalk.c b/gettext-tools/src/x-smalltalk.c index fcefdfeea..12aa259c9 100644 --- a/gettext-tools/src/x-smalltalk.c +++ b/gettext-tools/src/x-smalltalk.c @@ -26,8 +26,8 @@ #include #include "message.h" -#include "x-smalltalk.h" #include "xgettext.h" +#include "x-smalltalk.h" #include "error.h" #include "xmalloc.h" #include "exit.h" @@ -481,6 +481,7 @@ x_smalltalk_lex (token_ty *tp) void extract_smalltalk (FILE *f, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { message_list_ty *mlp = mdlp->item[0]->messages; @@ -532,7 +533,7 @@ extract_smalltalk (FILE *f, lex_pos_ty pos; pos.file_name = logical_file_name; pos.line_number = token.line_number; - remember_a_message (mlp, token.string, &pos); + remember_a_message (mlp, token.string, null_context, &pos); state = 0; break; } @@ -541,7 +542,8 @@ extract_smalltalk (FILE *f, lex_pos_ty pos; pos.file_name = logical_file_name; pos.line_number = token.line_number; - plural_mp = remember_a_message (mlp, token.string, &pos); + plural_mp = remember_a_message (mlp, token.string, + null_context, &pos); state = 4; break; } @@ -550,7 +552,8 @@ extract_smalltalk (FILE *f, lex_pos_ty pos; pos.file_name = logical_file_name; pos.line_number = token.line_number; - remember_a_message_plural (plural_mp, token.string, &pos); + remember_a_message_plural (plural_mp, token.string, + null_context, &pos); state = 0; break; } diff --git a/gettext-tools/src/x-smalltalk.h b/gettext-tools/src/x-smalltalk.h index 407ce4461..858f1eac4 100644 --- a/gettext-tools/src/x-smalltalk.h +++ b/gettext-tools/src/x-smalltalk.h @@ -21,9 +21,11 @@ { "st", "Smalltalk" }, \ #define SCANNERS_SMALLTALK \ - { "Smalltalk", extract_smalltalk, &formatstring_smalltalk, NULL }, \ + { "Smalltalk", extract_smalltalk, \ + NULL, &formatstring_smalltalk, NULL }, \ /* Scan a Smalltalk file and add its translatable strings to mdlp. */ extern void extract_smalltalk (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); diff --git a/gettext-tools/src/x-tcl.c b/gettext-tools/src/x-tcl.c index 8112fd3b1..549b3d133 100644 --- a/gettext-tools/src/x-tcl.c +++ b/gettext-tools/src/x-tcl.c @@ -30,8 +30,8 @@ #include #include "message.h" -#include "x-tcl.h" #include "xgettext.h" +#include "x-tcl.h" #include "error.h" #include "xmalloc.h" #include "exit.h" @@ -117,6 +117,13 @@ init_keywords () } } +void +init_flag_table_tcl () +{ + xgettext_record_flag ("::msgcat::mc:1:pass-tcl-format"); + xgettext_record_flag ("format:1:tcl-format"); +} + /* ======================== Reading of characters. ======================== */ @@ -446,6 +453,10 @@ string_of_word (const struct word *wp) } +/* Context lookup table. */ +static flag_context_list_table_ty *flag_context_list_table; + + /* Read an escape sequence. The value is an ISO-8859-1 character (in the range 0x00..0xff) or a Unicode character (in the range 0x0000..0xffff). */ static int @@ -555,12 +566,14 @@ enum terminator }; /* Forward declaration of local functions. */ -static enum word_type read_command_list (int looking_for); +static enum word_type read_command_list (int looking_for, + flag_context_ty outer_context); /* Accumulate tokens into the given word. 'looking_for' denotes a parse terminator combination. */ static int -accumulate_word (struct word *wp, enum terminator looking_for) +accumulate_word (struct word *wp, enum terminator looking_for, + flag_context_ty context) { int c; @@ -630,7 +643,7 @@ accumulate_word (struct word *wp, enum terminator looking_for) struct word index_word; index_word.type = t_other; - c = accumulate_word (&index_word, te_paren); + c = accumulate_word (&index_word, te_paren, null_context); if (c != EOF && c != ')') phase2_ungetc (c); wp->type = t_other; @@ -657,7 +670,7 @@ accumulate_word (struct word *wp, enum terminator looking_for) } else if (c == '[') { - read_command_list (']'); + read_command_list (']', context); wp->type = t_other; } else if (c == '\\') @@ -693,7 +706,7 @@ accumulate_word (struct word *wp, enum terminator looking_for) /* Read the next word. 'looking_for' denotes a parse terminator, either ']' or '\0'. */ static void -read_word (struct word *wp, int looking_for) +read_word (struct word *wp, int looking_for, flag_context_ty context) { int c; @@ -748,7 +761,7 @@ read_word (struct word *wp, int looking_for) previous_depth = phase2_push () - 1; /* Interpret it as a command list. */ - terminator = read_command_list ('\0'); + terminator = read_command_list ('\0', null_context); if (terminator == t_brace) phase2_pop (previous_depth); @@ -765,7 +778,7 @@ read_word (struct word *wp, int looking_for) if (c == '"') { - c = accumulate_word (wp, te_quote); + c = accumulate_word (wp, te_quote, context); if (c != EOF && c != '"') phase2_ungetc (c); } @@ -775,7 +788,8 @@ read_word (struct word *wp, int looking_for) c = accumulate_word (wp, looking_for == ']' ? te_space_separator_bracket - : te_space_separator); + : te_space_separator, + context); if (c != EOF) phase2_ungetc (c); } @@ -794,7 +808,7 @@ read_word (struct word *wp, int looking_for) Returns the type of the word that terminated the command: t_separator or t_bracket (only if looking_for is ']') or t_brace or t_eof. */ static enum word_type -read_command (int looking_for) +read_command (int looking_for, flag_context_ty outer_context) { int c; @@ -828,6 +842,7 @@ read_command (int looking_for) /* Read the words that make up the command. */ { int arg = 0; /* Current argument number. */ + flag_context_list_iterator_ty context_iter; int argnum1 = 0; /* First string position. */ int argnum2 = 0; /* Plural string position. */ message_ty *plural_mp = NULL; /* Remember the msgid. */ @@ -835,8 +850,17 @@ read_command (int looking_for) for (;; arg++) { struct word inner; + flag_context_ty inner_context; + + if (arg == 0) + inner_context = null_context; + else + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); - read_word (&inner, looking_for); + read_word (&inner, looking_for, inner_context); /* Recognize end of command. */ if (inner.type == t_separator || inner.type == t_bracket @@ -851,66 +875,75 @@ read_command (int looking_for) pos.file_name = logical_file_name; pos.line_number = inner.line_number_at_start; - remember_a_message (mlp, string_of_word (&inner), &pos); + remember_a_message (mlp, string_of_word (&inner), + inner_context, &pos); } } - else + + if (arg == 0) { - if (arg == 0) + /* This is the function position. */ + if (inner.type == t_string) { - /* This is the function position. */ - if (inner.type == t_string) + char *function_name = string_of_word (&inner); + char *stripped_name; + void *keyword_value; + + /* A leading "::" is redundant. */ + stripped_name = function_name; + if (function_name[0] == ':' && function_name[1] == ':') + stripped_name += 2; + + if (find_entry (&keywords, + stripped_name, strlen (stripped_name), + &keyword_value) + == 0) { - char *function_name = string_of_word (&inner); - char *stripped_name; - void *keyword_value; - - /* A leading "::" is redundant. */ - stripped_name = function_name; - if (function_name[0] == ':' && function_name[1] == ':') - stripped_name += 2; - - if (find_entry (&keywords, - stripped_name, strlen (stripped_name), - &keyword_value) - == 0) - { - argnum1 = (int) (long) keyword_value & ((1 << 10) - 1); - argnum2 = (int) (long) keyword_value >> 10; - } - - free (function_name); + argnum1 = (int) (long) keyword_value & ((1 << 10) - 1); + argnum2 = (int) (long) keyword_value >> 10; } + + context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + stripped_name, strlen (stripped_name))); + + free (function_name); } else + context_iter = null_context_list_iterator; + } + else + { + /* These are the argument positions. + Extract a string if we have reached the right + argument position. */ + if (arg == argnum1) { - /* These are the argument positions. - Extract a string if we have reached the right - argument position. */ - if (arg == argnum1) + if (inner.type == t_string) { - if (inner.type == t_string) - { - lex_pos_ty pos; - message_ty *mp; - - pos.file_name = logical_file_name; - pos.line_number = inner.line_number_at_start; - mp = remember_a_message (mlp, string_of_word (&inner), &pos); - if (argnum2 > 0) - plural_mp = mp; - } + lex_pos_ty pos; + message_ty *mp; + + pos.file_name = logical_file_name; + pos.line_number = inner.line_number_at_start; + mp = remember_a_message (mlp, string_of_word (&inner), + inner_context, &pos); + if (argnum2 > 0) + plural_mp = mp; } - else if (arg == argnum2) + } + else if (arg == argnum2) + { + if (inner.type == t_string && plural_mp != NULL) { - if (inner.type == t_string && plural_mp != NULL) - { - lex_pos_ty pos; - - pos.file_name = logical_file_name; - pos.line_number = inner.line_number_at_start; - remember_a_message_plural (plural_mp, string_of_word (&inner), &pos); - } + lex_pos_ty pos; + + pos.file_name = logical_file_name; + pos.line_number = inner.line_number_at_start; + remember_a_message_plural (plural_mp, string_of_word (&inner), + inner_context, &pos); } } } @@ -926,13 +959,13 @@ read_command (int looking_for) Returns the type of the word that terminated the command list: t_bracket (only if looking_for is ']') or t_brace or t_eof. */ static enum word_type -read_command_list (int looking_for) +read_command_list (int looking_for, flag_context_ty outer_context) { for (;;) { enum word_type terminator; - terminator = read_command (looking_for); + terminator = read_command (looking_for, outer_context); if (terminator != t_separator) return terminator; } @@ -942,6 +975,7 @@ read_command_list (int looking_for) void extract_tcl (FILE *f, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp) { mlp = mdlp->item[0]->messages; @@ -960,10 +994,12 @@ extract_tcl (FILE *f, last_comment_line = -1; last_non_comment_line = -1; + flag_context_list_table = flag_table; + init_keywords (); /* Eat tokens until eof is seen. */ - read_command_list ('\0'); + read_command_list ('\0', null_context); fp = NULL; real_file_name = NULL; diff --git a/gettext-tools/src/x-tcl.h b/gettext-tools/src/x-tcl.h index 13d155193..89a083949 100644 --- a/gettext-tools/src/x-tcl.h +++ b/gettext-tools/src/x-tcl.h @@ -21,11 +21,13 @@ { "tcl", "Tcl" }, \ #define SCANNERS_TCL \ - { "Tcl", extract_tcl, &formatstring_tcl, NULL }, \ + { "Tcl", extract_tcl, \ + &flag_table_tcl, &formatstring_tcl, NULL }, \ /* Scan a Tcl file and add its translatable strings to mdlp. */ extern void extract_tcl (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); @@ -33,3 +35,5 @@ extern void extract_tcl (FILE *fp, const char *real_filename, extern void x_tcl_extract_all (void); extern void x_tcl_keyword (const char *name); + +extern void init_flag_table_tcl (void); diff --git a/gettext-tools/src/x-ycp.c b/gettext-tools/src/x-ycp.c index d8ee34503..9116b4f89 100644 --- a/gettext-tools/src/x-ycp.c +++ b/gettext-tools/src/x-ycp.c @@ -29,8 +29,8 @@ #include #include "message.h" -#include "x-ycp.h" #include "xgettext.h" +#include "x-ycp.h" #include "error.h" #include "xmalloc.h" #include "exit.h" @@ -43,6 +43,19 @@ See also libycp/src/scanner.ll. */ +void +init_flag_table_ycp () +{ + xgettext_record_flag ("sformat:1:ycp-format"); + xgettext_record_flag ("y2debug:1:ycp-format"); + xgettext_record_flag ("y2milestone:1:ycp-format"); + xgettext_record_flag ("y2warning:1:ycp-format"); + xgettext_record_flag ("y2error:1:ycp-format"); + xgettext_record_flag ("y2security:1:ycp-format"); + xgettext_record_flag ("y2internal:1:ycp-format"); +} + + /* ======================== Reading of characters. ======================== */ @@ -520,16 +533,13 @@ x_ycp_lex (token_ty *tp) /* ========================= Extracting strings. ========================== */ -void -extract_ycp (FILE *f, - const char *real_filename, const char *logical_filename, - msgdomain_list_ty *mdlp) -{ - message_list_ty *mlp = mdlp->item[0]->messages; - int state; - message_ty *plural_mp = NULL; /* defined only when in states 1 and 2 */ - /* The file is broken into tokens. +/* Context lookup table. */ +static flag_context_list_table_ty *flag_context_list_table; + + +/* The file is broken into tokens. + Normal handling: Look for [A] _( [B] msgid ... ) Plural handling: Look for @@ -537,19 +547,33 @@ extract_ycp (FILE *f, At point [A]: state == 0. At point [B]: state == 1, plural_mp == NULL. At point [C]: state == 2, plural_mp != NULL. - At point [D]: state == 1, plural_mp != NULL. */ + At point [D]: state == 1, plural_mp != NULL. - fp = f; - real_file_name = real_filename; - logical_file_name = xstrdup (logical_filename); - line_number = 1; - char_in_line = 0; + We use recursion because we have to set the context according to the given + flags. */ - last_comment_line = -1; - last_non_comment_line = -1; - /* Start state is 0. */ - state = 0; +/* Extract messages until the next balanced closing parenthesis. + Extracted messages are added to MLP. + Return true upon eof, false upon closing parenthesis. */ +static bool +extract_parenthesized (message_list_ty *mlp, + flag_context_ty outer_context, + flag_context_list_iterator_ty context_iter, + bool in_i18n) +{ + int state; /* 1 or 2 inside _( ... ), otherwise 0 */ + message_ty *plural_mp = NULL; /* defined only when in states 1 and 2 */ + /* Context iterator that will be used if the next token is a '('. */ + flag_context_list_iterator_ty next_context_iter = + passthrough_context_list_iterator; + /* Current context. */ + flag_context_ty inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance (&context_iter)); + + /* Start state is 0 or 1. */ + state = (in_i18n ? 1 : 0); for (;;) { @@ -559,8 +583,11 @@ extract_ycp (FILE *f, switch (token.type) { case token_type_i18n: - state = 1; - plural_mp = NULL; + if (extract_parenthesized (mlp, inner_context, next_context_iter, + true)) + return true; + next_context_iter = null_context_list_iterator; + state = 0; continue; case token_type_string_literal: @@ -573,13 +600,15 @@ extract_ycp (FILE *f, if (plural_mp == NULL) { /* Seen an msgid. */ - plural_mp = remember_a_message (mlp, token.string, &pos); + plural_mp = remember_a_message (mlp, token.string, + inner_context, &pos); state = 2; } else { /* Seen an msgid_plural. */ - remember_a_message_plural (plural_mp, token.string, &pos); + remember_a_message_plural (plural_mp, token.string, + inner_context, &pos); state = 0; } } @@ -588,27 +617,81 @@ extract_ycp (FILE *f, free (token.string); state = 0; } + next_context_iter = null_context_list_iterator; + continue; + + case token_type_symbol: + next_context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + token.string, strlen (token.string))); + free (token.string); + state = 0; continue; + case token_type_lparen: + if (extract_parenthesized (mlp, inner_context, next_context_iter, + false)) + return true; + next_context_iter = null_context_list_iterator; + state = 0; + continue; + + case token_type_rparen: + return false; + case token_type_comma: if (state == 2) state = 1; else state = 0; + inner_context = + inherited_context (outer_context, + flag_context_list_iterator_advance ( + &context_iter)); + next_context_iter = passthrough_context_list_iterator; + continue; + + case token_type_other: + next_context_iter = null_context_list_iterator; + state = 0; continue; case token_type_eof: - break; + return true; - case token_type_symbol: - free (token.string); - /* FALLTHROUGH */ default: - state = 0; - continue; + abort (); } - break; } +} + + +void +extract_ycp (FILE *f, + const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, + msgdomain_list_ty *mdlp) +{ + message_list_ty *mlp = mdlp->item[0]->messages; + + fp = f; + real_file_name = real_filename; + logical_file_name = xstrdup (logical_filename); + line_number = 1; + char_in_line = 0; + + last_comment_line = -1; + last_non_comment_line = -1; + + flag_context_list_table = flag_table; + + /* Eat tokens until eof is seen. When extract_parenthesized returns + due to an unbalanced closing parenthesis, just restart it. */ + while (!extract_parenthesized (mlp, null_context, null_context_list_iterator, + false)) + ; fp = NULL; real_file_name = NULL; diff --git a/gettext-tools/src/x-ycp.h b/gettext-tools/src/x-ycp.h index e092452fd..f188342a0 100644 --- a/gettext-tools/src/x-ycp.h +++ b/gettext-tools/src/x-ycp.h @@ -21,9 +21,13 @@ { "ycp", "YCP" }, \ #define SCANNERS_YCP \ - { "YCP", extract_ycp, &formatstring_ycp, NULL }, \ + { "YCP", extract_ycp, \ + &flag_table_ycp, &formatstring_ycp, NULL }, \ /* Scan an YCP file and add its translatable strings to mdlp. */ extern void extract_ycp (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); + +extern void init_flag_table_ycp (void); diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c index 1b83d0ed1..47717e777 100644 --- a/gettext-tools/src/xgettext.c +++ b/gettext-tools/src/xgettext.c @@ -19,6 +19,7 @@ #ifdef HAVE_CONFIG_H # include #endif +#include #include #include @@ -135,6 +136,21 @@ static input_syntax_ty output_syntax = syntax_po; /* If nonzero omit header with information about this run. */ int xgettext_omit_header; +/* Table of flag_context_list_ty tables. */ +static flag_context_list_table_ty flag_table_c; +static flag_context_list_table_ty flag_table_gcc_internal; +static flag_context_list_table_ty flag_table_sh; +static flag_context_list_table_ty flag_table_python; +static flag_context_list_table_ty flag_table_lisp; +static flag_context_list_table_ty flag_table_elisp; +static flag_context_list_table_ty flag_table_librep; +static flag_context_list_table_ty flag_table_java; +static flag_context_list_table_ty flag_table_awk; +static flag_context_list_table_ty flag_table_ycp; +static flag_context_list_table_ty flag_table_tcl; +static flag_context_list_table_ty flag_table_perl; +static flag_context_list_table_ty flag_table_php; + /* Canonicalized encoding name for all input files. */ const char *xgettext_global_source_encoding; @@ -167,6 +183,7 @@ static const struct option long_options[] = { "exclude-file", required_argument, NULL, 'x' }, { "extract-all", no_argument, NULL, 'a' }, { "files-from", required_argument, NULL, 'f' }, + { "flag", required_argument, NULL, CHAR_MAX + 7 }, { "force-po", no_argument, &force_po, 1 }, { "foreign-user", no_argument, NULL, CHAR_MAX + 2 }, { "from-code", required_argument, NULL, CHAR_MAX + 3 }, @@ -202,8 +219,18 @@ static const struct option long_options[] = in which to add the messages. */ typedef void (*extractor_func) (FILE *fp, const char *real_filename, const char *logical_filename, + flag_context_list_table_ty *flag_table, msgdomain_list_ty *mdlp); +typedef struct extractor_ty extractor_ty; +struct extractor_ty +{ + extractor_func func; + flag_context_list_table_ty *flag_table; + struct formatstring_parser *formatstring_parser1; + struct formatstring_parser *formatstring_parser2; +}; + /* Forward declaration of local functions. */ static void usage (int status) @@ -212,11 +239,11 @@ static void usage (int status) #endif ; static void read_exclusion_file (char *file_name); -static void extract_from_file (const char *file_name, extractor_func extractor, +static void extract_from_file (const char *file_name, extractor_ty extractor, msgdomain_list_ty *mdlp); static message_ty *construct_header (void); static void finalize_header (msgdomain_list_ty *mdlp); -static extractor_func language_to_extractor (const char *name); +static extractor_ty language_to_extractor (const char *name); static const char *extension_to_language (const char *extension); @@ -235,7 +262,7 @@ main (int argc, char *argv[]) const char *files_from = NULL; string_list_ty *file_list; char *output_file = NULL; - extractor_func extractor = NULL; + extractor_ty extractor = { NULL, NULL, NULL, NULL }; /* Set program name for messages. */ set_program_name (argv[0]); @@ -256,6 +283,19 @@ main (int argc, char *argv[]) /* Set initial value of variables. */ default_domain = MESSAGE_DOMAIN_DEFAULT; xgettext_global_source_encoding = po_charset_ascii; + init_flag_table_c (); + init_flag_table_gcc_internal (); + init_flag_table_sh (); + init_flag_table_python (); + init_flag_table_lisp (); + init_flag_table_elisp (); + init_flag_table_librep (); + init_flag_table_java (); + init_flag_table_awk (); + init_flag_table_ycp (); + init_flag_table_tcl (); + init_flag_table_perl (); + init_flag_table_php (); while ((optchar = getopt_long (argc, argv, "ac::Cd:D:eEf:Fhijk::l:L:m::M::no:p:sTVw:x:", @@ -421,6 +461,9 @@ main (int argc, char *argv[]) message_print_syntax_properties (); output_syntax = syntax_properties; break; + case CHAR_MAX + 7: /* --flag */ + xgettext_record_flag (optarg); + break; default: usage (EXIT_FAILURE); /* NOTREACHED */ @@ -550,8 +593,9 @@ This version was built without iconv()."), /* Temporarily reset the directory list to empty, because file_name is an output file and therefore should not be searched for. */ void *saved_directory_list = dir_list_save_reset (); + extractor_ty po_extractor = { extract_po, NULL, NULL, NULL }; - extract_from_file (file_name, extract_po, mdlp); + extract_from_file (file_name, po_extractor, mdlp); dir_list_restore (saved_directory_list); } @@ -560,11 +604,11 @@ This version was built without iconv()."), for (cnt = 0; cnt < file_list->nitems; ++cnt) { const char *fname; - extractor_func this_file_extractor; + extractor_ty this_file_extractor; fname = file_list->item[cnt]; - if (extractor) + if (extractor.func) this_file_extractor = extractor; else { @@ -699,14 +743,31 @@ Operation mode:\n")); preceding keyword lines) in output file\n")); printf ("\n"); printf (_("\ -Language=C/C++ specific options:\n")); +Language specific options:\n")); printf (_("\ -a, --extract-all extract all strings\n")); + printf (_("\ + (only languages C, C++, ObjectiveC, Shell,\n\ + Python, Lisp, EmacsLisp, librep, Java, awk,\n\ + Tcl, Perl, PHP, GCC-source, Glade)\n")); printf (_("\ -k, --keyword[=WORD] additional keyword to be looked for (without\n\ WORD means not to use default keywords)\n")); + printf (_("\ + (only languages C, C++, ObjectiveC, Shell,\n\ + Python, Lisp, EmacsLisp, librep, Java, awk,\n\ + Tcl, Perl, PHP, GCC-source, Glade)\n")); + printf (_("\ + --flag=WORD:ARG:FLAG additional flag for strings inside the argument\n\ + number ARG of keyword WORD\n")); + printf (_("\ + (only languages C, C++, ObjectiveC, Shell,\n\ + Python, Lisp, EmacsLisp, librep, Java, awk,\n\ + YCP, Tcl, Perl, PHP, GCC-source)\n")); printf (_("\ -T, --trigraphs understand ANSI C trigraphs for input\n")); + printf (_("\ + (only languages C, C++, ObjectiveC)\n")); printf (_("\ --debug more detailed formatstring recognition result\n")); printf ("\n"); @@ -903,6 +964,405 @@ split_keywordspec (const char *spec, } +/* Null context. */ +flag_context_ty null_context = { undecided, false, undecided, false }; + +/* Transparent context. */ +flag_context_ty passthrough_context = { undecided, true, undecided, true }; + + +flag_context_ty +inherited_context (flag_context_ty outer_context, + flag_context_ty modifier_context) +{ + flag_context_ty result = modifier_context; + + if (result.pass_format1) + { + result.is_format1 = outer_context.is_format1; + result.pass_format1 = false; + } + if (result.pass_format2) + { + result.is_format2 = outer_context.is_format2; + result.pass_format2 = false; + } + return result; +} + + +/* Null context list iterator. */ +flag_context_list_iterator_ty null_context_list_iterator = { 1, NULL }; + +/* Transparent context list iterator. */ +static flag_context_list_ty passthrough_context_circular_list = + { + 1, + { undecided, true, undecided, true }, + &passthrough_context_circular_list + }; +flag_context_list_iterator_ty passthrough_context_list_iterator = + { + 1, + &passthrough_context_circular_list + }; + + +flag_context_list_iterator_ty +flag_context_list_iterator (flag_context_list_ty *list) +{ + flag_context_list_iterator_ty result = { 1, list }; + + return result; +} + + +flag_context_ty +flag_context_list_iterator_advance (flag_context_list_iterator_ty *iter) +{ + if (iter->head == NULL) + return null_context; + if (iter->argnum == iter->head->argnum) + { + flag_context_ty result = iter->head->flags; + + /* Special casing of circular list. */ + if (iter->head != iter->head->next) + { + iter->head = iter->head->next; + iter->argnum++; + } + + return result; + } + else + { + iter->argnum++; + return null_context; + } +} + + +flag_context_list_ty * +flag_context_list_table_lookup (flag_context_list_table_ty *flag_table, + const void *key, size_t keylen) +{ + void *entry; + + if (flag_table->table != NULL + && find_entry (flag_table, key, keylen, &entry) == 0) + return (flag_context_list_ty *) entry; + else + return NULL; +} + + +void +xgettext_record_flag (const char *optionstring) +{ + /* Check the string has at least two colons. (Colons in the name are + allowed, needed for the Lisp and the Tcl backends.) */ + const char *colon1; + const char *colon2; + + for (colon2 = optionstring + strlen (optionstring); ; ) + { + if (colon2 == optionstring) + goto err; + colon2--; + if (*colon2 == ':') + break; + } + for (colon1 = colon2; ; ) + { + if (colon1 == optionstring) + goto err; + colon1--; + if (*colon1 == ':') + break; + } + { + const char *name_start = optionstring; + const char *name_end = colon1; + const char *argnum_start = colon1 + 1; + const char *argnum_end = colon2; + const char *flag = colon2 + 1; + int argnum; + + /* Check the parts' syntax. */ + if (name_end == name_start) + goto err; + if (argnum_end == argnum_start) + goto err; + { + char *endp; + argnum = strtol (argnum_start, &endp, 10); + if (endp != argnum_end) + goto err; + } + if (argnum <= 0) + goto err; + + /* Analyze the flag part. */ + { + bool pass; + + pass = false; + if (strlen (flag) >= 5 && memcmp (flag, "pass-", 5) == 0) + { + pass = true; + flag += 5; + } + + /* Unlike po_parse_comment_special(), we don't accept "fuzzy" or "wrap" + here - it has no sense. */ + if (strlen (flag) >= 7 + && memcmp (flag + strlen (flag) - 7, "-format", 7) == 0) + { + const char *p; + size_t n; + enum is_format value; + size_t type; + + p = flag; + n = strlen (flag) - 7; + + if (n >= 3 && memcmp (p, "no-", 3) == 0) + { + p += 3; + n -= 3; + value = no; + } + else if (n >= 9 && memcmp (p, "possible-", 9) == 0) + { + p += 9; + n -= 9; + value = possible; + } + else if (n >= 11 && memcmp (p, "impossible-", 11) == 0) + { + p += 11; + n -= 11; + value = impossible; + } + else + value = yes_according_to_context; + + for (type = 0; type < NFORMATS; type++) + if (strlen (format_language[type]) == n + && memcmp (format_language[type], p, n) == 0) + { + flag_context_list_table_ty *table; + unsigned int index; + + index = 0; + switch (type) + { + case format_c: + table = &flag_table_c; + break; + case format_sh: + table = &flag_table_sh; + break; + case format_python: + table = &flag_table_python; + break; + case format_lisp: + table = &flag_table_lisp; + break; + case format_elisp: + table = &flag_table_elisp; + break; + case format_librep: + table = &flag_table_librep; + break; + case format_smalltalk: + return; + case format_java: + table = &flag_table_java; + break; + case format_awk: + table = &flag_table_awk; + break; + case format_pascal: + return; + case format_ycp: + table = &flag_table_ycp; + break; + case format_tcl: + table = &flag_table_tcl; + break; + case format_perl: + table = &flag_table_perl; + break; + case format_perl_brace: + index = 1; + table = &flag_table_perl; + break; + case format_php: + table = &flag_table_php; + break; + case format_gcc_internal: + table = &flag_table_gcc_internal; + break; + default: + abort (); + } + + if (table == &flag_table_lisp) + { + /* Convert NAME to upper case. */ + size_t name_len = name_end - name_start; + char *name = (char *) alloca (name_len); + size_t i; + + for (i = 0; i < name_len; i++) + name[i] = (name_start[i] >= 'a' && name_start[i] <= 'z' + ? name_start[i] - 'a' + 'A' + : name_start[i]); + name_start = name; + name_end = name + name_len; + } + else if (table == &flag_table_tcl) + { + /* Remove redundant "::" prefix. */ + if (name_end - name_start > 2 + && name_start[0] == ':' && name_start[1] == ':') + name_start += 2; + } + + /* Insert the pair (VALUE, PASS) at INDEX in the element + numbered ARGNUM of the list corresponding to NAME in the + TABLE. */ + if (table->table == NULL) + init_hash (table, 100); + { + void *entry; + + if (find_entry (table, name_start, name_end - name_start, + &entry) != 0) + { + /* Create new hash table entry. */ + flag_context_list_ty *list = + (flag_context_list_ty *) + xmalloc (sizeof (flag_context_list_ty)); + list->argnum = argnum; + memset (&list->flags, '\0', sizeof (list->flags)); + switch (index) + { + case 0: + list->flags.is_format1 = value; + list->flags.pass_format1 = pass; + break; + case 1: + list->flags.is_format2 = value; + list->flags.pass_format2 = pass; + break; + default: + abort (); + } + list->next = NULL; + insert_entry (table, name_start, name_end - name_start, + list); + } + else + { + flag_context_list_ty *list = + (flag_context_list_ty *)entry; + flag_context_list_ty **lastp = NULL; + + while (list != NULL && list->argnum < argnum) + { + lastp = &list->next; + list = *lastp; + } + if (list != NULL && list->argnum == argnum) + { + /* Add this flag to the current argument number. */ + switch (index) + { + case 0: + list->flags.is_format1 = value; + list->flags.pass_format1 = pass; + break; + case 1: + list->flags.is_format2 = value; + list->flags.pass_format2 = pass; + break; + default: + abort (); + } + } + else if (lastp != NULL) + { + /* Add a new list entry for this argument number. */ + list = + (flag_context_list_ty *) + xmalloc (sizeof (flag_context_list_ty)); + list->argnum = argnum; + memset (&list->flags, '\0', sizeof (list->flags)); + switch (index) + { + case 0: + list->flags.is_format1 = value; + list->flags.pass_format1 = pass; + break; + case 1: + list->flags.is_format2 = value; + list->flags.pass_format2 = pass; + break; + default: + abort (); + } + list->next = *lastp; + *lastp = list; + } + else + { + /* Add a new list entry for this argument number, + at the beginning of the list. Since we don't + have an API for replacing the value of a key + in the hash table, we have to copy the first + list element. */ + flag_context_list_ty *copy = + (flag_context_list_ty *) + xmalloc (sizeof (flag_context_list_ty)); + *copy = *list; + + list->argnum = argnum; + memset (&list->flags, '\0', sizeof (list->flags)); + switch (index) + { + case 0: + list->flags.is_format1 = value; + list->flags.pass_format1 = pass; + break; + case 1: + list->flags.is_format2 = value; + list->flags.pass_format2 = pass; + break; + default: + abort (); + } + list->next = copy; + } + } + } + return; + } + /* If the flag is not among the valid values, the optionstring is + invalid. */ + } + } + } + +err: + error (EXIT_FAILURE, 0, _("\ +A --flag argument doesn't have the ::[pass-] syntax: %s"), + optionstring); +} + + static string_list_ty *comment; void @@ -994,8 +1454,14 @@ error while opening \"%s\" for reading"), new_name); } +/* Language dependent format string parser. + NULL if the language has no notion of format strings. */ +static struct formatstring_parser *current_formatstring_parser1; +static struct formatstring_parser *current_formatstring_parser2; + + static void -extract_from_file (const char *file_name, extractor_func extractor, +extract_from_file (const char *file_name, extractor_ty extractor, msgdomain_list_ty *mdlp) { char *logical_file_name; @@ -1009,7 +1475,10 @@ extract_from_file (const char *file_name, extractor_func extractor, xgettext_current_source_iconv = xgettext_global_source_iconv; #endif - extractor (fp, real_file_name, logical_file_name, mdlp); + current_formatstring_parser1 = extractor.formatstring_parser1; + current_formatstring_parser2 = extractor.formatstring_parser2; + extractor.func (fp, real_file_name, logical_file_name, extractor.flag_table, + mdlp); if (fp != stdin) fclose (fp); @@ -1019,12 +1488,6 @@ extract_from_file (const char *file_name, extractor_func extractor, -/* Language dependent format string parser. - NULL if the language has no notion of format strings. */ -static struct formatstring_parser *current_formatstring_parser1; -static struct formatstring_parser *current_formatstring_parser2; - - #if !HAVE_ICONV /* If we don't have iconv(), the only supported values for xgettext_global_source_encoding and thus also for @@ -1070,8 +1533,67 @@ Please specify the source encoding through --from-code.\n"), pos->line_number); +/* Update the is_format[] flags depending on the information given in the + context. */ +static void +set_format_flags_from_context (enum is_format is_format[NFORMATS], + flag_context_ty context, const char *string, + lex_pos_ty *pos, const char *pretty_msgstr) +{ + size_t i; + + if (context.is_format1 != undecided || context.is_format2 != undecided) + for (i = 0; i < NFORMATS; i++) + { + if (is_format[i] == undecided) + { + if (formatstring_parsers[i] == current_formatstring_parser1 + && context.is_format1 != undecided) + is_format[i] = context.is_format1; + if (formatstring_parsers[i] == current_formatstring_parser2 + && context.is_format2 != undecided) + is_format[i] = context.is_format2; + } + if (possible_format_p (is_format[i])) + { + struct formatstring_parser *parser = formatstring_parsers[i]; + char *invalid_reason = NULL; + void *descr = parser->parse (string, &invalid_reason); + + if (descr != NULL) + parser->free (descr); + else + { + /* The string is not a valid format string. */ + if (is_format[i] != possible) + { + char buffer[21]; + + error_with_progname = false; + if (pos->line_number == (size_t)(-1)) + buffer[0] = '\0'; + else + sprintf (buffer, ":%ld", (long) pos->line_number); + multiline_warning (xasprintf (_("%s%s: warning: "), + pos->file_name, buffer), + xasprintf (is_format[i] == yes_according_to_context ? _("Although being used in a format string position, the %s is not a valid %s format string. Reason: %s\n") : _("Although declared as such, the %s is not a valid %s format string. Reason: %s\n"), + pretty_msgstr, + format_language_pretty[i], + invalid_reason)); + error_with_progname = true; + } + + is_format[i] = impossible; + free (invalid_reason); + } + } + } +} + + message_ty * -remember_a_message (message_list_ty *mlp, char *string, lex_pos_ty *pos) +remember_a_message (message_list_ty *mlp, char *string, + flag_context_ty context, lex_pos_ty *pos) { enum is_format is_format[NFORMATS]; enum is_wrap do_wrap; @@ -1148,6 +1670,10 @@ meta information, not the empty string.\n"))); message_list_append (mlp, mp); } + /* Determine whether the context specifies that the msgid is a format + string. */ + set_format_flags_from_context (is_format, context, mp->msgid, pos, "msgid"); + /* Ask the lexer for the comments it has seen. Only do this for the first instance, otherwise there could be problems; especially if the same comment appears before each. */ @@ -1261,7 +1787,8 @@ meta information, not the empty string.\n"))); void -remember_a_message_plural (message_ty *mp, char *string, lex_pos_ty *pos) +remember_a_message_plural (message_ty *mp, char *string, + flag_context_ty context, lex_pos_ty *pos) { char *msgid_plural; char *msgstr1; @@ -1298,6 +1825,11 @@ remember_a_message_plural (message_ty *mp, char *string, lex_pos_ty *pos) mp->msgstr = msgstr; mp->msgstr_len = mp->msgstr_len + msgstr1_len; + /* Determine whether the context specifies that the msgid_plural is a + format string. */ + set_format_flags_from_context (mp->is_format, context, mp->msgid_plural, + pos, "msgid_plural"); + /* If it is not already decided, through programmer comments or the msgid, whether the msgid is a format string, examine the msgid_plural. This is a heuristic. */ @@ -1471,7 +2003,7 @@ finalize_header (msgdomain_list_ty *mdlp) #define ENDOF(a) ((a) + SIZEOF(a)) -static extractor_func +static extractor_ty language_to_extractor (const char *name) { typedef struct table_ty table_ty; @@ -1479,6 +2011,7 @@ language_to_extractor (const char *name) { const char *name; extractor_func func; + flag_context_list_table_ty *flag_table; struct formatstring_parser *formatstring_parser1; struct formatstring_parser *formatstring_parser2; }; @@ -1511,16 +2044,22 @@ language_to_extractor (const char *name) for (tp = table; tp < ENDOF(table); ++tp) if (strcasecmp (name, tp->name) == 0) { - /* XXX Ugly side effect. */ - current_formatstring_parser1 = tp->formatstring_parser1; - current_formatstring_parser2 = tp->formatstring_parser2; + extractor_ty result = + { + tp->func, + tp->flag_table, + tp->formatstring_parser1, tp->formatstring_parser2 + }; - return tp->func; + return result; } error (EXIT_FAILURE, 0, _("language `%s' unknown"), name); /* NOTREACHED */ - return NULL; + { + extractor_ty result = { NULL, NULL, NULL, NULL }; + return result; + } } diff --git a/gettext-tools/src/xgettext.h b/gettext-tools/src/xgettext.h index b20940530..426af68dd 100644 --- a/gettext-tools/src/xgettext.h +++ b/gettext-tools/src/xgettext.h @@ -44,10 +44,69 @@ extern int xgettext_omit_header; extern bool substring_match; + /* Split keyword spec into keyword, argnum1, argnum2. */ extern void split_keywordspec (const char *spec, const char **endp, int *argnum1p, int *argnum2p); + +/* Context representing some flags. */ +typedef struct flag_context_ty flag_context_ty; +struct flag_context_ty +{ + /* Regarding the primary formatstring type. */ + enum is_format is_format1 : 3; + bool pass_format1 : 1; + /* Regarding the secondary formatstring type. */ + enum is_format is_format2 : 3; + bool pass_format2 : 1; +}; +/* Null context. */ +extern flag_context_ty null_context; +/* Transparent context. */ +extern flag_context_ty passthrough_context; +/* Compute an inherited context. + The outer_context is assumed to have all pass_format* flags = false. + The result will then also have all pass_format* flags = false. */ +extern flag_context_ty + inherited_context (flag_context_ty outer_context, + flag_context_ty modifier_context); + +/* Context representing some flags, for each possible argument number. + This is a linked list, sorted according to the argument number. */ +typedef struct flag_context_list_ty flag_context_list_ty; +struct flag_context_list_ty +{ + int argnum; /* current argument number, > 0 */ + flag_context_ty flags; /* flags for current argument */ + flag_context_list_ty *next; +}; + +/* Iterator through a flag_context_list_ty. */ +typedef struct flag_context_list_iterator_ty flag_context_list_iterator_ty; +struct flag_context_list_iterator_ty +{ + int argnum; /* current argument number, > 0 */ + const flag_context_list_ty* head; /* tail of list */ +}; +extern flag_context_list_iterator_ty null_context_list_iterator; +extern flag_context_list_iterator_ty passthrough_context_list_iterator; +extern flag_context_list_iterator_ty + flag_context_list_iterator (flag_context_list_ty *list); +extern flag_context_ty + flag_context_list_iterator_advance (flag_context_list_iterator_ty *iter); + +/* For nearly each backend, we have a separate table mapping a keyword to + a flag_context_list_ty *. */ +typedef hash_table /* char[] -> flag_context_list_ty * */ + flag_context_list_table_ty; +extern flag_context_list_ty * + flag_context_list_table_lookup (flag_context_list_table_ty *flag_table, + const void *key, size_t keylen); +/* Record a flag in the appropriate backend's table. */ +extern void xgettext_record_flag (const char *optionstring); + + /* Canonicalized encoding name for all input files. */ extern const char *xgettext_global_source_encoding; @@ -74,27 +133,34 @@ extern char *from_current_source_encoding (const char *string, const char *file_name, size_t line_number); + /* List of messages whose msgids must not be extracted, or NULL. Used by remember_a_message(). */ extern message_list_ty *exclude; + /* Comment handling: There is a list of automatic comments that may be appended to the next message. Used by remember_a_message(). */ extern void xgettext_comment_add (const char *str); extern const char *xgettext_comment (size_t n); extern void xgettext_comment_reset (void); + /* Add a message to the list of extracted messages. string must be malloc()ed string; its ownership is passed to the callee. pos->file_name must be allocated with indefinite extent. */ extern message_ty *remember_a_message (message_list_ty *mlp, - char *string, lex_pos_ty *pos); + char *string, + flag_context_ty context, + lex_pos_ty *pos); /* Add an msgid_plural to a message previously returned by remember_a_message. string must be malloc()ed string; its ownership is passed to the callee. pos->file_name must be allocated with indefinite extent. */ extern void remember_a_message_plural (message_ty *mp, - char *string, lex_pos_ty *pos); + char *string, + flag_context_ty context, + lex_pos_ty *pos); #ifdef __cplusplus diff --git a/gettext-tools/tests/ChangeLog b/gettext-tools/tests/ChangeLog index 5f20369e5..8e1f52a1a 100644 --- a/gettext-tools/tests/ChangeLog +++ b/gettext-tools/tests/ChangeLog @@ -1,3 +1,18 @@ +2003-10-05 Bruno Haible + + * xgettext-4: Update expected test result. + * xgettext-5: Likewise. + * xgettext-6: Likewise. + * xgettext-8: Likewise. + * xgettext-24: Likewise. + * xgettext-26: Pass --flag options to xgettext. Update expected test + result. + * lang-perl-1: Pass --flag options to xgettext. + * lang-perl-2: Likewise. + + * xgettext-31: New file. + * Makefile.am (TESTS): Add it. + 2003-10-04 Bruno Haible * xgettext-11: Test details of the new Java backend, instead of the diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am index 8d8ab0620..fa7e3986d 100644 --- a/gettext-tools/tests/Makefile.am +++ b/gettext-tools/tests/Makefile.am @@ -50,7 +50,7 @@ TESTS = gettext-1 gettext-2 \ xgettext-13 xgettext-14 xgettext-15 xgettext-16 xgettext-17 \ xgettext-18 xgettext-19 xgettext-20 xgettext-21 xgettext-22 \ xgettext-23 xgettext-24 xgettext-25 xgettext-26 xgettext-27 \ - xgettext-28 xgettext-29 xgettext-30 \ + xgettext-28 xgettext-29 xgettext-30 xgettext-31 \ format-awk-1 format-awk-2 \ format-c-1 format-c-2 format-c-3 format-c-4 \ format-elisp-1 format-elisp-2 \ diff --git a/gettext-tools/tests/lang-perl-1 b/gettext-tools/tests/lang-perl-1 index 7adc35d5f..8bdbd0448 100755 --- a/gettext-tools/tests/lang-perl-1 +++ b/gettext-tools/tests/lang-perl-1 @@ -26,7 +26,9 @@ EOF tmpfiles="$tmpfiles prog.pot" : ${XGETTEXT=xgettext} -${XGETTEXT} -k__ -o prog.pot --omit-header --no-location program.pl +${XGETTEXT} \ + -k__ --flag=__:1:pass-perl-format --flag=__:1:pass-perl-brace-format \ + -o prog.pot --omit-header --no-location program.pl tmpfiles="$tmpfiles prog.ok" cat < prog.ok @@ -55,7 +57,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n > 1);\n" msgid "'Your command, please?', asked the waiter." -msgstr "«Votre commande, s'il vous plait», dit le garçon." +msgstr "Votre commande, s'il vous plait, dit le gar�n." # Les gateaux allemands sont les meilleurs du monde. #, perl-format @@ -87,7 +89,7 @@ ${MSGFMT} -o fr/LC_MESSAGES/prog.mo fr.po tmpfiles="$tmpfiles prog.ok prog.out" : ${DIFF=diff} cat <<\EOF > prog.ok -«Votre commande, s'il vous plait», dit le garçon. +Votre commande, s'il vous plait, dit le gar�n. 2 morceaux de gateau EUR remplace FF. EOF diff --git a/gettext-tools/tests/lang-perl-2 b/gettext-tools/tests/lang-perl-2 index 845b5a0b0..f9868b20e 100755 --- a/gettext-tools/tests/lang-perl-2 +++ b/gettext-tools/tests/lang-perl-2 @@ -22,7 +22,12 @@ EOF tmpfiles="$tmpfiles prog.pot" : ${XGETTEXT=xgettext} -${XGETTEXT} -k__ -k__n:1,2 -k__x -o prog.pot --omit-header --no-location program.pl +${XGETTEXT} \ + -k__ --flag=__:1:pass-perl-format --flag=__:1:pass-perl-brace-format \ + -k__n:1,2 --flag=__n:1:pass-perl-format --flag=__n:1:pass-perl-brace-format \ + --flag=__n:2:pass-perl-format --flag=__n:2:pass-perl-brace-format \ + -k__x --flag=__x:1:perl-brace-format \ + -o prog.pot --omit-header --no-location program.pl tmpfiles="$tmpfiles prog.ok" cat < prog.ok diff --git a/gettext-tools/tests/xgettext-24 b/gettext-tools/tests/xgettext-24 index eb82a66e5..8ebb2b273 100755 --- a/gettext-tools/tests/xgettext-24 +++ b/gettext-tools/tests/xgettext-24 @@ -65,6 +65,7 @@ msgid "hello" msgstr "Again some text for fuzzy" #: xg-test24.c:6 +#, c-format msgid "Hello, world." msgstr "" diff --git a/gettext-tools/tests/xgettext-26 b/gettext-tools/tests/xgettext-26 index f17b5fcdd..8491be414 100755 --- a/gettext-tools/tests/xgettext-26 +++ b/gettext-tools/tests/xgettext-26 @@ -150,7 +150,11 @@ EOF tmpfiles="$tmpfiles xg-test26.po" : ${XGETTEXT=xgettext} -${XGETTEXT} --omit-header -n -k_ -k%__ -k\$__ xg-test26.pl -d xg-test26 +${XGETTEXT} --omit-header -n \ + -k_ --flag=_:1:pass-perl-format --flag=_:1:pass-perl-brace-format \ + -k%__ --flag=%__:1:pass-perl-format --flag=%__:1:pass-perl-brace-format \ + -k\$__ --flag=\$__:1:pass-perl-format --flag=\$__:1:pass-perl-brace-format \ + xg-test26.pl -d xg-test26 test $? = 0 || { rm -fr $tmpfiles; exit 1; } tmpfiles="$tmpfiles xg-test26.ok" @@ -179,6 +183,7 @@ msgstr[0] "" msgstr[1] "" #: xg-test26.pl:22 +#, perl-format msgid "Singular\n" msgid_plural "Plural\n" msgstr[0] "" diff --git a/gettext-tools/tests/xgettext-31 b/gettext-tools/tests/xgettext-31 new file mode 100755 index 000000000..0b6f11b0f --- /dev/null +++ b/gettext-tools/tests/xgettext-31 @@ -0,0 +1,259 @@ +#!/bin/sh + +# Test of position dependent recognition of format strings. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles xg-test31.c" +cat <<\EOF > xg-test31.c +fprintf (fp, gettext ("c-format positive1"), gettext ("c-format negative1")); +printk (gettext ("c-format negative2")); +EOF + +tmpfiles="$tmpfiles xg-test31.sh" +cat <<\EOF > xg-test31.sh +eval_gettext "sh-format positive1" "`gettext \"sh-format negative1\"`" +echo "`gettext \"sh-format negative2\"`" +EOF + +tmpfiles="$tmpfiles xg-test31.py" +cat <<\EOF > xg-test31.py +my_printf (gettext ("python-format positive1"), + gettext ("python-format negative1")); +printk (gettext ("python-format negative2")); +EOF + +tmpfiles="$tmpfiles xg-test31.lisp" +cat <<\EOF > xg-test31.lisp +(format t (gettext "lisp-format positive1") (gettext "lisp-format negative1")) +(prin1 (gettext "lisp-format negative2")) +EOF + +tmpfiles="$tmpfiles xg-test31.el" +cat <<\EOF > xg-test31.el +(format (_ "elisp-format positive1") (_ "elisp-format negative1")) +(printk (_ "elisp-format negative2")) +EOF + +tmpfiles="$tmpfiles xg-test31.jl" +cat <<\EOF > xg-test31.jl +(format stream (_ "librep-format positive1") (_ "librep-format negative1")) +(printk (_ "librep-format negative2")) +EOF + +tmpfiles="$tmpfiles xg-test31.java" +cat <<\EOF > xg-test31.java +MessageFormat.format(gettext("java-format positive1"), + gettext("java-format negative1")); +System.err.println(gettext("java-format negative2")); +EOF + +tmpfiles="$tmpfiles xg-test31.awk" +cat <<\EOF > xg-test31.awk +printf dcgettext ("awk-format positive1"), dcgettext ("awk-format negative1"); +printf (dcgettext ("awk-format positive2"), dcgettext ("awk-format negative2")); +printf dcgettext ("awk-format positive3"); dcgettext ("awk-format negative3"); +printk dcgettext ("awk-format negative4"); +EOF + +tmpfiles="$tmpfiles xg-test31.ycp" +cat <<\EOF > xg-test31.ycp +sformat (_("ycp-format positive1"), _("ycp-format negative1")); +printk (_("ycp-format negative2")); +EOF + +tmpfiles="$tmpfiles xg-test31.tcl" +cat <<\EOF > xg-test31.tcl +[format [::msgcat::mc "tcl-format positive1"] + [::msgcat::mc "tcl-format negative1"]] +[print [::msgcat::mc "tcl-format negative2"]] +EOF + +tmpfiles="$tmpfiles xg-test31.pl" +cat <<\EOF > xg-test31.pl +printf gettext "perl-format positive1", gettext ("perl-format negative1"); +gettext ("perl-format negative2"); +printf gettext "perl-format positive2"; gettext ("perl-format negative3"); +gettext ("perl-format negative4"); +print sprintf gettext "perl-format positive3"; +print kprintf gettext "perl-format negative5"; +EOF + +tmpfiles="$tmpfiles xg-test31.php" +cat <<\EOF > xg-test31.php + xg-test31.ok +#, c-format +msgid "c-format positive1" +msgstr "" + +msgid "c-format negative1" +msgstr "" + +msgid "c-format negative2" +msgstr "" + +#, sh-format +msgid "sh-format positive1" +msgstr "" + +msgid "sh-format negative1" +msgstr "" + +msgid "sh-format negative2" +msgstr "" + +#, python-format +msgid "python-format positive1" +msgstr "" + +msgid "python-format negative1" +msgstr "" + +msgid "python-format negative2" +msgstr "" + +#, lisp-format +msgid "lisp-format positive1" +msgstr "" + +msgid "lisp-format negative1" +msgstr "" + +msgid "lisp-format negative2" +msgstr "" + +#, elisp-format +msgid "elisp-format positive1" +msgstr "" + +msgid "elisp-format negative1" +msgstr "" + +msgid "elisp-format negative2" +msgstr "" + +#, librep-format +msgid "librep-format positive1" +msgstr "" + +msgid "librep-format negative1" +msgstr "" + +msgid "librep-format negative2" +msgstr "" + +#, java-format +msgid "java-format positive1" +msgstr "" + +msgid "java-format negative1" +msgstr "" + +msgid "java-format negative2" +msgstr "" + +#, awk-format +msgid "awk-format positive1" +msgstr "" + +msgid "awk-format negative1" +msgstr "" + +#, awk-format +msgid "awk-format positive2" +msgstr "" + +msgid "awk-format negative2" +msgstr "" + +#, awk-format +msgid "awk-format positive3" +msgstr "" + +msgid "awk-format negative3" +msgstr "" + +msgid "awk-format negative4" +msgstr "" + +#, ycp-format +msgid "ycp-format positive1" +msgstr "" + +msgid "ycp-format negative1" +msgstr "" + +msgid "ycp-format negative2" +msgstr "" + +#, tcl-format +msgid "tcl-format positive1" +msgstr "" + +msgid "tcl-format negative1" +msgstr "" + +msgid "tcl-format negative2" +msgstr "" + +#, perl-format +msgid "perl-format positive1" +msgstr "" + +msgid "perl-format negative1" +msgstr "" + +msgid "perl-format negative2" +msgstr "" + +#, perl-format +msgid "perl-format positive2" +msgstr "" + +msgid "perl-format negative3" +msgstr "" + +msgid "perl-format negative4" +msgstr "" + +#, perl-format +msgid "perl-format positive3" +msgstr "" + +msgid "perl-format negative5" +msgstr "" + +#, php-format +msgid "php-format positive1" +msgstr "" + +msgid "php-format negative1" +msgstr "" + +msgid "php-format negative2" +msgstr "" +EOF + +: ${DIFF=diff} +${DIFF} xg-test31.ok xg-test31.po +result=$? + +rm -fr $tmpfiles + +exit $result diff --git a/gettext-tools/tests/xgettext-4 b/gettext-tools/tests/xgettext-4 index 80cfb2ca4..42fe84164 100755 --- a/gettext-tools/tests/xgettext-4 +++ b/gettext-tools/tests/xgettext-4 @@ -21,6 +21,7 @@ test $? = 0 || { rm -fr $tmpfiles; exit 1; } tmpfiles="$tmpfiles xg-test4.ok" cat < xg-test4.ok #: bozo:42 +#, c-format msgid "Hello, World!\n" msgstr "" diff --git a/gettext-tools/tests/xgettext-5 b/gettext-tools/tests/xgettext-5 index 36f6dc64c..ce100a4c5 100755 --- a/gettext-tools/tests/xgettext-5 +++ b/gettext-tools/tests/xgettext-5 @@ -17,6 +17,7 @@ test $? = 0 || { rm -fr $tmpfiles; exit 1; } tmpfiles="$tmpfiles xg-test5.ok" cat < xg-test5.ok +#, c-format msgid "Hello, World!\n" msgstr "" EOF diff --git a/gettext-tools/tests/xgettext-6 b/gettext-tools/tests/xgettext-6 index 16a7a3a3f..c9a411935 100755 --- a/gettext-tools/tests/xgettext-6 +++ b/gettext-tools/tests/xgettext-6 @@ -19,6 +19,7 @@ tmpfiles="$tmpfiles xg-test6.ok" cat < xg-test6.ok #. puke #. barf +#, c-format msgid "Hello, World!\n" msgstr "" EOF diff --git a/gettext-tools/tests/xgettext-8 b/gettext-tools/tests/xgettext-8 index 837832a1c..0c784ffc5 100755 --- a/gettext-tools/tests/xgettext-8 +++ b/gettext-tools/tests/xgettext-8 @@ -68,6 +68,7 @@ msgid "hello" msgstr "Again some text for fuzzy" #: xg-test8.c:6 +#, c-format msgid "Hello, world." msgstr ""