- 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.
+2003-10-05 Bruno Haible <bruno@clisp.org>
+
+ * Makevars (XGETTEXT_OPTIONS): Add --flag options.
+
2003-09-14 Bruno Haible <bruno@clisp.org>
* POTFILES.in: Add ../gettext-tools/lib/closeout.c, src/envsubst.c.
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
+2003-10-05 Bruno Haible <bruno@clisp.org>
+
+ * 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 <bruno@clisp.org>
* gettext.texi: Update menus.
@end table
-@subsection Language=C/C++ specific options
+@subsection Language specific options
@table @samp
@item -a
@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}
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
@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}
+2003-10-05 Bruno Haible <bruno@clisp.org>
+
+ * Makevars (XGETTEXT_OPTIONS): Add --flag options.
+
2003-09-14 Bruno Haible <bruno@clisp.org>
* POTFILES.in: Add src/format-sh.c, src/x-sh.c, lib/closeout.c.
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
+2003-10-05 Bruno Haible <bruno@clisp.org>
+
+ * 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 <bruno@clisp.org>
* x-awk.c (extract_parenthesized): Remove optimization of the
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;
}
undecided,
yes,
no,
+ yes_according_to_context,
possible,
impossible
};
/* 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;
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;
if (c == '\n')
{
if (signal_eilseq)
- /* xgettext: c-format */
po_gram_error (_("\
incomplete multibyte sequence at end of line"));
bytes = mbf->bufcount - 1;
/* FIXME: \u and \U are not handled. */
}
lex_ungetc (mbc);
- /* xgettext: c-format */
po_gram_error (_("invalid control sequence"));
return ' ';
}
break;
}
/* FALLTHROUGH */
+ case yes_according_to_context:
case yes:
sprintf (result, " %s-format", lang);
break;
#include <string.h>
#include "message.h"
-#include "x-awk.h"
#include "xgettext.h"
+#include "x-awk.h"
#include "error.h"
#include "error-progname.h"
#include "xmalloc.h"
}
}
+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. ======================== */
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;
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':
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;
/* ========================= 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
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. */
/* 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;
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:
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;
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;
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)
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;
}
{
/* Seen an msgid_plural. */
remember_a_message_plural (plural_mp, token.string,
- &pos);
+ inner_context, &pos);
plural_mp = NULL;
}
}
free (token.string);
}
}
+ next_is_argument = false;
+ next_context_iter = null_context_list_iterator;
state = 0;
continue;
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;
return true;
case token_type_other:
+ next_is_argument = false;
+ next_context_iter = null_context_list_iterator;
state = 0;
continue;
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;
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;
{ "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);
#include <string.h>
#include "message.h"
-#include "x-c.h"
#include "xgettext.h"
+#include "x-c.h"
#include "error.h"
#include "error-progname.h"
#include "xmalloc.h"
}
}
+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");
+ /* <stdio.h> */
+ 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. */
+ /* <stdio.h> */
+ 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");
+ /* <error.h> */
+ xgettext_record_flag ("error:3:c-format");
+ xgettext_record_flag ("error_at_line:5:c-format");
+ /* <argp.h> */
+ 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. ======================== */
{
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;
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
}
else
tp->type = xgettext_token_type_symbol;
- free (token.string);
+ tp->string = token.string;
return;
case token_type_lparen:
default:
last_non_comment_line = newline_count;
- tp->type = xgettext_token_type_symbol;
+ tp->type = xgettext_token_type_other;
return;
}
}
/* ========================= 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
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. */
/* 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;
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;
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)
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;
}
{
/* 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;
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;
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. */
{ "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);
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);
#include <string.h>
#include "message.h"
-#include "x-elisp.h"
#include "xgettext.h"
+#include "x-elisp.h"
#include "error.h"
#include "xmalloc.h"
#include "exit.h"
}
}
+void
+init_flag_table_elisp ()
+{
+ xgettext_record_flag ("_:1:pass-elisp-format");
+ xgettext_record_flag ("format:1:elisp-format");
+}
+
/* ======================== Reading of characters. ======================== */
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
'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 (;;)
{
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. */
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)
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
{
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;
}
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);
}
}
}
{
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)
{
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. */
{
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. */
{
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. */
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;
{
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)
/* 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);
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
{
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);
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);
}
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;
}
/* 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);
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;
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
{
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;
{ "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);
extern void x_elisp_extract_all (void);
extern void x_elisp_keyword (const char *name);
+
+extern void init_flag_table_elisp (void);
#endif
#include "message.h"
-#include "x-glade.h"
#include "xgettext.h"
+#include "x-glade.h"
#include "error.h"
#include "xerror.h"
#include "basename.h"
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;
}
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;
}
}
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
{ "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);
#include <string.h>
#include "message.h"
-#include "x-java.h"
#include "xgettext.h"
+#include "x-java.h"
#include "error.h"
#include "xmalloc.h"
#include "exit.h"
}
}
+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. ======================== */
/* ========================= 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
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. */
/* 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;
char *sum = token.string;
size_t sum_len = strlen (sum);
const char *dottedname;
+ flag_context_list_ty *context_list;
for (;;)
{
}
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;
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;
logical_file_name, token.line_number);
error_with_progname = true;
}
+ next_context_iter = null_context_list_iterator;
state = 0;
continue;
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;
{
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;
}
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)
/* 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;
}
}
}
drop_reference (token.comment);
+ next_context_iter = null_context_list_iterator;
state = 0;
continue;
case token_type_number:
case token_type_plus:
case token_type_other:
+ next_context_iter = null_context_list_iterator;
state = 0;
continue;
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;
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;
{ "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);
#include <string.h>
#include "message.h"
-#include "x-librep.h"
#include "xgettext.h"
+#include "x-librep.h"
#include "error.h"
#include "xmalloc.h"
#include "exit.h"
}
}
+void
+init_flag_table_librep ()
+{
+ xgettext_record_flag ("_:1:pass-librep-format");
+ xgettext_record_flag ("format:2:librep-format");
+}
+
/* ======================== Reading of characters. ======================== */
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)
/* Read the next object. */
static void
-read_object (struct object *op)
+read_object (struct object *op, flag_context_ty outer_context)
{
for (;;)
{
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. */
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)
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
{
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;
}
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);
}
}
}
{
struct object inner;
- read_object (&inner);
+ read_object (&inner, null_context);
/* Recognize end of vector. */
if (inner.type == t_close)
{
struct object inner;
- read_object (&inner);
+ read_object (&inner, null_context);
/* Dots and EOF are not allowed here. But be tolerant. */
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;
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);
{
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);
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;
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
{
struct object toplevel_object;
- read_object (&toplevel_object);
+ read_object (&toplevel_object, null_context);
if (toplevel_object.type == t_eof)
break;
{ "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);
extern void x_librep_extract_all (void);
extern void x_librep_keyword (const char *name);
+
+extern void init_flag_table_librep (void);
#include <string.h>
#include "message.h"
-#include "x-lisp.h"
#include "xgettext.h"
+#include "x-lisp.h"
#include "error.h"
#include "xmalloc.h"
#include "exit.h"
}
}
+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. ======================== */
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 (;;)
{
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. */
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)
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
{
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;
}
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);
}
}
}
{
struct object inner;
- read_object (&inner);
+ read_object (&inner, null_context);
/* Dots and EOF are not allowed here. But be tolerant. */
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;
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);
/* 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);
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;
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
{
struct object toplevel_object;
- read_object (&toplevel_object);
+ read_object (&toplevel_object, null_context);
if (toplevel_object.type == t_eof)
break;
{ "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);
extern void x_lisp_extract_all (void);
extern void x_lisp_keyword (const char *name);
+
+extern void init_flag_table_lisp (void);
#include <string.h>
#include "message.h"
-#include "x-perl.h"
#include "xgettext.h"
+#include "x-perl.h"
#include "error.h"
#include "error-progname.h"
#include "xmalloc.h"
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. ======================== */
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
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;
}
/* 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
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);
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;
}
}
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 '[':
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 '-':
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
{
* 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;
{
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;
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;
state = wait_lbrace;
break;
default:
+ context = null_context;
state = initial;
break;
}
state = wait_quote;
break;
default:
+ context = null_context;
state = initial;
break;
}
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;
buffer[bufpos++] = string++[0];
}
else
- state = initial;
+ {
+ context = null_context;
+ state = initial;
+ }
break;
default:
buffer[bufpos++] = c;
buffer[bufpos++] = string++[0];
}
else
- state = initial;
+ {
+ context = null_context;
+ state = initial;
+ }
break;
default:
buffer[bufpos++] = c;
}
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)
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;
}
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:
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;
/* 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;
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:
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:
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:
#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:
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:
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:
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)
{
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;
}
plural_mp = NULL;
}
+ next_is_argument = false;
+ next_context_iter = null_context_list_iterator;
break;
case token_type_eof:
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:
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;
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:
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;
/* 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:
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:
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;
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;
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:
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;
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;
last_comment_line = -1;
last_non_comment_line = -1;
+ flag_context_list_table = flag_table;
+
init_keywords ();
token_stack.items = NULL;
/* 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;
{ "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);
#include <stdlib.h>
#include "message.h"
-#include "x-php.h"
#include "xgettext.h"
+#include "x-php.h"
#include "error.h"
#include "xmalloc.h"
#include "exit.h"
}
}
+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. ======================== */
/* ========================= 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
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. */
/* 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;
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;
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;
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)
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;
}
{
/* Seen an msgid_plural. */
remember_a_message_plural (plural_mp, token.string,
- &pos);
+ inner_context, &pos);
plural_mp = NULL;
}
}
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;
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;
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. */
/* 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. */
{ "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);
#include <string.h>
#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"
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);
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);
{ "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);
{ "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);
#include <string.h>
#include "message.h"
-#include "x-python.h"
#include "xgettext.h"
+#include "x-python.h"
#include "error.h"
#include "error-progname.h"
#include "xmalloc.h"
}
}
+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. ======================== */
/* ========================= 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
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. */
/* 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;
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;
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;
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)
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;
}
{
/* Seen an msgid_plural. */
remember_a_message_plural (plural_mp, token.string,
- &pos);
+ inner_context, &pos);
plural_mp = NULL;
}
}
free (token.string);
}
}
+ next_context_iter = null_context_list_iterator;
state = 0;
continue;
return true;
case token_type_other:
+ next_context_iter = null_context_list_iterator;
state = 0;
continue;
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;
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;
{ "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);
#include <stddef.h>
#include "message.h"
-#include "x-rst.h"
#include "xgettext.h"
+#include "x-rst.h"
#include "error.h"
#include "error-progname.h"
#include "xmalloc.h"
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;
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)
{ "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);
#include <string.h>
#include "message.h"
-#include "x-sh.h"
#include "xgettext.h"
+#include "x-sh.h"
#include "error.h"
#include "xmalloc.h"
#include "exit.h"
}
}
+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. ======================== */
}
+/* 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);
'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;
{
/* Command substitution. */
phase2_ungetc (c3);
- read_command_list (')');
+ read_command_list (')', context);
}
}
else if (c2 == '\'' && !open_singlequote)
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;
/* Handle an opening backquote. */
saw_opening_backquote ();
- read_command_list (CLOSING_BACKQUOTE);
+ read_command_list (CLOSING_BACKQUOTE, context);
wp->type = t_other;
continue;
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
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. */
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
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);
}
}
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
{
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;
}
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;
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;
}
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;
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;
{ "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);
#include <stdlib.h>
#include "message.h"
-#include "x-smalltalk.h"
#include "xgettext.h"
+#include "x-smalltalk.h"
#include "error.h"
#include "xmalloc.h"
#include "exit.h"
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;
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;
}
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;
}
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;
}
{ "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);
#include <string.h>
#include "message.h"
-#include "x-tcl.h"
#include "xgettext.h"
+#include "x-tcl.h"
#include "error.h"
#include "xmalloc.h"
#include "exit.h"
}
}
+void
+init_flag_table_tcl ()
+{
+ xgettext_record_flag ("::msgcat::mc:1:pass-tcl-format");
+ xgettext_record_flag ("format:1:tcl-format");
+}
+
/* ======================== Reading of characters. ======================== */
}
+/* 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
};
/* 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;
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;
}
else if (c == '[')
{
- read_command_list (']');
+ read_command_list (']', context);
wp->type = t_other;
}
else if (c == '\\')
/* 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;
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);
if (c == '"')
{
- c = accumulate_word (wp, te_quote);
+ c = accumulate_word (wp, te_quote, context);
if (c != EOF && c != '"')
phase2_ungetc (c);
}
c = accumulate_word (wp,
looking_for == ']'
? te_space_separator_bracket
- : te_space_separator);
+ : te_space_separator,
+ context);
if (c != EOF)
phase2_ungetc (c);
}
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;
/* 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. */
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
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);
}
}
}
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;
}
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;
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;
{ "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);
extern void x_tcl_extract_all (void);
extern void x_tcl_keyword (const char *name);
+
+extern void init_flag_table_tcl (void);
#include <stdlib.h>
#include "message.h"
-#include "x-ycp.h"
#include "xgettext.h"
+#include "x-ycp.h"
#include "error.h"
#include "xmalloc.h"
#include "exit.h"
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. ======================== */
/* ========================= 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
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 (;;)
{
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:
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;
}
}
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;
{ "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);
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
+#include <alloca.h>
#include <ctype.h>
#include <errno.h>
/* 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;
{ "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 },
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)
#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);
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]);
/* 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:",
message_print_syntax_properties ();
output_syntax = syntax_properties;
break;
+ case CHAR_MAX + 7: /* --flag */
+ xgettext_record_flag (optarg);
+ break;
default:
usage (EXIT_FAILURE);
/* NOTREACHED */
/* 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);
}
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
{
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");
}
+/* 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 <keyword>:<argnum>:[pass-]<flag> syntax: %s"),
+ optionstring);
+}
+
+
static string_list_ty *comment;
void
}
+/* 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;
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);
-/* 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
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;
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. */
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;
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. */
#define ENDOF(a) ((a) + SIZEOF(a))
-static extractor_func
+static extractor_ty
language_to_extractor (const char *name)
{
typedef struct table_ty table_ty;
{
const char *name;
extractor_func func;
+ flag_context_list_table_ty *flag_table;
struct formatstring_parser *formatstring_parser1;
struct formatstring_parser *formatstring_parser2;
};
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;
+ }
}
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;
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
+2003-10-05 Bruno Haible <bruno@clisp.org>
+
+ * 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 <bruno@clisp.org>
* xgettext-11: Test details of the new Java backend, instead of the
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 \
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 <<EOF > prog.ok
"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
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
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 <<EOF > prog.ok
msgstr "Again some text for fuzzy"
#: xg-test24.c:6
+#, c-format
msgid "Hello, world."
msgstr ""
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"
msgstr[1] ""
#: xg-test26.pl:22
+#, perl-format
msgid "Singular\n"
msgid_plural "Plural\n"
msgstr[0] ""
--- /dev/null
+#!/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
+<? php
+printf (_ ("php-format positive1"), _ ("php-format negative1"));
+printk (_ ("php-format negative2"));
+EOF
+
+tmpfiles="$tmpfiles xg-test31.po"
+: ${XGETTEXT=xgettext}
+${XGETTEXT} --omit-header --no-location -d xg-test31 \
+ --flag=my_printf:1:python-format \
+ xg-test31.c xg-test31.sh xg-test31.py xg-test31.lisp xg-test31.el \
+ xg-test31.jl xg-test31.java xg-test31.awk xg-test31.ycp xg-test31.tcl \
+ xg-test31.pl xg-test31.php
+test $? = 0 || { rm -fr $tmpfiles; exit 1; }
+
+tmpfiles="$tmpfiles xg-test31.ok"
+cat <<\EOF > 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
tmpfiles="$tmpfiles xg-test4.ok"
cat <<EOF > xg-test4.ok
#: bozo:42
+#, c-format
msgid "Hello, World!\n"
msgstr ""
tmpfiles="$tmpfiles xg-test5.ok"
cat <<EOF > xg-test5.ok
+#, c-format
msgid "Hello, World!\n"
msgstr ""
EOF
cat <<EOF > xg-test6.ok
#. puke
#. barf
+#, c-format
msgid "Hello, World!\n"
msgstr ""
EOF
msgstr "Again some text for fuzzy"
#: xg-test8.c:6
+#, c-format
msgid "Hello, world."
msgstr ""