From: Bruno Haible Date: Thu, 23 Oct 2003 17:21:20 +0000 (+0000) Subject: Support Objective C specific format strings. X-Git-Tag: v0.13~192 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3857bca7b26196b239010f5d5e1a558d14b7b2a6;p=thirdparty%2Fgettext.git Support Objective C specific format strings. --- diff --git a/NEWS b/NEWS index 334e735ab..506f075ec 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ Version 0.12.2 - September 2003 * Programming languages support: - Shell: + xgettext now also supports shell scripts. It recognizes invocations of the programs 'gettext', 'ngettext', the functions 'eval_gettext', 'eval_ngettext', as well as the deprecated GNU bash builtin syntax $"...". @@ -12,12 +13,25 @@ Version 0.12.2 - September 2003 envsubst - substitutes environment variables in shell format strings. - Perl: + xgettext now also supports Perl. - PHP: + "xgettext --language=PHP" now supports the plural handling functions ngettext, dngettext, dcngettext (introduced in PHP 4.2.0). + - ObjectiveC: + + "xgettext --language=ObjectiveC" now supports the @"..." string syntax, + the NSLocalizedString function and the ObjectiveC specific format strings. + + All the tools that manipulate PO files can work with .strings files + as well, if given the --stringtable-input and/or --stringtable-output + option. To create a .strings file from a PO or POT file, use + "msgcat --stringtable-output". To create a PO or POT file from a + .strings file, use "xgettext". + - GCC-source: xgettext's --language option now supports the value "GCC-source". This diff --git a/gettext-tools/doc/ChangeLog b/gettext-tools/doc/ChangeLog index 5f869c3ce..d38e66691 100644 --- a/gettext-tools/doc/ChangeLog +++ b/gettext-tools/doc/ChangeLog @@ -1,3 +1,9 @@ +2003-10-13 Bruno Haible + + * gettext.texi (PO Files): Mention objc-format, sh-format, perl-format, + perl-brace-format. + (objc-format): New subsection. + 2003-10-12 Bruno Haible * msgattrib.texi: Document --stringtable-input and --stringtable-output diff --git a/gettext-tools/doc/gettext.texi b/gettext-tools/doc/gettext.texi index 01a4b909b..2ae21aa17 100644 --- a/gettext-tools/doc/gettext.texi +++ b/gettext-tools/doc/gettext.texi @@ -327,6 +327,7 @@ Other Programming Languages The Translator's View * c-format:: C Format Strings +* objc-format:: Objective C Format Strings * sh-format:: Shell Format Strings * python-format:: Python Format Strings * lisp-format:: Lisp Format Strings @@ -1166,6 +1167,18 @@ In case the @code{c-format} flag is given for a string the @code{msgfmt} does some more tests to check to validity of the translation. @xref{msgfmt Invocation}, @ref{c-format Flag} and @ref{c-format}. +@item objc-format +@kwindex objc-format@r{ flag} +@itemx no-objc-format +@kwindex no-objc-format@r{ flag} +Likewise for Objective C, see @ref{objc-format}. + +@item sh-format +@kwindex sh-format@r{ flag} +@itemx no-sh-format +@kwindex no-sh-format@r{ flag} +Likewise for Shell, see @ref{sh-format}. + @item python-format @kwindex python-format@r{ flag} @itemx no-python-format @@ -1226,6 +1239,18 @@ Likewise for YCP, see @ref{ycp-format}. @kwindex no-tcl-format@r{ flag} Likewise for Tcl, see @ref{tcl-format}. +@item perl-format +@kwindex perl-format@r{ flag} +@itemx no-perl-format +@kwindex no-perl-format@r{ flag} +Likewise for Perl, see @ref{perl-format}. + +@item perl-brace-format +@kwindex perl-brace-format@r{ flag} +@itemx no-perl-brace-format +@kwindex no-perl-brace-format@r{ flag} +Likewise for Perl brace, see @ref{perl-format}. + @item php-format @kwindex php-format@r{ flag} @itemx no-php-format @@ -7235,6 +7260,7 @@ strings. @menu * c-format:: C Format Strings +* objc-format:: Objective C Format Strings * sh-format:: Shell Format Strings * python-format:: Python Format Strings * lisp-format:: Lisp Format Strings @@ -7251,7 +7277,7 @@ strings. * gcc-internal-format:: GCC internal Format Strings @end menu -@node c-format, sh-format, Translators for other Languages, Translators for other Languages +@node c-format, objc-format, Translators for other Languages, Translators for other Languages @subsection C Format Strings C format strings are described in POSIX (IEEE P1003.1 2001), section @@ -7281,7 +7307,14 @@ on this reordering ability: On the few platforms where @code{printf()}, or @file{libintl.so} provides replacement functions, and GNU @code{} activates these replacement functions automatically. -@node sh-format, python-format, c-format, Translators for other Languages +@node objc-format, sh-format, c-format, Translators for other Languages +@subsection Objective C Format Strings + +Objective C format strings are like C format strings. They support an +additional format directive: "$@@", which when executed consumes an argument +of type @code{Object *}. + +@node sh-format, python-format, objc-format, Translators for other Languages @subsection Shell Format Strings Shell format strings, as supported by GNU gettext and the @samp{envsubst} diff --git a/gettext-tools/src/ChangeLog b/gettext-tools/src/ChangeLog index 2a104ea60..5bf5bf202 100644 --- a/gettext-tools/src/ChangeLog +++ b/gettext-tools/src/ChangeLog @@ -1,3 +1,48 @@ +2003-10-13 Bruno Haible + + Support and recognize Objective C specific format strings. + * message.h (enum format_type): New item format_objc. + (NFORMATS): Increment. + * message.c (format_language): Add an entry for format_objc. + (format_language_pretty): Likewise. + * format.h (formatstring_objc): New declaration. + * format-c.c (enum format_arg_type): New item FAT_OBJC_OBJECT. + (format_parse): Add objc_extensions argument. Handle %@ in ObjC mode. + (format_c_parse, format_objc_parse): New functions. + (formatstring_c): Use format_c_parse instead of format_parse. + (formatstring_objc): New variable. + (get_c99_format_directives): Update. + * format.c (formatstring_parsers): Add an entry for format_objc. + * write-mo.c (write_table): Look for system dependent strings also in + ObjectiveC format strings. + * x-c.h (SCANNERS_C): Use separate flag_table for ObjectiveC. + (x_objc_keyword, init_flag_table_objc): New declarations. + (x_c_any_keywords): Remove declaration. + * x-c.c (c_keywords): Renamed from keywords. + (objc_keywords): New variable. + (add_keyword): Renamed from x_c_keyword. Add keywords table argument. + (x_c_keyword, x_objc_keyword): New functions. + (x_c_any_keywords): Remove function. + (init_keywords): Also initialize ObjectiveC keyword table. + (init_flag_table_objc): New function. + (enum token_type_ty): New item token_type_colon. + (phase5_get): Recognize colon. + (enum xgettext_token_type_ty): New item xgettext_token_type_colon. + (x_c_lex): Use keywords table depending on objc_extensions. Handle + colon. + (extract_parenthesized): Change the context_iter and inner_context + after a keyword/symbol followed by a colon was seen. + * xgettext.c (flag_table_objc): New variable. + (main): Invoke init_flag_table_objc, x_objc_keyword. Watch out for + keywords arguments, instead of calling x_c_any_keywords(). + (flag_context_list_table_insert): New function, extracted from + xgettext_record_flag. + (xgettext_record_flag): Call it. For format_c, insert the flags also + in the flag_table_objc. Handle format_objc. + (remember_a_message): Don't add a heuristic c-format flag to an entry + that already carries objc-format. + (remember_a_message_plural): Likewise. + 2003-10-18 Bruno Haible Support for GNUstep .strings format. diff --git a/gettext-tools/src/format-c.c b/gettext-tools/src/format-c.c index 9452ddf85..86ae6a3ce 100644 --- a/gettext-tools/src/format-c.c +++ b/gettext-tools/src/format-c.c @@ -73,8 +73,9 @@ enum format_arg_type FAT_DOUBLE = 2, FAT_CHAR = 3, FAT_STRING = 4, - FAT_POINTER = 5, - FAT_COUNT_POINTER = 6, + FAT_OBJC_OBJECT = 5, + FAT_POINTER = 6, + FAT_COUNT_POINTER = 7, /* Flags */ FAT_UNSIGNED = 1 << 3, FAT_SIZE_SHORT = 1 << 4, @@ -176,7 +177,7 @@ numbered_arg_compare (const void *p1, const void *p2) xasprintf (_("In the directive number %u, the token after '<' is not the name of a format specifier macro. The valid macro names are listed in ISO C 99 section 7.8.1."), directive_number) static void * -format_parse (const char *format, char **invalid_reason) +format_parse (const char *format, bool objc_extensions, char **invalid_reason) { struct spec spec; unsigned int numbered_arg_count; @@ -622,6 +623,13 @@ format_parse (const char *format, char **invalid_reason) type = FAT_DOUBLE; type |= (size & FAT_SIZE_LONGLONG); break; + case '@': + if (objc_extensions) + { + type = FAT_OBJC_OBJECT; + break; + } + goto other; case 'p': type = FAT_POINTER; break; @@ -629,6 +637,7 @@ format_parse (const char *format, char **invalid_reason) type = FAT_COUNT_POINTER; type |= (size & FAT_SIZE_MASK); break; + other: default: *invalid_reason = (*format == '\0' @@ -770,6 +779,18 @@ format_parse (const char *format, char **invalid_reason) return NULL; } +static void * +format_c_parse (const char *format, char **invalid_reason) +{ + return format_parse (format, false, invalid_reason); +} + +static void * +format_objc_parse (const char *format, char **invalid_reason) +{ + return format_parse (format, true, invalid_reason); +} + static void format_free (void *descr) { @@ -835,7 +856,16 @@ format_check (const lex_pos_ty *pos, void *msgid_descr, void *msgstr_descr, struct formatstring_parser formatstring_c = { - format_parse, + format_c_parse, + format_free, + format_get_number_of_directives, + format_check +}; + + +struct formatstring_parser formatstring_objc = +{ + format_objc_parse, format_free, format_get_number_of_directives, format_check @@ -846,8 +876,12 @@ void get_c99_format_directives (const char *string, struct interval **intervalsp, size_t *lengthp) { + /* Parse the format string with all possible extensions turned on. (The + caller has already verified that the format string is valid for the + particular language.) */ char *invalid_reason = NULL; - struct spec *descr = (struct spec *) format_parse (string, &invalid_reason); + struct spec *descr = + (struct spec *) format_parse (string, true, &invalid_reason); if (descr != NULL && descr->c99_directives_count > 0) { @@ -985,6 +1019,9 @@ format_print (void *descr) case FAT_STRING: printf ("s"); break; + case FAT_OBJC_OBJECT: + printf ("@"); + break; case FAT_POINTER: printf ("p"); break; @@ -1016,7 +1053,7 @@ main () line[--line_len] = '\0'; invalid_reason = NULL; - descr = format_parse (line, &invalid_reason); + descr = format_c_parse (line, &invalid_reason); format_print (descr); printf ("\n"); diff --git a/gettext-tools/src/format.c b/gettext-tools/src/format.c index cfa57f7bf..5b7edfcf3 100644 --- a/gettext-tools/src/format.c +++ b/gettext-tools/src/format.c @@ -27,6 +27,7 @@ struct formatstring_parser *formatstring_parsers[NFORMATS] = { /* format_c */ &formatstring_c, + /* format_objc */ &formatstring_objc, /* format_sh */ &formatstring_sh, /* format_python */ &formatstring_python, /* format_lisp */ &formatstring_lisp, diff --git a/gettext-tools/src/format.h b/gettext-tools/src/format.h index e29d09584..004ff8aef 100644 --- a/gettext-tools/src/format.h +++ b/gettext-tools/src/format.h @@ -62,6 +62,7 @@ struct formatstring_parser /* Format string parsers, each defined in its own file. */ extern DLL_VARIABLE struct formatstring_parser formatstring_c; +extern DLL_VARIABLE struct formatstring_parser formatstring_objc; extern DLL_VARIABLE struct formatstring_parser formatstring_sh; extern DLL_VARIABLE struct formatstring_parser formatstring_python; extern DLL_VARIABLE struct formatstring_parser formatstring_lisp; diff --git a/gettext-tools/src/message.c b/gettext-tools/src/message.c index c82ed412c..17de7225d 100644 --- a/gettext-tools/src/message.c +++ b/gettext-tools/src/message.c @@ -35,6 +35,7 @@ const char *const format_language[NFORMATS] = { /* format_c */ "c", + /* format_objc */ "objc", /* format_sh */ "sh", /* format_python */ "python", /* format_lisp */ "lisp", @@ -55,6 +56,7 @@ const char *const format_language[NFORMATS] = const char *const format_language_pretty[NFORMATS] = { /* format_c */ "C", + /* format_objc */ "Objective C", /* format_sh */ "Shell", /* format_python */ "Python", /* format_lisp */ "Lisp", diff --git a/gettext-tools/src/message.h b/gettext-tools/src/message.h index 9d9dbe58c..b6d6ff595 100644 --- a/gettext-tools/src/message.h +++ b/gettext-tools/src/message.h @@ -41,6 +41,7 @@ extern "C" { enum format_type { format_c, + format_objc, format_sh, format_python, format_lisp, @@ -57,7 +58,7 @@ enum format_type format_php, format_gcc_internal }; -#define NFORMATS 16 /* Number of format_type enum values. */ +#define NFORMATS 17 /* Number of format_type enum values. */ extern DLL_VARIABLE const char *const format_language[NFORMATS]; extern DLL_VARIABLE const char *const format_language_pretty[NFORMATS]; diff --git a/gettext-tools/src/write-mo.c b/gettext-tools/src/write-mo.c index 4efdf0d11..f8503072e 100644 --- a/gettext-tools/src/write-mo.c +++ b/gettext-tools/src/write-mo.c @@ -181,7 +181,8 @@ write_table (FILE *output_file, message_list_ty *mlp) /* Test if mp contains system dependent strings and thus requires the use of the .mo file minor revision 1. */ - if (possible_format_p (mp->is_format[format_c])) + if (possible_format_p (mp->is_format[format_c]) + || possible_format_p (mp->is_format[format_objc])) { /* Check whether msgid or msgstr contain ISO C 99 format string directives. No need to check msgid_plural, because diff --git a/gettext-tools/src/x-c.c b/gettext-tools/src/x-c.c index 368cabab7..c7194f855 100644 --- a/gettext-tools/src/x-c.c +++ b/gettext-tools/src/x-c.c @@ -90,7 +90,8 @@ x_c_trigraphs () /* If true extract all strings. */ static bool extract_all = false; -static hash_table keywords; +static hash_table c_keywords; +static hash_table objc_keywords; static bool default_keywords = true; @@ -101,8 +102,8 @@ x_c_extract_all () } -void -x_c_keyword (const char *name) +static void +add_keyword (const char *name, hash_table *keywords) { if (name == NULL) default_keywords = false; @@ -113,8 +114,8 @@ x_c_keyword (const char *name) int argnum2; const char *colon; - if (keywords.table == NULL) - init_hash (&keywords, 100); + if (keywords->table == NULL) + init_hash (keywords, 100); split_keywordspec (name, &end, &argnum1, &argnum2); @@ -125,19 +126,25 @@ x_c_keyword (const char *name) { if (argnum1 == 0) argnum1 = 1; - insert_entry (&keywords, name, end - name, + insert_entry (keywords, name, end - name, (void *) (long) (argnum1 + (argnum2 << 10))); } } } -bool -x_c_any_keywords () +void +x_c_keyword (const char *name) { - return (keywords.filled > 0) || default_keywords; + add_keyword (name, &c_keywords); } -/* Finish initializing the keywords hash table. +void +x_objc_keyword (const char *name) +{ + add_keyword (name, &objc_keywords); +} + +/* Finish initializing the keywords hash tables. Called after argument processing, before each file is processed. */ static void init_keywords () @@ -151,6 +158,19 @@ init_keywords () x_c_keyword ("dngettext:2,3"); x_c_keyword ("dcngettext:2,3"); x_c_keyword ("gettext_noop"); + + x_objc_keyword ("gettext"); + x_objc_keyword ("dgettext:2"); + x_objc_keyword ("dcgettext:2"); + x_objc_keyword ("ngettext:1,2"); + x_objc_keyword ("dngettext:2,3"); + x_objc_keyword ("dcngettext:2,3"); + x_objc_keyword ("gettext_noop"); + x_objc_keyword ("NSLocalizedString"); /* similar to gettext */ + x_objc_keyword ("_"); /* similar to gettext */ + x_objc_keyword ("NSLocalizedStaticString"); /* similar to gettext_noop */ + x_objc_keyword ("__"); /* similar to gettext_noop */ + default_keywords = false; } } @@ -194,6 +214,32 @@ init_flag_table_c () #endif } +void +init_flag_table_objc () +{ + /* Since the settings done in init_flag_table_c() also have an effect for + the ObjectiveC parser, we don't have to repeat them here. */ + xgettext_record_flag ("gettext:1:pass-objc-format"); + xgettext_record_flag ("dgettext:2:pass-objc-format"); + xgettext_record_flag ("dcgettext:2:pass-objc-format"); + xgettext_record_flag ("ngettext:1:pass-objc-format"); + xgettext_record_flag ("ngettext:2:pass-objc-format"); + xgettext_record_flag ("dngettext:2:pass-objc-format"); + xgettext_record_flag ("dngettext:3:pass-objc-format"); + xgettext_record_flag ("dcngettext:2:pass-objc-format"); + xgettext_record_flag ("dcngettext:3:pass-objc-format"); + xgettext_record_flag ("gettext_noop:1:pass-objc-format"); + xgettext_record_flag ("NSLocalizedString:1:pass-c-format"); + xgettext_record_flag ("NSLocalizedString:1:pass-objc-format"); + xgettext_record_flag ("_:1:pass-c-format"); + xgettext_record_flag ("_:1:pass-objc-format"); + xgettext_record_flag ("stringWithFormat::1:objc-format"); + xgettext_record_flag ("initWithFormat::1:objc-format"); + xgettext_record_flag ("stringByAppendingFormat::1:objc-format"); + xgettext_record_flag ("localizedStringWithFormat::1:objc-format"); + xgettext_record_flag ("appendFormat::1:objc-format"); +} + void init_flag_table_gcc_internal () { @@ -614,6 +660,7 @@ enum token_type_ty token_type_lparen, /* ( */ token_type_rparen, /* ) */ token_type_comma, /* , */ + token_type_colon, /* : */ token_type_name, /* abc */ token_type_number, /* 2.7 */ token_type_string_literal, /* "abc" */ @@ -1061,6 +1108,10 @@ phase5_get (token_ty *tp) tp->type = token_type_hash; return; + case ':': + tp->type = token_type_colon; + return; + case '@': if (objc_extensions) { @@ -1397,6 +1448,7 @@ enum xgettext_token_type_ty xgettext_token_type_lparen, xgettext_token_type_rparen, xgettext_token_type_comma, + xgettext_token_type_colon, xgettext_token_type_string_literal, xgettext_token_type_other }; @@ -1443,8 +1495,8 @@ x_c_lex (xgettext_token_ty *tp) case token_type_name: last_non_comment_line = newline_count; - if (find_entry (&keywords, token.string, strlen (token.string), - &keyword_value) + if (find_entry (objc_extensions ? &objc_keywords : &c_keywords, + token.string, strlen (token.string), &keyword_value) == 0) { tp->type = xgettext_token_type_keyword; @@ -1476,6 +1528,12 @@ x_c_lex (xgettext_token_ty *tp) tp->type = xgettext_token_type_comma; return; + case token_type_colon: + last_non_comment_line = newline_count; + + tp->type = xgettext_token_type_colon; + return; + case token_type_string_literal: last_non_comment_line = newline_count; @@ -1541,6 +1599,10 @@ extract_parenthesized (message_list_ty *mlp, /* Context iterator that will be used if the next token is a '('. */ flag_context_list_iterator_ty next_context_iter = passthrough_context_list_iterator; + /* Context iterator that will be used if the next token is a ':'. + (Objective C selector syntax.) */ + flag_context_list_iterator_ty selectorcall_context_iter = + passthrough_context_list_iterator; /* Current context. */ flag_context_ty inner_context = inherited_context (outer_context, @@ -1560,23 +1622,30 @@ extract_parenthesized (message_list_ty *mlp, next_commas_to_skip = token.argnum1 - 1; next_plural_commas = (token.argnum2 > token.argnum1 ? token.argnum2 - token.argnum1 : 0); - next_context_iter = - flag_context_list_iterator ( - flag_context_list_table_lookup ( - flag_context_list_table, - token.string, strlen (token.string))); - free (token.string); state = 1; - continue; + goto keyword_or_symbol; case xgettext_token_type_symbol: + state = 0; + keyword_or_symbol: next_context_iter = flag_context_list_iterator ( flag_context_list_table_lookup ( flag_context_list_table, token.string, strlen (token.string))); + if (objc_extensions) + { + size_t token_string_len = strlen (token.string); + token.string = xrealloc (token.string, token_string_len + 2); + token.string[token_string_len] = ':'; + token.string[token_string_len + 1] = '\0'; + selectorcall_context_iter = + flag_context_list_iterator ( + flag_context_list_table_lookup ( + flag_context_list_table, + token.string, token_string_len + 1)); + } free (token.string); - state = 0; continue; case xgettext_token_type_lparen: @@ -1585,6 +1654,7 @@ extract_parenthesized (message_list_ty *mlp, state ? next_plural_commas : 0)) return true; next_context_iter = null_context_list_iterator; + selectorcall_context_iter = null_context_list_iterator; state = 0; continue; @@ -1610,6 +1680,26 @@ extract_parenthesized (message_list_ty *mlp, flag_context_list_iterator_advance ( &context_iter)); next_context_iter = passthrough_context_list_iterator; + selectorcall_context_iter = passthrough_context_list_iterator; + state = 0; + continue; + + case xgettext_token_type_colon: + if (objc_extensions) + { + context_iter = selectorcall_context_iter; + inner_context = + inherited_context (inner_context, + flag_context_list_iterator_advance ( + &context_iter)); + next_context_iter = passthrough_context_list_iterator; + selectorcall_context_iter = passthrough_context_list_iterator; + } + else + { + next_context_iter = null_context_list_iterator; + selectorcall_context_iter = null_context_list_iterator; + } state = 0; continue; @@ -1641,11 +1731,13 @@ extract_parenthesized (message_list_ty *mlp, free (token.string); } next_context_iter = null_context_list_iterator; + selectorcall_context_iter = null_context_list_iterator; state = 0; continue; case xgettext_token_type_other: next_context_iter = null_context_list_iterator; + selectorcall_context_iter = null_context_list_iterator; state = 0; continue; diff --git a/gettext-tools/src/x-c.h b/gettext-tools/src/x-c.h index 90c9c42b7..cfacb4d95 100644 --- a/gettext-tools/src/x-c.h +++ b/gettext-tools/src/x-c.h @@ -36,7 +36,7 @@ { "C++", extract_c, \ &flag_table_c, &formatstring_c, NULL }, \ { "ObjectiveC", extract_objc, \ - &flag_table_c, &formatstring_c, NULL }, \ + &flag_table_objc, &formatstring_c, &formatstring_objc }, \ { "GCC-source", extract_c, \ &flag_table_gcc_internal, &formatstring_gcc_internal, NULL }, \ @@ -57,9 +57,10 @@ extern void extract_objc (FILE *fp, const char *real_filename, extern void x_c_extract_all (void); extern void x_c_keyword (const char *name); -extern bool x_c_any_keywords (void); +extern void x_objc_keyword (const char *name); extern void x_c_trigraphs (void); extern void init_flag_table_c (void); +extern void init_flag_table_objc (void); extern void init_flag_table_gcc_internal (void); diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c index c10b3ead4..1e3f4e7fb 100644 --- a/gettext-tools/src/xgettext.c +++ b/gettext-tools/src/xgettext.c @@ -135,6 +135,7 @@ 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_objc; 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; @@ -253,6 +254,8 @@ main (int argc, char *argv[]) bool do_version = false; msgdomain_list_ty *mdlp; bool join_existing = false; + bool no_default_keywords = false; + bool some_additional_keywords = false; bool sort_by_msgid = false; bool sort_by_filepos = false; const char *file_name; @@ -281,6 +284,7 @@ main (int argc, char *argv[]) default_domain = MESSAGE_DOMAIN_DEFAULT; xgettext_global_source_encoding = po_charset_ascii; init_flag_table_c (); + init_flag_table_objc (); init_flag_table_gcc_internal (); init_flag_table_sh (); init_flag_table_python (); @@ -364,6 +368,7 @@ main (int argc, char *argv[]) if (optarg == NULL || *optarg != '\0') { x_c_keyword (optarg); + x_objc_keyword (optarg); x_sh_keyword (optarg); x_python_keyword (optarg); x_lisp_keyword (optarg); @@ -375,6 +380,10 @@ main (int argc, char *argv[]) x_perl_keyword (optarg); x_php_keyword (optarg); x_glade_keyword (optarg); + if (optarg == NULL) + no_default_keywords = true; + else + some_additional_keywords = true; } break; case 'l': @@ -498,7 +507,7 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ error (EXIT_FAILURE, 0, _("\ --join-existing cannot be used when output is written to stdout")); - if (!x_c_any_keywords ()) + if (no_default_keywords && !some_additional_keywords) { error (0, 0, _("\ xgettext cannot work without keywords to look for")); @@ -1070,6 +1079,146 @@ flag_context_list_table_lookup (flag_context_list_table_ty *flag_table, } +static void +flag_context_list_table_insert (flag_context_list_table_ty *table, + unsigned int index, + const char *name_start, const char *name_end, + int argnum, enum is_format value, bool pass) +{ + 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; + } + } + } +} + + void xgettext_record_flag (const char *optionstring) { @@ -1165,202 +1314,93 @@ xgettext_record_flag (const char *optionstring) 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; + flag_context_list_table_insert (&flag_table_c, 0, + name_start, name_end, + argnum, value, pass); + flag_context_list_table_insert (&flag_table_objc, 0, + name_start, name_end, + argnum, value, pass); + break; + case format_objc: + flag_context_list_table_insert (&flag_table_objc, 1, + name_start, name_end, + argnum, value, pass); break; case format_sh: - table = &flag_table_sh; + flag_context_list_table_insert (&flag_table_sh, 0, + name_start, name_end, + argnum, value, pass); break; case format_python: - table = &flag_table_python; + flag_context_list_table_insert (&flag_table_python, 0, + name_start, name_end, + argnum, value, pass); break; case format_lisp: - table = &flag_table_lisp; + flag_context_list_table_insert (&flag_table_lisp, 0, + name_start, name_end, + argnum, value, pass); break; case format_elisp: - table = &flag_table_elisp; + flag_context_list_table_insert (&flag_table_elisp, 0, + name_start, name_end, + argnum, value, pass); break; case format_librep: - table = &flag_table_librep; + flag_context_list_table_insert (&flag_table_librep, 0, + name_start, name_end, + argnum, value, pass); break; case format_smalltalk: - return; + break; case format_java: - table = &flag_table_java; + flag_context_list_table_insert (&flag_table_java, 0, + name_start, name_end, + argnum, value, pass); break; case format_awk: - table = &flag_table_awk; + flag_context_list_table_insert (&flag_table_awk, 0, + name_start, name_end, + argnum, value, pass); break; case format_pascal: - return; + break; case format_ycp: - table = &flag_table_ycp; + flag_context_list_table_insert (&flag_table_ycp, 0, + name_start, name_end, + argnum, value, pass); break; case format_tcl: - table = &flag_table_tcl; + flag_context_list_table_insert (&flag_table_tcl, 0, + name_start, name_end, + argnum, value, pass); break; case format_perl: - table = &flag_table_perl; + flag_context_list_table_insert (&flag_table_perl, 0, + name_start, name_end, + argnum, value, pass); break; case format_perl_brace: - index = 1; - table = &flag_table_perl; + flag_context_list_table_insert (&flag_table_perl, 1, + name_start, name_end, + argnum, value, pass); break; case format_php: - table = &flag_table_php; + flag_context_list_table_insert (&flag_table_php, 0, + name_start, name_end, + argnum, value, pass); break; case format_gcc_internal: - table = &flag_table_gcc_internal; + flag_context_list_table_insert (&flag_table_gcc_internal, 0, + name_start, name_end, + argnum, value, pass); 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 @@ -1754,7 +1794,10 @@ meta information, not the empty string.\n"))); { if (is_format[i] == undecided && (formatstring_parsers[i] == current_formatstring_parser1 - || formatstring_parsers[i] == current_formatstring_parser2)) + || formatstring_parsers[i] == current_formatstring_parser2) + /* But avoid redundancy: objc-format is stronger than c-format. */ + && !(i == format_c && possible_format_p (is_format[format_objc])) + && !(i == format_objc && possible_format_p (is_format[format_c]))) { struct formatstring_parser *parser = formatstring_parsers[i]; char *invalid_reason = NULL; @@ -1849,7 +1892,12 @@ remember_a_message_plural (message_ty *mp, char *string, for (i = 0; i < NFORMATS; i++) if ((formatstring_parsers[i] == current_formatstring_parser1 || formatstring_parsers[i] == current_formatstring_parser2) - && (mp->is_format[i] == undecided || mp->is_format[i] == possible)) + && (mp->is_format[i] == undecided || mp->is_format[i] == possible) + /* But avoid redundancy: objc-format is stronger than c-format. */ + && !(i == format_c + && possible_format_p (mp->is_format[format_objc])) + && !(i == format_objc + && possible_format_p (mp->is_format[format_c]))) { struct formatstring_parser *parser = formatstring_parsers[i]; char *invalid_reason = NULL;