]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Support for Qt message catalog format and Qt format strings.
authorBruno Haible <bruno@clisp.org>
Tue, 28 Oct 2003 16:10:35 +0000 (16:10 +0000)
committerBruno Haible <bruno@clisp.org>
Tue, 23 Jun 2009 10:11:06 +0000 (12:11 +0200)
28 files changed:
NEWS
gettext-tools/doc/ChangeLog
gettext-tools/doc/gettext.texi
gettext-tools/doc/msgfmt.texi
gettext-tools/doc/xgettext.texi
gettext-tools/po/ChangeLog
gettext-tools/po/POTFILES.in
gettext-tools/src/ChangeLog
gettext-tools/src/FILES
gettext-tools/src/Makefile.am
gettext-tools/src/Makefile.msvc
gettext-tools/src/Makefile.vms
gettext-tools/src/format-qt.c [new file with mode: 0644]
gettext-tools/src/format.c
gettext-tools/src/format.h
gettext-tools/src/message.c
gettext-tools/src/message.h
gettext-tools/src/msgfmt.c
gettext-tools/src/write-qt.c [new file with mode: 0644]
gettext-tools/src/write-qt.h [new file with mode: 0644]
gettext-tools/src/xgettext.c
gettext-tools/tests/ChangeLog
gettext-tools/tests/Makefile.am
gettext-tools/tests/format-qt-1 [new file with mode: 0755]
gettext-tools/tests/format-qt-2 [new file with mode: 0755]
gettext-tools/tests/msgfmt-15 [new file with mode: 0755]
gettext-tools/tests/qttest_pl.po [new file with mode: 0644]
gettext-tools/tests/qttest_pl.qm [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 506f075ec5ea03b43769e0cddfcce33aef468a78..7f19c8ef82e6444a66238f779f6e5237346b96b1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -39,6 +39,14 @@ Version 0.12.2 - September 2003
     special kind of format strings used in the GCC sources and marks them
     as 'gcc-internal-format'.
 
+  - C++ with Qt:
+
+    xgettext has a new option --qt that triggers the recognition and marking
+    of Qt format strings.
+
+    msgfmt has a new option --qt that generates binary message catalogs in
+    Qt's .qm format.
+
 * Data formats support:
 
   - Glade:
index d38e666918526b2a934045e9839cae63466d80e1..e8be6755129833d2108dd1a57e00832e52b0c0ca 100644 (file)
@@ -1,3 +1,10 @@
+2003-10-19  Bruno Haible  <bruno@clisp.org>
+
+       * gettext.texi (PO Files): Mention qt-format.
+       (qt-format): New subsection.
+       * msgfmt.texi: Document --qt option.
+       * xgettext.texi: Likewise.
+
 2003-10-13  Bruno Haible  <bruno@clisp.org>
 
        * gettext.texi (PO Files): Mention objc-format, sh-format, perl-format,
index 2ae21aa17c9711f84705a553069adebc8f1da1c8..6076ff72cdc8e7618aa0f5134e53eab0fd09d1fd 100644 (file)
@@ -342,6 +342,7 @@ The Translator's View
 * perl-format::                 Perl Format Strings
 * php-format::                  PHP Format Strings
 * gcc-internal-format::         GCC internal Format Strings
+* qt-format::                   Qt Format Strings
 
 Individual Programming Languages
 
@@ -1263,6 +1264,12 @@ Likewise for PHP, see @ref{php-format}.
 @kwindex no-gcc-internal-format@r{ flag}
 Likewise for the GCC sources, see @ref{gcc-internal-format}.
 
+@item qt-format
+@kwindex qt-format@r{ flag}
+@itemx no-qt-format
+@kwindex no-qt-format@r{ flag}
+Likewise for Qt, see @ref{qt-format}.
+
 @end table
 
 @kwindex msgid_plural
@@ -7275,6 +7282,7 @@ strings.
 * perl-format::                 Perl Format Strings
 * php-format::                  PHP Format Strings
 * gcc-internal-format::         GCC internal Format Strings
+* qt-format::                   Qt Format Strings
 @end menu
 
 @node c-format, objc-format, Translators for other Languages, Translators for other Languages
@@ -7437,7 +7445,7 @@ PHP format strings are described in the documentation of the PHP function
 @code{sprintf}, in @file{phpdoc/manual/function.sprintf.html} or
 @uref{http://www.php.net/manual/en/function.sprintf.php}.
 
-@node gcc-internal-format,  , php-format, Translators for other Languages
+@node gcc-internal-format, qt-format, php-format, Translators for other Languages
 @subsection GCC internal Format Strings
 
 These format strings are used inside the GCC sources.  In such a format
@@ -7455,6 +7463,14 @@ denotes a programming language, @samp{O} denotes a binary operator,
 @samp{P} denotes a function parameter, @samp{Q} denotes an assignment
 operator, @samp{V} denotes a const/volatile qualifier.
 
+@node qt-format,  , gcc-internal-format, Translators for other Languages
+@subsection Qt Format Strings
+
+Qt format strings are described in the documentation of the QString class
+@uref{file:/usr/lib/qt-3.0.5/doc/html/qstring.html}.
+In summary, a directive consists of a @samp{%} followed by a digit. The same
+directive cannot occur more than once in a format string.
+
 @node Maintainers for other Languages, List of Programming Languages, Translators for other Languages, Programming Languages
 @section The Maintainer's View
 
index 821ca8855167b1d1c6dc8d59814571f8cda01ddf..b072443a3fe4b327667c52b92e20bb33f2f930f9 100644 (file)
@@ -44,6 +44,11 @@ Like --java, and assume Java2 (JDK 1.2 or higher).
 @cindex Tcl mode, and @code{msgfmt} program
 Tcl mode: generate a tcl/msgcat @file{.msg} file.
 
+@item --qt
+@opindex --qt@r{, @code{msgfmt} option}
+@cindex Qt mode, and @code{msgfmt} program
+Qt mode: generate a Qt @file{.qm} file.
+
 @end table
 
 @subsection Output file location
index 2c84ee6e36cfbf2b5f975e191123c95a1c65c05b..5204b1f704cafc4d65fbf154a1750ab215d496e9 100644 (file)
@@ -215,7 +215,14 @@ Understand ANSI C trigraphs for input.
 @*
 This option has an effect only with the languages C, C++, ObjectiveC.
 
-@itemx --debug
+@item --qt
+@opindex --qt@r{, @code{xgettext} option}
+@cindex Qt format strings
+Recognize Qt format strings.
+@*
+This option has an effect only with the language C++.
+
+@item --debug
 @opindex --debug@r{, @code{xgettext} option}
 @cindex debugging messages marked as format strings
 Use the flags @code{c-format} and @code{possible-c-format} to show who was
index 8ccd553898866f6618465c2e8a4b8a77d7637073..dc675b62e53e0d61b6e41f2d1b9b73cf98cceb1f 100644 (file)
@@ -1,3 +1,7 @@
+2003-10-19  Bruno Haible  <bruno@clisp.org>
+
+       * POTFILES.in: Add src/format-qt.c and src/write-qt.c.
+
 2003-10-14  Bruno Haible  <bruno@clisp.org>
 
        * POTFILES.in: Add src/read-stringtable.c.
index c35255253c0254c0442eb96468d717e56ed6d460..6c2eba26a8051a6c858f9f661aa5f37d79745ba6 100644 (file)
@@ -38,6 +38,7 @@ src/format-perl.c
 src/format-perl-brace.c
 src/format-php.c
 src/format-python.c
+src/format-qt.c
 src/format-sh.c
 src/format-tcl.c
 src/format-ycp.c
@@ -74,6 +75,7 @@ src/urlget.c
 src/write-java.c
 src/write-mo.c
 src/write-po.c
+src/write-qt.c
 src/write-tcl.c
 src/x-awk.c
 src/x-c.c
index 496c6c7998c846359aad1dec1cb6e099be2443e6..8b58adfd2a1b9299a3c7b279596b81d3671a27b1 100644 (file)
@@ -1,3 +1,40 @@
+2003-10-19  Bruno Haible  <bruno@clisp.org>
+
+        * message.h (format_type): New enum value 'format_qt'.
+        (NFORMATS): Increment.
+        * message.c (format_language): Add format_qt entry.
+        (format_language_pretty): Likewise.
+       * format.h (formatstring_qt): New declaration.
+       * format-qt.c: New file.
+       * format.c (formatstring_parsers): Add formatstring_qt.
+       * xgettext.c (recognize_format_qt): New variable.
+       (long_options): Add option "--qt".
+       (main): Handle --qt option. Determine the extractor only after option
+       processing is complete.
+       (usage): Document --qt option.
+       (xgettext_record_flag): Handle format_qt.
+       (language_to_extractor): For C++, return a different extractor when
+       --qt was specified.
+       * write-qt.h: New file.
+       * write-qt.c: New file.
+       * msgfmt.c: Include write-qt.h.
+       (qt_mode): New variable.
+       (long_options): Add option "--qt".
+       (main): Handle --qt option. More checks for contradicting options.
+       Call msgdomain_write_qt.
+       (usage): Mention Qt mode.
+        (format_directive_domain): Ignore domain directive if in Qt mode.
+       * Makefile.am (noinst_HEADERS): Add write-qt.h.
+       (FORMAT_SOURCE): Add format-qt.c.
+       (msgfmt_SOURCES): Add write-qt.c.
+       * Makefile.msvc (OBJECTS): Add format-qt.obj.
+       (msgfmt_OBJECTS): Add write-qt.obj.
+       (format-qt.obj, write-qt.obj): New rules.
+       * Makefile.vms (OBJECTS): Add format-qt.obj.
+       (msgfmt_OBJECTS): Add write-qt.obj.
+       (format-qt.obj, write-qt.obj): New rules.
+       * FILES: Update.
+
 2003-10-14  Bruno Haible  <bruno@clisp.org>
 
        Fix compilation errors in C++ mode.
index e67a6a746cfe3c2f67b6a5b1e4227eaa6e8a1333..677132a0a9907b71d27b3f83f894822dc72453ec 100644 (file)
@@ -196,6 +196,7 @@ format-perl.c          Format string handling for Perl.
 format-perl-brace.c    Format string handling for Perl, braced syntax.
 format-php.c           Format string handling for PHP.
 format-gcc-internal.c  Format string handling GCC internal.
+format-qt.c            Format string handling for Qt.
 format.c        Table of the language dependent format string handlers.
 
 +-------------- The 'msgfmt' program
@@ -214,6 +215,9 @@ format.c        Table of the language dependent format string handlers.
 | write-tcl.h
 | write-tcl.c
 |               Generating Tcl .msg files.
+| write-qt.h
+| write-qt.c
+|               Generating Qt .qm files.
 | msgfmt.c
 |               Main source for the 'msgfmt' program.
 |
index fc8cae11cd613250b0c1faaa591027988950139f..11ce1577ca2132f52373053ab5ad59535572f1cc 100644 (file)
@@ -40,10 +40,10 @@ str-list.h write-po.h write-properties.h write-stringtable.h dir-list.h \
 file-list.h po-gram-gen.h po-gram-gen2.h po-hash-gen.h msgl-charset.h \
 msgl-equal.h msgl-iconv.h msgl-ascii.h msgl-cat.h msgl-english.h msgfmt.h \
 msgunfmt.h plural-count.h read-mo.h write-mo.h read-java.h write-java.h \
-read-tcl.h write-tcl.h po-time.h plural-table.h format.h xgettext.h x-c.h \
-x-po.h x-sh.h x-python.h x-lisp.h x-elisp.h x-librep.h x-smalltalk.h x-java.h \
-x-properties.h x-awk.h x-ycp.h x-tcl.h x-perl.h x-php.h x-stringtable.h \
-x-rst.h x-glade.h
+read-tcl.h write-tcl.h write-qt.h po-time.h plural-table.h format.h \
+xgettext.h x-c.h x-po.h x-sh.h x-python.h x-lisp.h x-elisp.h x-librep.h \
+x-smalltalk.h x-java.h x-properties.h x-awk.h x-ycp.h x-tcl.h x-perl.h \
+x-php.h x-stringtable.h x-rst.h x-glade.h
 
 EXTRA_DIST += FILES project-id ChangeLog.0
 
@@ -95,7 +95,7 @@ FORMAT_SOURCE = format.c format-invalid.h \
 format-c.c format-sh.c format-python.c format-lisp.c format-elisp.c \
 format-librep.c format-java.c format-awk.c format-pascal.c format-ycp.c \
 format-tcl.c format-perl.c format-perl-brace.c format-php.c \
-format-gcc-internal.c
+format-gcc-internal.c format-qt.c
 
 # libgettextsrc contains all code that is needed by at least two programs.
 libgettextsrc_la_SOURCES = \
@@ -116,7 +116,8 @@ LIBUNINAME = ../libuniname/libuniname.a
 
 # Source dependencies.
 msgcmp_SOURCES = msgcmp.c
-msgfmt_SOURCES = msgfmt.c write-mo.c write-java.c write-tcl.c plural-eval.c
+msgfmt_SOURCES = msgfmt.c write-mo.c write-java.c write-tcl.c write-qt.c \
+  plural-eval.c
 msgmerge_SOURCES = msgmerge.c plural-count.c
 msgunfmt_SOURCES = msgunfmt.c read-mo.c read-java.c read-tcl.c
 xgettext_SOURCES = xgettext.c \
index d9c2f922e43133072068112b6d931469e2cb843a..d17bfb5809ac8a81d050de67de2bcf0934771548 100644 (file)
@@ -145,10 +145,11 @@ OBJECTS = \
   format-perl.obj \
   format-perl-brace.obj \
   format-php.obj \
-  format-gcc-internal.obj
+  format-gcc-internal.obj \
+  format-qt.obj
 
 msgcmp_OBJECTS = msgcmp.obj
-msgfmt_OBJECTS = msgfmt.obj write-mo.obj write-java.obj write-tcl.obj plural-eval.obj
+msgfmt_OBJECTS = msgfmt.obj write-mo.obj write-java.obj write-tcl.obj write-qt.obj plural-eval.obj
 msgmerge_OBJECTS = msgmerge.obj plural-count.obj
 msgunfmt_OBJECTS = msgunfmt.obj read-mo.obj read-java.obj read-tcl.obj
 xgettext_OBJECTS = xgettext.obj x-c.obj x-po.obj x-sh.obj x-python.obj x-lisp.obj x-elisp.obj x-librep.obj x-smalltalk.obj x-java.obj x-awk.obj x-ycp.obj x-tcl.obj x-perl.obj x-php.obj x-rst.obj x-glade.obj
@@ -293,6 +294,9 @@ format-php.obj : format-php.c
 format-gcc-internal.obj : format-gcc-internal.c
        $(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c format-gcc-internal.c
 
+format-qt.obj : format-qt.c
+       $(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c format-qt.c
+
 !if !$(DLL)
 
 gettextsrc.lib : $(OBJECTS)
@@ -327,6 +331,9 @@ write-java.obj : write-java.c
 write-tcl.obj : write-tcl.c
        $(CC) $(INCLUDES) $(CFLAGS) -c write-tcl.c
 
+write-qt.obj : write-qt.c
+       $(CC) $(INCLUDES) $(CFLAGS) -c write-qt.c
+
 plural-eval.obj : plural-eval.c
        $(CC) $(INCLUDES) $(CFLAGS) -c plural-eval.c
 
index 2282c7fbca6923a69a4777e86b221e7f302da97a..8344f4a8bcbe8b32d11dd7086874eff26b2c1730 100644 (file)
@@ -91,10 +91,11 @@ OBJECTS = \
   format-perl.obj, \
   format-perl-brace.obj, \
   format-php.obj, \
-  format-gcc-internal.obj
+  format-gcc-internal.obj \
+  format-qt.obj
 
 msgcmp_OBJECTS = msgcmp.obj
-msgfmt_OBJECTS = msgfmt.obj, write-mo.obj, write-java.obj, write-tcl.obj, plural-eval.obj
+msgfmt_OBJECTS = msgfmt.obj, write-mo.obj, write-java.obj, write-tcl.obj, write-qt.obj, plural-eval.obj
 msgmerge_OBJECTS = msgmerge.obj, plural-count.obj
 msgunfmt_OBJECTS = msgunfmt.obj, read-mo.obj, read-java.obj, read-tcl.obj
 xgettext_OBJECTS = xgettext.obj, x-c.obj, x-po.obj, x-sh.obj, x-python.obj, x-lisp.obj, x-elisp.obj, x-librep.obj, x-smalltalk.obj, x-java.obj, x-awk.obj, x-ycp.obj, x-tcl.obj, x-perl.obj, x-php.obj, x-rst.obj, x-glade.obj
@@ -237,6 +238,9 @@ format-php.obj : format-php.c
 format-gcc-internal.obj : format-gcc-internal.c
        $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) format-gcc-internal.c
 
+format-qt.obj : format-qt.c
+       $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) format-qt.c
+
 gettextsrc.olb : $(OBJECTS)
        $(AR) $(AR_FLAGS) gettextsrc.olb $(OBJECTS)
 
@@ -257,6 +261,9 @@ write-java.obj : write-java.c
 write-tcl.obj : write-tcl.c
        $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) write-tcl.c
 
+write-qt.obj : write-qt.c
+       $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) write-qt.c
+
 plural-eval.obj : plural-eval.c
        $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) plural-eval.c
 
diff --git a/gettext-tools/src/format-qt.c b/gettext-tools/src/format-qt.c
new file mode 100644 (file)
index 0000000..715d10b
--- /dev/null
@@ -0,0 +1,230 @@
+/* Qt format strings.
+   Copyright (C) 2003 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "xalloc.h"
+#include "xerror.h"
+#include "error.h"
+#include "error-progname.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Qt format strings are processed by QString::arg and are documented in
+   qt-3.0.5/doc/html/qstring.html.
+   A directive starts with '%' and is followed by a digit ('0' to '9').
+   Each %n must occur only once in the given string.
+   The first .arg() invocation replaces the %n with the lowest numbered n,
+   the next .arg() invocation then replaces the %n with the second-lowest
+   numbered n, and so on.
+   (This is inherently buggy because a '%' in the first replacement confuses
+   the second .arg() invocation.)
+   Although %0 is supported, usually %1 denotes the first argument, %2 the
+   second argument etc.  */
+
+struct spec
+{
+  unsigned int directives;
+  unsigned int arg_count;
+  bool args_used[10];
+};
+
+
+static void *
+format_parse (const char *format, char **invalid_reason)
+{
+  struct spec spec;
+  struct spec *result;
+
+  spec.directives = 0;
+  spec.arg_count = 0;
+
+  for (; *format != '\0';)
+    if (*format++ == '%')
+      if (*format >= '0' && *format <= '9')
+       {
+         /* A directive.  */
+         unsigned int number;
+
+         spec.directives++;
+
+         number = *format - '0';
+
+         while (spec.arg_count <= number)
+           spec.args_used[spec.arg_count++] = false;
+         if (spec.args_used[number])
+           {
+             *invalid_reason =
+               xasprintf (_("Multiple references to %%%c."), *format);
+             goto bad_format;
+           }
+         spec.args_used[number] = true;
+
+         format++;
+       }
+
+  result = (struct spec *) xmalloc (sizeof (struct spec));
+  *result = spec;
+  return result;
+
+ bad_format:
+  return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+  struct spec *spec = (struct spec *) descr;
+
+  free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+  struct spec *spec = (struct spec *) descr;
+
+  return spec->directives;
+}
+
+static bool
+format_check (const lex_pos_ty *pos, void *msgid_descr, void *msgstr_descr,
+             bool equality, bool noisy, const char *pretty_msgstr)
+{
+  struct spec *spec1 = (struct spec *) msgid_descr;
+  struct spec *spec2 = (struct spec *) msgstr_descr;
+  bool err = false;
+  unsigned int i;
+
+  for (i = 0; i < spec1->arg_count || i < spec2->arg_count; i++)
+    {
+      bool arg_used1 = (i < spec1->arg_count && spec1->args_used[i]);
+      bool arg_used2 = (i < spec2->arg_count && spec2->args_used[i]);
+
+      /* The translator cannot omit a %n from the msgstr because that would
+        yield a "Argument missing" warning at runtime.  */
+      if (arg_used1 != arg_used2)
+       {
+         if (noisy)
+           {
+             error_with_progname = false;
+             error_at_line (0, 0, pos->file_name, pos->line_number,
+                            arg_used1
+                            ? _("a format specification for argument %u doesn't exist in '%s'")
+                            : _("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
+                            i, pretty_msgstr);
+             error_with_progname = true;
+           }
+         err = true;
+         break;
+       }
+    }
+
+  return err;
+}
+
+
+struct formatstring_parser formatstring_qt =
+{
+  format_parse,
+  format_free,
+  format_get_number_of_directives,
+  format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+   format_parse for strings read from standard input.  */
+
+#include <stdio.h>
+#include "getline.h"
+
+static void
+format_print (void *descr)
+{
+  struct spec *spec = (struct spec *) descr;
+  unsigned int i;
+
+  if (spec == NULL)
+    {
+      printf ("INVALID");
+      return;
+    }
+
+  printf ("(");
+  for (i = 0; i < spec->arg_count; i++)
+    {
+      if (i > 0)
+       printf (" ");
+      if (spec->args_used[i])
+       printf ("*");
+      else
+       printf ("_");
+    }
+  printf (")");
+}
+
+int
+main ()
+{
+  for (;;)
+    {
+      char *line = NULL;
+      size_t line_size = 0;
+      int line_len;
+      char *invalid_reason;
+      void *descr;
+
+      line_len = getline (&line, &line_size, stdin);
+      if (line_len < 0)
+       break;
+      if (line_len > 0 && line[line_len - 1] == '\n')
+       line[--line_len] = '\0';
+
+      invalid_reason = NULL;
+      descr = format_parse (line, &invalid_reason);
+
+      format_print (descr);
+      printf ("\n");
+      if (descr == NULL)
+       printf ("%s\n", invalid_reason);
+
+      free (invalid_reason);
+      free (line);
+    }
+
+  return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../lib -I../intl -DHAVE_CONFIG_H -DTEST format-qt.c ../lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
index 5b7edfcf35da19be72634b482d8a89cbb254cc3c..82a439379e1dbfd9fff0b036d6ad860d86c070b1 100644 (file)
@@ -42,5 +42,6 @@ struct formatstring_parser *formatstring_parsers[NFORMATS] =
   /* format_perl */            &formatstring_perl,
   /* format_perl_brace */      &formatstring_perl_brace,
   /* format_php */             &formatstring_php,
-  /* format_gcc_internal */    &formatstring_gcc_internal
+  /* format_gcc_internal */    &formatstring_gcc_internal,
+  /* format_qt */              &formatstring_qt
 };
index 004ff8aefad066810dc2925d4016c6c08207aa79..180c99a05a0fe7e78b91edc5753b4f58306635e7 100644 (file)
@@ -78,6 +78,7 @@ extern DLL_VARIABLE struct formatstring_parser formatstring_perl;
 extern DLL_VARIABLE struct formatstring_parser formatstring_perl_brace;
 extern DLL_VARIABLE struct formatstring_parser formatstring_php;
 extern DLL_VARIABLE struct formatstring_parser formatstring_gcc_internal;
+extern DLL_VARIABLE struct formatstring_parser formatstring_qt;
 
 /* Table of all format string parsers.  */
 extern DLL_VARIABLE struct formatstring_parser *formatstring_parsers[NFORMATS];
index 17de7225d333b8862000904f7cea8c9af4fc721a..e168b57e149a9dfcb2b5e1169a826b95929f2e2a 100644 (file)
@@ -50,7 +50,8 @@ const char *const format_language[NFORMATS] =
   /* format_perl */            "perl",
   /* format_perl_brace */      "perl-brace",
   /* format_php */             "php",
-  /* format_gcc_internal */    "gcc-internal"
+  /* format_gcc_internal */    "gcc-internal",
+  /* format_qt */              "qt"
 };
 
 const char *const format_language_pretty[NFORMATS] =
@@ -71,7 +72,8 @@ const char *const format_language_pretty[NFORMATS] =
   /* format_perl */            "Perl",
   /* format_perl_brace */      "Perl brace",
   /* format_php */             "PHP",
-  /* format_gcc_internal */    "GCC internal"
+  /* format_gcc_internal */    "GCC internal",
+  /* format_qt */              "Qt"
 };
 
 
index b6d6ff59554655f49555debac2458c57cfe8a2cd..085fd37543a99d724d9c454ed6ea92d019678295 100644 (file)
@@ -56,9 +56,10 @@ enum format_type
   format_perl,
   format_perl_brace,
   format_php,
-  format_gcc_internal
+  format_gcc_internal,
+  format_qt
 };
-#define NFORMATS 17    /* Number of format_type enum values.  */
+#define NFORMATS 18    /* 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 a30b98a05607fb33ae4c4215765dc71fc77eeef5..25931370fb31e3572a9d5d2e66c1a339a030a752 100644 (file)
@@ -49,6 +49,7 @@
 #include "write-mo.h"
 #include "write-java.h"
 #include "write-tcl.h"
+#include "write-qt.h"
 
 #include "gettext.h"
 #include "message.h"
@@ -95,6 +96,9 @@ static bool tcl_mode;
 static const char *tcl_locale_name;
 static const char *tcl_base_directory;
 
+/* Qt mode output file specification.  */
+static bool qt_mode;
+
 /* We may have more than one input file.  Domains with same names in
    different files have to merged.  So we need a list of tables for
    each output file.  */
@@ -164,6 +168,7 @@ static const struct option long_options[] =
   { "no-hash", no_argument, NULL, CHAR_MAX + 6 },
   { "output-file", required_argument, NULL, 'o' },
   { "properties-input", no_argument, NULL, 'P' },
+  { "qt", no_argument, NULL, CHAR_MAX + 9 },
   { "resource", required_argument, NULL, 'r' },
   { "statistics", no_argument, &do_statistics, 1 },
   { "strict", no_argument, NULL, 'S' },
@@ -317,6 +322,9 @@ main (int argc, char *argv[])
       case CHAR_MAX + 8: /* --stringtable-input */
        input_syntax = syntax_stringtable;
        break;
+      case CHAR_MAX + 9: /* --qt */
+       qt_mode = true;
+       break;
       default:
        usage (EXIT_FAILURE);
        break;
@@ -351,6 +359,12 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
   if (java_mode && tcl_mode)
     error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
           "--java", "--tcl");
+  if (java_mode && qt_mode)
+    error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+          "--java", "--qt");
+  if (tcl_mode && qt_mode)
+    error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+          "--tcl", "--qt");
   if (java_mode)
     {
       if (output_file_name != NULL)
@@ -415,8 +429,8 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
   if (output_file_name != NULL)
     current_domain =
       new_domain (output_file_name,
-                 strict_uniforum ? add_mo_suffix (output_file_name)
-                                 : output_file_name);
+                 !qt_mode && strict_uniforum ? add_mo_suffix (output_file_name)
+                                             : output_file_name);
 
   /* Process all given .po files.  */
   while (argc > optind)
@@ -465,6 +479,12 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
                                   tcl_locale_name, tcl_base_directory))
            exit_status = EXIT_FAILURE;
        }
+      else if (qt_mode)
+       {
+         if (msgdomain_write_qt (domain->mlp, canon_encoding,
+                                 domain->domain_name, domain->file_name))
+           exit_status = EXIT_FAILURE;
+       }
       else
        {
          if (msgdomain_write_mo (domain->mlp, domain->domain_name,
@@ -541,6 +561,8 @@ Operation mode:\n"));
       --java2                 like --java, and assume Java2 (JDK 1.2 or higher)\n"));
       printf (_("\
       --tcl                   Tcl mode: generate a tcl/msgcat .msg file\n"));
+      printf (_("\
+      --qt                    Qt mode: generate a Qt .qm file\n"));
       printf ("\n");
       printf (_("\
 Output file location:\n"));
@@ -1391,7 +1413,7 @@ msgfmt_set_domain (default_po_reader_ty *this, char *name)
 {
   /* If no output file was given, we change it with each `domain'
      directive.  */
-  if (!java_mode && !tcl_mode && output_file_name == NULL)
+  if (!java_mode && !tcl_mode && !qt_mode && output_file_name == NULL)
     {
       size_t correct;
 
diff --git a/gettext-tools/src/write-qt.c b/gettext-tools/src/write-qt.c
new file mode 100644 (file)
index 0000000..95fabe3
--- /dev/null
@@ -0,0 +1,539 @@
+/* Writing Qt .qm files.
+   Copyright (C) 2003 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification.  */
+#include "write-qt.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "xerror.h"
+#include "message.h"
+#include "po-charset.h"
+#include "msgl-iconv.h"
+#include "hash-string.h"
+#include "utf8-ucs4.h"
+#include "xalloc.h"
+#include "obstack.h"
+#include "binary-io.h"
+#include "fwriteerror.h"
+#include "exit.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Qt .qm files are read by the QTranslator::load() function and written
+   by the Qt QTranslator::save() function.
+
+   The Qt tool 'msg2qm' uses the latter function and can convert PO files
+   to .qm files. But since 'msg2qm' is marked as an "old" tool in Qt 3.0.5's
+   i18n.html documentation and therefore likely to disappear, we provide the
+   same functionality here.
+
+   The format of .qm files, as reverse engineered from the functions
+     QTranslator::save(const QString& filename, SaveMode mode)
+     QTranslator::squeeze(SaveMode mode)
+     QTranslatorMessage::write(QDataStream& stream, bool strip, Prefix prefix)
+     elfHash(const char* name)
+   in qt-3.0.5, is as follows:
+
+     It's a binary data format. Elements are u8 (byte), u16, u32. They are
+     written in big-endian order.
+
+     The file starts with a magic string of 16 bytes:
+       3C B8 64 18 CA EF 9C 95 CD 21 1C BF 60 A1 BD DD
+
+     Then come three sections. Each of the three sections is optional. Each
+     has this structure:
+       struct {
+         u8 section_type; // 0x42 = hashes, 0x69 = messages, 0x2f = contexts
+         u32 length; // number of bytes of the data
+         u8 data[length];
+       };
+
+     In the first section, the hashes section, the data has the following
+     structure:
+       It's a sorted array of
+         struct {
+           u32 hashcode; // elfHash of the concatenation of msgid and
+                         // disambiguating-comment
+           u32 offset; // offset within the data[] of the messages section
+         };
+       It's sorted in ascending order by hashcode as primary sorting criteria
+       and - when the hashcodes are the same - by offset as secondary criteria.
+
+     In the second section, the messages section, the data has the following
+     structure:
+       It's a sequence of records, each representing a message, in no
+       particular order. Each record is a sequence of subsections, each
+       introduced by a particular subsection tag. The possible subsection tags
+       are (and they usually occur in this order):
+         - 03: Translation. Followed by the msgstr in UCS-2 or UTF-16 format:
+               struct {
+                 u32 length;
+                 u16 chars[length/2];
+               };
+         - 08: Disambiguating-comment. Followed by the NUL-terminated,
+               ISO-8859-1 encoded, disambiguating-comment string:
+               struct {
+                 u32 length;    // number of bytes including the NUL at the end
+                 u8 chars[length];
+               };
+         - 06: SourceText, i.e. msgid. Followed by the NUL-terminated,
+               ISO-8859-1 encoded, msgid:
+               struct {
+                 u32 length;    // number of bytes including the NUL at the end
+                 u8 chars[length];
+               };
+         - 02: SourceText16, i.e. msgid. Encoded as UCS-2, but must actually
+               be ISO-8859-1.
+               struct {
+                 u32 length;
+                 u16 chars[length/2];
+               };
+               This subsection tag is obsoleted by SourceText.
+         - 07: Context. Followed by the NUL-terminated, ISO-8859-1 encoded,
+               context string (usually a C++ class name or empty):
+               struct {
+                 u32 length;    // number of bytes including the NUL at the end
+                 u8 chars[length];
+               };
+         - 04: Context16. Encoded as UCS-2, but must actually be ISO-8859-1.
+               struct {
+                 u32 length;
+                 u16 chars[length/2];
+               };
+               This subsection tag is obsoleted by Context.
+         - 05: Hash. Followed by
+               struct {
+                 u32 hashcode; // elfHash of the concatenation of msgid and
+                               // disambiguating-comment
+               };
+         - 01: End. Designates the end of the record. No further data.
+       Usually the following subsections are written, but some of them are
+       optional:
+         - 03: Translation.
+         - 08: Disambiguating-comment (optional).
+         - 06: SourceText (optional).
+         - 07: Context (optional).
+         - 05: Hash.
+         - 01: End.
+       A subsection can be omitted if the value to be output is the same as
+       for the previous record.
+
+     In the third section, the contexts section, the data contains a hash
+     table. Quite complicated.
+
+     The elfHash function is the same as our hash_string function, except that
+     at the end it maps a hash code of 0x00000000 to 0x00000001.
+
+   When we convert from PO file format, all disambiguating-comments and
+   contexts are empty, and therefore the contexts section can be omitted.  */
+
+
+/* Write a u8 (a single byte) to the output stream.  */
+static inline void
+write_u8 (FILE *output_file, unsigned char value)
+{
+  putc (value, output_file);
+}
+
+/* Write a u16 (two bytes) to the output stream.  */
+static inline void
+write_u16 (FILE *output_file, unsigned short value)
+{
+  unsigned char data[2];
+
+  data[0] = (value >> 8) & 0xff;
+  data[1] = value & 0xff;
+
+  fwrite (data, 2, 1, output_file);
+}
+
+/* Write a u32 (four bytes) to the output stream.  */
+static inline void
+write_u32 (FILE *output_file, unsigned int value)
+{
+  unsigned char data[4];
+
+  data[0] = (value >> 24) & 0xff;
+  data[1] = (value >> 16) & 0xff;
+  data[2] = (value >> 8) & 0xff;
+  data[3] = value & 0xff;
+
+  fwrite (data, 4, 1, output_file);
+}
+
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+/* Add a u8 (a single byte) to an obstack.  */
+static void
+append_u8 (struct obstack *mempool, unsigned char value)
+{
+  unsigned char data[1];
+
+  data[0] = value;
+
+  obstack_grow (mempool, data, 1);
+}
+
+/* Add a u16 (two bytes) to an obstack.  */
+static void
+append_u16 (struct obstack *mempool, unsigned short value)
+{
+  unsigned char data[2];
+
+  data[0] = (value >> 8) & 0xff;
+  data[1] = value & 0xff;
+
+  obstack_grow (mempool, data, 2);
+}
+
+/* Add a u32 (four bytes) to an obstack.  */
+static void
+append_u32 (struct obstack *mempool, unsigned int value)
+{
+  unsigned char data[4];
+
+  data[0] = (value >> 24) & 0xff;
+  data[1] = (value >> 16) & 0xff;
+  data[2] = (value >> 8) & 0xff;
+  data[3] = value & 0xff;
+
+  obstack_grow (mempool, data, 4);
+}
+
+/* Add an ISO-8859-1 encoded string to an obstack.  */
+static void
+append_base_string (struct obstack *mempool, const char *string)
+{
+  size_t length = strlen (string) + 1;
+  append_u32 (mempool, length);
+  obstack_grow (mempool, string, length);
+}
+
+/* Add an UTF-16 encoded string to an obstack.  */
+static void
+append_unicode_string (struct obstack *mempool, const unsigned short *string,
+                      size_t length)
+{
+  append_u32 (mempool, length * 2);
+  for (; length > 0; string++, length--)
+    append_u16 (mempool, *string);
+}
+
+/* Retrieve a 4-byte integer from memory.  */
+static inline unsigned int
+peek_u32 (const unsigned char *p)
+{
+  return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+}
+
+/* Convert an UTF-8 string to ISO-8859-1, without error checking.  */
+static char *
+conv_to_iso_8859_1 (const char *string)
+{
+  size_t length = strlen (string);
+  const char *str = string;
+  const char *str_limit = string + length;
+  /* Conversion to ISO-8859-1 can only reduce the number of bytes.  */
+  char *result = (char *) xmalloc (length + 1);
+  char *q = result;
+
+  while (str < str_limit)
+    {
+      unsigned int uc;
+      str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
+      /* It has already been verified that the string its in ISO-8859-1.  */
+      if (!(uc < 0x100))
+       abort ();
+      /* Store as ISO-8859-1.  */
+      *q++ = (unsigned char) uc;
+    }
+  *q = '\0';
+  assert (q - result <= length);
+
+  return result;
+}
+
+/* Convert an UTF-8 string to UTF-16, returning its size (number of UTF-16
+   codepoints) in *SIZEP.  */
+static unsigned short *
+conv_to_utf16 (const char *string, size_t *sizep)
+{
+  size_t length = strlen (string);
+  const char *str = string;
+  const char *str_limit = string + length;
+  /* Conversion to UTF-16 can at most double the number of bytes.  */
+  unsigned short *result = (unsigned short *) xmalloc (2 * length);
+  unsigned short *q = result;
+
+  while (str < str_limit)
+    {
+      unsigned int uc;
+      str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
+      if (uc < 0x10000)
+       /* UCS-2 character.  */
+       *q++ = (unsigned short) uc;
+      else
+       {
+         /* UTF-16 surrogate.  */
+         *q++ = 0xd800 + ((uc - 0x10000) >> 10);
+         *q++ = 0xdc00 + ((uc - 0x10000) & 0x3ff);
+       }
+    }
+  assert (q - result <= 2 * length);
+
+  *sizep = q - result;
+  return result;
+}
+
+/* Return the Qt hash code of a string.  */
+static unsigned int
+string_hashcode (const char *str)
+{
+  unsigned int h;
+
+  h = hash_string (str);
+  if (h == 0)
+    h = 1;
+  return h;
+}
+
+/* Compare two entries of the hashes section.  */
+static int
+cmp_hashes (const void *va, const void *vb)
+{
+  const unsigned char *a = (const unsigned char *) va;
+  const unsigned char *b = (const unsigned char *) vb;
+  unsigned int a_hashcode = peek_u32 (a);
+  unsigned int b_hashcode = peek_u32 (b);
+
+  if (a_hashcode != b_hashcode)
+    return (a_hashcode >= b_hashcode ? 1 : -1);
+  else
+    {
+      unsigned int a_offset = peek_u32 (a + 4);
+      unsigned int b_offset = peek_u32 (b + 4);
+
+      if (a_offset != b_offset)
+       return (a_offset >= b_offset ? 1 : -1);
+      else
+       return 0;
+    }
+}
+
+
+/* Write a section to the output stream.  */
+static void
+write_section (FILE *output_file, unsigned char tag, void *data, size_t size)
+{
+  /* A section can be omitted if it is empty.  */
+  if (size > 0)
+    {
+      write_u8 (output_file, tag);
+      write_u32 (output_file, size);
+      fwrite (data, size, 1, output_file);
+    }
+}
+
+
+/* Write an entire .qm file.  */
+static void
+write_qm (FILE *output_file, message_list_ty *mlp)
+{
+  static unsigned char magic[16] =
+    {
+      0x3C, 0xB8, 0x64, 0x18, 0xCA, 0xEF, 0x9C, 0x95,
+      0xCD, 0x21, 0x1C, 0xBF, 0x60, 0xA1, 0xBD, 0xDD
+    };
+  struct obstack hashes_pool;
+  struct obstack messages_pool;
+  size_t j;
+
+  obstack_init (&hashes_pool);
+  obstack_init (&messages_pool);
+
+  /* Prepare the hashes section and the messages section.  */
+  for (j = 0; j < mlp->nitems; j++)
+    {
+      message_ty *mp = mlp->item[j];
+
+      /* No need to emit the header entry, it's not needed at runtime.  */
+      if (mp->msgid[0] != '\0')
+       {
+         char *msgid_as_iso_8859_1 = conv_to_iso_8859_1 (mp->msgid);
+         size_t msgstr_len;
+         unsigned short *msgstr_as_utf16 =
+           conv_to_utf16 (mp->msgstr, &msgstr_len);
+         unsigned int hashcode = string_hashcode (msgid_as_iso_8859_1);
+         unsigned int offset = obstack_object_size (&messages_pool);
+
+         /* Add a record to the hashes section.  */
+         append_u32 (&hashes_pool, hashcode);
+         append_u32 (&hashes_pool, offset);
+
+         /* Add a record to the messages section.  */
+
+         append_u8 (&messages_pool, 0x03);
+         append_unicode_string (&messages_pool, msgstr_as_utf16, msgstr_len);
+
+         append_u8 (&messages_pool, 0x08);
+         append_base_string (&messages_pool, "");
+
+         append_u8 (&messages_pool, 0x06);
+         append_base_string (&messages_pool, msgid_as_iso_8859_1);
+
+         append_u8 (&messages_pool, 0x07);
+         append_base_string (&messages_pool, "");
+
+         append_u8 (&messages_pool, 0x05);
+         append_u32 (&messages_pool, hashcode);
+
+         append_u8 (&messages_pool, 0x01);
+
+         free (msgstr_as_utf16);
+         free (msgid_as_iso_8859_1);
+       }
+    }
+
+  /* Sort the hashes section.  */
+  {
+    size_t nstrings = obstack_object_size (&hashes_pool) / 8;
+    if (nstrings > 0)
+      qsort (obstack_base (&hashes_pool), nstrings, 8, cmp_hashes);
+  }
+
+  /* Write the magic number.  */
+  fwrite (magic, sizeof (magic), 1, output_file);
+
+  /* Write the hashes section.  */
+  write_section (output_file, 0x42, obstack_base (&hashes_pool),
+                obstack_object_size (&hashes_pool));
+
+  /* Write the messages section.  */
+  write_section (output_file, 0x69, obstack_base (&messages_pool),
+                obstack_object_size (&messages_pool));
+
+  /* Omit the contexts section.  */
+#if 0
+  write_section (output_file, 0x2f, ...);
+#endif
+
+  obstack_free (&messages_pool, NULL);
+  obstack_free (&hashes_pool, NULL);
+}
+
+
+int
+msgdomain_write_qt (message_list_ty *mlp, const char *canon_encoding,
+                   const char *domain_name, const char *file_name)
+{
+  FILE *output_file;
+
+  /* If no entry for this domain don't even create the file.  */
+  if (mlp->nitems != 0)
+    {
+      /* Determine whether mlp has plural entries.  */
+      {
+       bool has_plural;
+       size_t j;
+
+       has_plural = false;
+       for (j = 0; j < mlp->nitems; j++)
+         if (mlp->item[j]->msgid_plural != NULL)
+           has_plural = true;
+       if (has_plural)
+         {
+           multiline_error (xstrdup (""),
+                            xstrdup (_("\
+message catalog has plural form translations\n\
+but the Qt message catalog format doesn't support plural handling\n")));
+           return 1;
+         }
+      }
+
+      /* Convert the messages to Unicode.  */
+      iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
+
+      /* Determine whether mlp has non-ISO-8859-1 msgid entries.  */
+      {
+       size_t j;
+
+       for (j = 0; j < mlp->nitems; j++)
+         {
+           const char *string = mlp->item[j]->msgid;
+
+           /* An UTF-8 encoded string fits in ISO-8859-1 if and only if all
+              its bytes are < 0xc4.  */
+           for (; *string; string++)
+             if ((unsigned char) *string >= 0xc4)
+               {
+                 multiline_error (xstrdup (""),
+                                  xstrdup (_("\
+message catalog has msgid strings containing characters outside ISO-8859-1\n\
+but the Qt message catalog format supports Unicode only in the translated\n\
+strings, not in the untranslated strings\n")));
+                 return 1;
+               }
+         }
+      }
+
+      if (strcmp (domain_name, "-") == 0)
+       {
+         output_file = stdout;
+         SET_BINARY (fileno (output_file));
+       }
+      else
+       {
+         output_file = fopen (file_name, "wb");
+         if (output_file == NULL)
+           {
+             error (0, errno, _("error while opening \"%s\" for writing"),
+                    file_name);
+             return 1;
+           }
+       }
+
+      if (output_file != NULL)
+       {
+         write_qm (output_file, mlp);
+
+         /* Make sure nothing went wrong.  */
+         if (fwriteerror (output_file))
+           error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"),
+                  file_name);
+
+         if (output_file != stdout)
+           fclose (output_file);
+       }
+    }
+
+  return 0;
+}
diff --git a/gettext-tools/src/write-qt.h b/gettext-tools/src/write-qt.h
new file mode 100644 (file)
index 0000000..54c1ef4
--- /dev/null
@@ -0,0 +1,31 @@
+/* Writing Qt .qm files.
+   Copyright (C) 2003 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef _WRITE_QT_H
+#define _WRITE_QT_H
+
+#include "message.h"
+
+/* Write a Qt .qm file.  mlp is a list containing the messages to be output.
+   domain_name is the domain name, file_name is the desired file name.
+   Return 0 if ok, nonzero on error.  */
+extern int
+       msgdomain_write_qt (message_list_ty *mlp, const char *canon_encoding,
+                          const char *domain_name, const char *file_name);
+
+#endif /* _WRITE_QT_H */
index ae3d3f814c146ee71519024427cbd3abc376a4f9..0e1eabb50560df84cd6c77032467a59b96766f87 100644 (file)
@@ -149,6 +149,9 @@ 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;
 
+/* If true, recognize Qt format strings.  */
+static bool recognize_format_qt;
+
 /* Canonicalized encoding name for all input files.  */
 const char *xgettext_global_source_encoding;
 
@@ -200,6 +203,7 @@ static const struct option long_options[] =
   { "output", required_argument, NULL, 'o' },
   { "output-dir", required_argument, NULL, 'p' },
   { "properties-output", no_argument, NULL, CHAR_MAX + 6 },
+  { "qt", no_argument, NULL, CHAR_MAX + 9 },
   { "sort-by-file", no_argument, NULL, 'F' },
   { "sort-output", no_argument, NULL, 's' },
   { "strict", no_argument, NULL, 'S' },
@@ -262,6 +266,7 @@ main (int argc, char *argv[])
   const char *files_from = NULL;
   string_list_ty *file_list;
   char *output_file = NULL;
+  const char *language = NULL;
   extractor_ty extractor = { NULL, NULL, NULL, NULL };
 
   /* Set program name for messages.  */
@@ -335,7 +340,7 @@ main (int argc, char *argv[])
          }
        break;
       case 'C':
-       extractor = language_to_extractor ("C++");
+       language = "C++";
        break;
       case 'd':
        default_domain = optarg;
@@ -390,7 +395,7 @@ main (int argc, char *argv[])
        /* Accepted for backward compatibility with 0.10.35.  */
        break;
       case 'L':
-       extractor = language_to_extractor (optarg);
+       language = optarg;
        break;
       case 'm':
        /* -m takes an optional argument.  If none is given "" is assumed. */
@@ -471,6 +476,9 @@ main (int argc, char *argv[])
       case CHAR_MAX + 8:       /* --flag */
        xgettext_record_flag (optarg);
        break;
+      case CHAR_MAX + 9:       /* --qt */
+       recognize_format_qt = true;
+       break;
       default:
        usage (EXIT_FAILURE);
        /* NOTREACHED */
@@ -521,6 +529,10 @@ xgettext cannot work without keywords to look for"));
       usage (EXIT_FAILURE);
     }
 
+  /* Determine extractor from language.  */
+  if (language != NULL)
+    extractor = language_to_extractor (language);
+
   /* Canonize msgstr prefix/suffix.  */
   if (msgstr_prefix != NULL && msgstr_suffix == NULL)
     msgstr_suffix = "";
@@ -787,6 +799,10 @@ Language specific options:\n"));
       printf (_("\
                                 (only languages C, C++, ObjectiveC)\n"));
       printf (_("\
+      --qt                    recognize Qt format strings\n"));
+      printf (_("\
+                                (only language C++)\n"));
+      printf (_("\
       --debug                 more detailed formatstring recognition result\n"));
       printf ("\n");
       printf (_("\
@@ -1398,6 +1414,11 @@ xgettext_record_flag (const char *optionstring)
                                                    name_start, name_end,
                                                    argnum, value, pass);
                    break;
+                 case format_qt:
+                   flag_context_list_table_insert (&flag_table_c, 0,
+                                                   name_start, name_end,
+                                                   argnum, value, pass);
+                   break;
                  default:
                    abort ();
                  }
@@ -2115,6 +2136,12 @@ language_to_extractor (const char *name)
        result.formatstring_parser1 = tp->formatstring_parser1;
        result.formatstring_parser2 = tp->formatstring_parser2;
 
+       /* Handle --qt.  It's preferrable to handle this facility here rather
+          than through an option --language=C++/Qt because the latter would
+          conflict with the language "C++" regarding the file extensions.  */
+       if (recognize_format_qt && strcmp (tp->name, "C++") == 0)
+         result.formatstring_parser2 = &formatstring_qt;
+
        return result;
       }
 
index 64d7221ad3e1a99a20b4ed43d24e3811f9bdba95..9c70bc75723fd449ef57597ea88b0c313d5b3344 100644 (file)
@@ -1,3 +1,13 @@
+2003-10-19  Bruno Haible  <bruno@clisp.org>
+
+       * format-qt-1: New file.
+       * format-qt-2: New file.
+       * msgfmt-15: New file.
+       * qttest_pl.po: New file.
+       * qttest_pl.qm: New file.
+       * Makefile.am (TESTS): Add format-qt-1, format-qt-2, msgfmt-15.
+       (EXTRA_DIST): Add qttest_pl.po, qttest_pl.qm.
+
 2003-10-14  Bruno Haible  <bruno@clisp.org>
 
        * xgettext-22: Update expected result.
index 0fdb2897c79a59de8d04fe7d577ed7a1905465dc..d51e3a7cb0f3d96e0bcc93c21ed13b9440c23d6d 100644 (file)
@@ -37,6 +37,7 @@ TESTS = gettext-1 gettext-2 \
        msgfilter-1 msgfilter-2 msgfilter-3 \
        msgfmt-1 msgfmt-2 msgfmt-3 msgfmt-4 msgfmt-5 msgfmt-6 msgfmt-7 \
        msgfmt-8 msgfmt-9 msgfmt-10 msgfmt-11 msgfmt-12 msgfmt-13 msgfmt-14 \
+       msgfmt-15 \
        msggrep-1 msggrep-2 msggrep-3 msggrep-4 msggrep-5 msggrep-6 \
        msgmerge-1 msgmerge-2 msgmerge-3 msgmerge-4 msgmerge-5 msgmerge-6 \
        msgmerge-7 msgmerge-8 msgmerge-9 msgmerge-10 msgmerge-11 msgmerge-12 \
@@ -65,6 +66,7 @@ TESTS = gettext-1 gettext-2 \
        format-perl-1 format-perl-2 \
        format-perl-brace-1 format-perl-brace-2 \
        format-perl-mixed-1 format-perl-mixed-2 \
+       format-qt-1 format-qt-2 \
        format-sh-1 format-sh-2 \
        format-tcl-1 format-tcl-2 \
        format-ycp-1 format-ycp-2 \
@@ -76,7 +78,7 @@ TESTS = gettext-1 gettext-2 \
 
 EXTRA_DIST += $(TESTS) \
        test.mo xg-test1.ok.po mex-test2.ok msguniq-a.in msguniq-a.inp \
-       msguniq-a.out ChangeLog.0
+       msguniq-a.out qttest_pl.po qttest_pl.qm ChangeLog.0
 
 XGETTEXT = ../src/xgettext
 
diff --git a/gettext-tools/tests/format-qt-1 b/gettext-tools/tests/format-qt-1
new file mode 100755 (executable)
index 0000000..74df988
--- /dev/null
@@ -0,0 +1,65 @@
+#! /bin/sh
+
+# Test recognition of Qt format strings.
+
+tmpfiles=""
+trap 'rm -fr $tmpfiles' 1 2 3 15
+
+tmpfiles="$tmpfiles f-qt-1.data"
+cat <<\EOF > f-qt-1.data
+# Unrecognized: no argument
+"abc%%def"
+# Valid: one argument
+"abc%1def"
+# Valid: one argument
+"abc%9def"
+# Valid: unterminated
+"abc%1def%"
+# Valid: non-digit
+"abc%1def%x"
+# Valid: zero
+"abc%1def%0"
+# Valid: permutation
+"abc%2def%1"
+# Invalid: multiple uses of same argument
+"abc%2def%1ghi%2"
+EOF
+
+: ${XGETTEXT=xgettext}
+n=0
+while read comment; do
+  read string
+  n=`expr $n + 1`
+  tmpfiles="$tmpfiles f-qt-1-$n.in f-qt-1-$n.po"
+  cat <<EOF > f-qt-1-$n.in
+_(${string});
+EOF
+  ${XGETTEXT} -L C++ --qt -k_ -o f-qt-1-$n.po f-qt-1-$n.in || exit 1
+  test -f f-qt-1-$n.po || exit 1
+  fail=
+  if echo "$comment" | grep 'Valid:' > /dev/null; then
+    if grep qt-format f-qt-1-$n.po > /dev/null; then
+      :
+    else
+      fail=yes
+    fi
+  else
+    if grep qt-format f-qt-1-$n.po > /dev/null; then
+      fail=yes
+    else
+      :
+    fi
+  fi
+  if test -n "$fail"; then
+    echo "Format string recognition error:" 1>&2
+    cat f-qt-1-$n.in 1>&2
+    echo "Got:" 1>&2
+    cat f-qt-1-$n.po 1>&2
+    exit 1
+  fi
+  rm -f f-qt-1-$n.in f-qt-1-$n.po
+done < f-qt-1.data
+
+rm -fr $tmpfiles
+
+exit 0
diff --git a/gettext-tools/tests/format-qt-2 b/gettext-tools/tests/format-qt-2
new file mode 100755 (executable)
index 0000000..e011867
--- /dev/null
@@ -0,0 +1,73 @@
+#! /bin/sh
+
+# Test checking of Qt format strings.
+
+tmpfiles=""
+trap 'rm -fr $tmpfiles' 1 2 3 15
+
+tmpfiles="$tmpfiles f-qt-2.data"
+cat <<\EOF > f-qt-2.data
+# Valid: %% doesn't count
+msgid  "abc%%def"
+msgstr "xyz"
+# Invalid: invalid msgstr
+msgid  "abc%1def"
+msgstr "xyz%1%1"
+# Valid: same arguments
+msgid  "abc%2def"
+msgstr "xyz%2"
+# Valid: permutation
+msgid  "abc%3%1%2def"
+msgstr "xyz%2%1%3"
+# Invalid: too few arguments
+msgid  "abc%2def%1"
+msgstr "xyz%1"
+# Invalid: too many arguments
+msgid  "abc%1def"
+msgstr "xyz%1uvw%2"
+# Invalid: missing non-final argument
+msgid  "abc%2def%1"
+msgstr "xyz%2"
+# Invalid: added non-final argument
+msgid  "abc%2def"
+msgstr "xyz%1%2"
+EOF
+
+: ${MSGFMT=msgfmt}
+n=0
+while read comment; do
+  read msgid_line
+  read msgstr_line
+  n=`expr $n + 1`
+  tmpfiles="$tmpfiles f-qt-2-$n.po f-qt-2-$n.mo"
+  cat <<EOF > f-qt-2-$n.po
+#, qt-format
+${msgid_line}
+${msgstr_line}
+EOF
+  fail=
+  if echo "$comment" | grep 'Valid:' > /dev/null; then
+    if ${MSGFMT} --check-format -o f-qt-2-$n.mo f-qt-2-$n.po; then
+      :
+    else
+      fail=yes
+    fi
+  else
+    ${MSGFMT} --check-format -o f-qt-2-$n.mo f-qt-2-$n.po 2> /dev/null
+    if test $? = 1; then
+      :
+    else
+      fail=yes
+    fi
+  fi
+  if test -n "$fail"; then
+    echo "Format string checking error:" 1>&2
+    cat f-qt-2-$n.po 1>&2
+    exit 1
+  fi
+  rm -f f-qt-2-$n.po f-qt-2-$n.mo
+done < f-qt-2.data
+
+rm -fr $tmpfiles
+
+exit 0
diff --git a/gettext-tools/tests/msgfmt-15 b/gettext-tools/tests/msgfmt-15
new file mode 100755 (executable)
index 0000000..76158e9
--- /dev/null
@@ -0,0 +1,19 @@
+#! /bin/sh
+
+# Test output in Qt .qm format.
+
+tmpfiles=""
+trap 'rm -fr $tmpfiles' 1 2 3 15
+
+tmpfiles="$tmpfiles mf-test15.qm"
+: ${MSGFMT=msgfmt}
+${MSGFMT} --qt ${top_srcdir}/tests/qttest_pl.po -o mf-test15.qm
+test $? = 0 || { rm -fr $tmpfiles; exit 1; }
+
+: ${CMP=cmp}
+${CMP} ${top_srcdir}/tests/qttest_pl.qm mf-test15.qm >/dev/null 2>/dev/null
+result=$?
+
+rm -fr $tmpfiles
+
+exit $result
diff --git a/gettext-tools/tests/qttest_pl.po b/gettext-tools/tests/qttest_pl.po
new file mode 100644 (file)
index 0000000..d90ab8a
--- /dev/null
@@ -0,0 +1,26 @@
+# Polish translations for hello-cplusplus-qt package.
+# Copyright (C) 2003 Yoyodyne, Inc.
+# This file is distributed under the same license as the hello-cplusplus-qt package.
+# Bruno Haible <bruno@clisp.org>, 2003.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: hello-cplusplus-qt 0\n"
+"Report-Msgid-Bugs-To: bug-gnu-gettext@gnu.org\n"
+"POT-Creation-Date: 2003-10-20 10:14+0200\n"
+"PO-Revision-Date: 2003-10-20 10:13+0200\n"
+"Last-Translator: Bruno Haible <bruno@clisp.org>\n"
+"Language-Team: Polish <pl@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-2\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
+
+#: hello.cc:45
+msgid "Written by François Pinard."
+msgstr "Program napisa³ François Pinard."
+
+#: hello.cc:52
+msgid "error %1."
+msgstr "b³±d %1."
diff --git a/gettext-tools/tests/qttest_pl.qm b/gettext-tools/tests/qttest_pl.qm
new file mode 100644 (file)
index 0000000..2cdffcc
Binary files /dev/null and b/gettext-tools/tests/qttest_pl.qm differ