]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Support Objective C specific format strings.
authorBruno Haible <bruno@clisp.org>
Thu, 23 Oct 2003 17:21:20 +0000 (17:21 +0000)
committerBruno Haible <bruno@clisp.org>
Tue, 23 Jun 2009 10:11:06 +0000 (12:11 +0200)
13 files changed:
NEWS
gettext-tools/doc/ChangeLog
gettext-tools/doc/gettext.texi
gettext-tools/src/ChangeLog
gettext-tools/src/format-c.c
gettext-tools/src/format.c
gettext-tools/src/format.h
gettext-tools/src/message.c
gettext-tools/src/message.h
gettext-tools/src/write-mo.c
gettext-tools/src/x-c.c
gettext-tools/src/x-c.h
gettext-tools/src/xgettext.c

diff --git a/NEWS b/NEWS
index 334e735abb50e919340621d52d0d5e814c8282c1..506f075ec5ea03b43769e0cddfcce33aef468a78 100644 (file)
--- 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
index 5f869c3cef1b9dc8b6ef9c93040371fca7a83f06..d38e666918526b2a934045e9839cae63466d80e1 100644 (file)
@@ -1,3 +1,9 @@
+2003-10-13  Bruno Haible  <bruno@clisp.org>
+
+       * gettext.texi (PO Files): Mention objc-format, sh-format, perl-format,
+       perl-brace-format.
+       (objc-format): New subsection.
+
 2003-10-12  Bruno Haible  <bruno@clisp.org>
 
        * msgattrib.texi: Document --stringtable-input and --stringtable-output
index 01a4b909bfb5ac8351feb9e70c6bc2a1cceaa870..2ae21aa17c9711f84705a553069adebc8f1da1c8 100644 (file)
@@ -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{<libintl.h>}
 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}
index 2a104ea60ad93b5ca6345046173c1163f8c40299..5bf5bf2028b5a1d3cbed48a63607d4345520d00d 100644 (file)
@@ -1,3 +1,48 @@
+2003-10-13  Bruno Haible  <bruno@clisp.org>
+
+       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  <bruno@clisp.org>
 
        Support for GNUstep .strings format.
index 9452ddf855f48c7117569d9bffe523e0ab96bf37..86ae6a3ce9971aa292c962e4ec4e4dbcd444adef 100644 (file)
@@ -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");
index cfa57f7bf4de634e876c33c0da66b33a83be5583..5b7edfcf35da19be72634b482d8a89cbb254cc3c 100644 (file)
@@ -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,
index e29d095846e96e0ae1eafba18c3e6c8d9794a1cb..004ff8aefad066810dc2925d4016c6c08207aa79 100644 (file)
@@ -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;
index c82ed412cf7e22afd961f0a32cd0e0805337734d..17de7225d333b8862000904f7cea8c9af4fc721a 100644 (file)
@@ -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",
index 9d9dbe58cc9e0e3f4696162da96cbe027065e65c..b6d6ff59554655f49555debac2458c57cfe8a2cd 100644 (file)
@@ -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];
 
index 4efdf0d11cfed7e3ae52b88d2e2d4f2c01f781f2..f8503072ed0b930787d36fdacd2f7e1391107396 100644 (file)
@@ -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 <inttypes.h>
             format string directives.  No need to check msgid_plural, because
index 368cabab76c9a3f1fbab47722aeb6e99e9cc37a6..c7194f855f312f885c3470cb5bdf61b4899f2363 100644 (file)
@@ -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;
 
index 90c9c42b7a0a333ffe3d32c08d05556eb4b61d7f..cfacb4d95035dce2bbe30a25f1ef4b562f91e093 100644 (file)
@@ -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);
index c10b3ead41b9e76dfbecd6a6b22dd7930b4d2745..1e3f4e7fbd9fe320c3b4cbdd3a222b5a5211680a 100644 (file)
@@ -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;