]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
xgettext: Split source code into smaller files.
authorBruno Haible <bruno@clisp.org>
Sun, 4 Nov 2018 19:20:19 +0000 (20:20 +0100)
committerBruno Haible <bruno@clisp.org>
Sun, 4 Nov 2018 23:25:33 +0000 (00:25 +0100)
* gettext-tools/src/rc-str-list.h: New file, extracted from
gettext-tools/src/xgettext.h.
* gettext-tools/src/xg-encoding.h: Likewise.
* gettext-tools/src/xg-mixed-string.h: Likewise.
* gettext-tools/src/xg-arglist-context.h: Likewise.
* gettext-tools/src/xg-arglist-callshape.h: Likewise.
* gettext-tools/src/xg-arglist-parser.h: Likewise.
* gettext-tools/src/xg-message.h: Likewise.
* gettext-tools/src/xg-encoding.c: New file, extracted from
gettext-tools/src/xgettext.c.
* gettext-tools/src/xg-mixed-string.c: Likewise.
* gettext-tools/src/xg-arglist-context.c: Likewise.
* gettext-tools/src/xg-arglist-callshape.c: Likewise.
* gettext-tools/src/xg-arglist-parser.c: Likewise.
* gettext-tools/src/xg-message.c: Likewise.
* gettext-tools/src/xgettext.h: Remove declarations that moved to rc-str-list.h
and xg-*.h.
(add_all_comments, comment_tag, msgstr_prefix, msgstr_suffix,
default_syntax_check, current_formatstring_parser1,
current_formatstring_parser2, current_formatstring_parser3): New declarations.
(xgettext_comment, xgettext_comment_reset, savable_comment_to_xgettext_comment,
recognize_qt_formatstrings): New declarations.
(substring_match): Remove obsolete declaration.
* gettext-tools/src/xgettext.c: Include xgettext.h first.
Include <iconv.h>, rc-str-list.h, xg-encoding.h, xg-arglist-context.h,
xg-message.h. Don't include xsize.h, po-xerror.h, unistr.h.
(add_all_comments, comment_tag, msgstr_prefix, msgstr_suffix,
default_syntax_check): Make global.
(split_keywordspec, insert_keyword_callshape): Moved to xg-arglist-callshape.c.
(null_context, passthrough_context, inherited_context,
null_context_list_iterator, passthrough_context_circular_list,
passthrough_context_list_iterator, flag_context_list_iterator,
flag_context_list_iterator_advance, flag_context_list_table_lookup): Move to
xg-arglist-context.c.
(flag_context_list_table_insert): Move bulk of body to new function
flag_context_list_table_add in xg-arglist-context.c.
(xgettext_comment, xgettext_comment_reset, savable_comment_to_xgettext_comment):
Make global.
(current_formatstring_parser1, current_formatstring_parser2,
current_formatstring_parser3): Make global.
(non_ascii_error_message, from_current_source_encoding): Move to xg-encoding.c.
(CONVERT_STRING, set_format_flags_from_context, warn_format_string,
remember_a_message, remember_a_message_plural): Move to xg-message.c.
(arglist_parser_alloc, arglist_parser_clone, arglist_parser_remember,
arglist_parser_remember_msgctxt, arglist_parser_decidedp, arglist_parser_done):
Move to xg-arglist-parser.c.
(mixed_string_buffer_alloc, mixed_string_buffer_append_to_curr_buffer,
mixed_string_buffer_grow_utf8_buffer, mixed_string_buffer_append_to_utf8_buffer,
mixed_string_buffer_flush_utf16_surr, mixed_string_buffer_flush_curr_buffer,
mixed_string_buffer_append_char, mixed_string_buffer_append_unicode,
mixed_string_buffer_done): Move to xg-mixed-string.c.
(recognize_qt_formatstrings): New function.
* gettext-tools/src/x-*.h: Include xg-arglist-context.h instead of xgettext.h.
* gettext-tools/src/x-*.c: Update include directives.
* gettext-tools/src/its.h: Likewise.
* gettext-tools/src/Makefile.am (noinst_HEADERS): Add the new .h files.
(xgettext_SOURCES): Add the new .c files.
* gettext-tools/src/FILES: Update.

62 files changed:
gettext-tools/src/FILES
gettext-tools/src/Makefile.am
gettext-tools/src/its.h
gettext-tools/src/rc-str-list.h [new file with mode: 0644]
gettext-tools/src/x-awk.c
gettext-tools/src/x-awk.h
gettext-tools/src/x-c.c
gettext-tools/src/x-c.h
gettext-tools/src/x-csharp.c
gettext-tools/src/x-csharp.h
gettext-tools/src/x-desktop.c
gettext-tools/src/x-desktop.h
gettext-tools/src/x-elisp.c
gettext-tools/src/x-elisp.h
gettext-tools/src/x-java.c
gettext-tools/src/x-java.h
gettext-tools/src/x-javascript.c
gettext-tools/src/x-javascript.h
gettext-tools/src/x-librep.c
gettext-tools/src/x-librep.h
gettext-tools/src/x-lisp.c
gettext-tools/src/x-lisp.h
gettext-tools/src/x-lua.c
gettext-tools/src/x-lua.h
gettext-tools/src/x-perl.c
gettext-tools/src/x-perl.h
gettext-tools/src/x-php.c
gettext-tools/src/x-php.h
gettext-tools/src/x-po.c
gettext-tools/src/x-po.h
gettext-tools/src/x-properties.h
gettext-tools/src/x-python.c
gettext-tools/src/x-python.h
gettext-tools/src/x-rst.c
gettext-tools/src/x-rst.h
gettext-tools/src/x-scheme.c
gettext-tools/src/x-scheme.h
gettext-tools/src/x-sh.c
gettext-tools/src/x-sh.h
gettext-tools/src/x-smalltalk.c
gettext-tools/src/x-smalltalk.h
gettext-tools/src/x-stringtable.h
gettext-tools/src/x-tcl.c
gettext-tools/src/x-tcl.h
gettext-tools/src/x-vala.c
gettext-tools/src/x-vala.h
gettext-tools/src/x-ycp.c
gettext-tools/src/x-ycp.h
gettext-tools/src/xg-arglist-callshape.c [new file with mode: 0644]
gettext-tools/src/xg-arglist-callshape.h [new file with mode: 0644]
gettext-tools/src/xg-arglist-context.c [new file with mode: 0644]
gettext-tools/src/xg-arglist-context.h [new file with mode: 0644]
gettext-tools/src/xg-arglist-parser.c [new file with mode: 0644]
gettext-tools/src/xg-arglist-parser.h [new file with mode: 0644]
gettext-tools/src/xg-encoding.c [new file with mode: 0644]
gettext-tools/src/xg-encoding.h [new file with mode: 0644]
gettext-tools/src/xg-message.c [new file with mode: 0644]
gettext-tools/src/xg-message.h [new file with mode: 0644]
gettext-tools/src/xg-mixed-string.c [new file with mode: 0644]
gettext-tools/src/xg-mixed-string.h [new file with mode: 0644]
gettext-tools/src/xgettext.c
gettext-tools/src/xgettext.h

index b647a84b90e4357e6e6754944b570cb17fb116c3..0a9bee97a4aac326430c0984e9899e436fec2a1c 100644 (file)
@@ -285,6 +285,30 @@ msgl-check.c
 +-------------- The 'xgettext' program
 | xgettext.h
 |               Declarations used by the backends.
+| rc-str-list.h
+|               A reference-counted list-of-immutable-strings type.
+| xg-encoding.h
+| xg-encoding.c
+|               Keeping track of the encoding of strings to be extracted.
+| xg-mixed-string.h
+| xg-mixed-string.c
+|               Handling strings that are given partially in the source
+|               encoding and partially in Unicode.
+| xg-arglist-context.h
+| xg-arglist-context.c
+|               Keeping track of the flags that apply to a string extracted
+|               in a certain context.
+| xg-arglist-callshape.h
+| xg-arglist-callshape.c
+|               Resolving ambiguity of argument lists: Information given
+|               through command-line options.
+| xg-arglist-parser.h
+| xg-arglist-parser.c
+|               Resolving ambiguity of argument lists: Progressive parsing
+|               of an argument list, keeping track of all possibilities.
+| xg-message.h
+| xg-message.c
+|               Extracting a message.  Accumulating the message list.
 | x-c.h
 | x-c.c
 |               String extractor for C.
index c94f7f3f305d908926886cba2472ee2c1c8872b2..bf5ea0e883dffc4702ad0edc71e4df4ee044a591 100644 (file)
@@ -52,7 +52,10 @@ write-qt.h \
 read-desktop.h write-desktop.h \
 write-xml.h \
 po-time.h plural-table.h lang-table.h format.h filters.h \
-xgettext.h x-c.h x-po.h x-sh.h x-python.h x-lisp.h x-elisp.h x-librep.h \
+xgettext.h \
+rc-str-list.h xg-encoding.h xg-mixed-string.h xg-arglist-context.h \
+xg-arglist-callshape.h xg-arglist-parser.h xg-message.h \
+x-c.h x-po.h x-sh.h x-python.h x-lisp.h x-elisp.h x-librep.h \
 x-scheme.h x-smalltalk.h x-java.h x-properties.h x-csharp.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 x-lua.h \
 x-javascript.h x-vala.h x-gsettings.h x-desktop.h x-appdata.h
@@ -177,6 +180,10 @@ else
 xgettext_SOURCES = ../woe32dll/c++xgettext.cc
 endif
 xgettext_SOURCES += \
+  xg-encoding.c \
+  xg-mixed-string.c \
+  xg-arglist-context.c xg-arglist-callshape.c xg-arglist-parser.c \
+  xg-message.c \
   x-c.c x-po.c x-sh.c x-python.c x-lisp.c x-elisp.c x-librep.c x-scheme.c \
   x-smalltalk.c x-java.c x-csharp.c x-awk.c x-ycp.c x-tcl.c x-perl.c x-php.c \
   x-rst.c x-lua.c x-javascript.c x-vala.c \
index 242f079a44b37a5f46cb8ccb9e31536d669f6d4d..72c30c9925becce2719996d7c50166f7c37d4657 100644 (file)
@@ -1,5 +1,5 @@
 /* Internationalization Tag Set (ITS) handling
-   Copyright (C) 2015-2016 Free Software Foundation, Inc.
+   Copyright (C) 2015, 2018 Free Software Foundation, Inc.
 
    This file was written by Daiki Ueno <ueno@gnu.org>, 2015.
 
 #ifndef _ITS_H_
 #define _ITS_H_
 
+#include <stdio.h>
+
 #include "message.h"
-#include "xgettext.h"
+#include "pos.h"
+#include "xg-arglist-context.h"
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/gettext-tools/src/rc-str-list.h b/gettext-tools/src/rc-str-list.h
new file mode 100644 (file)
index 0000000..3c42844
--- /dev/null
@@ -0,0 +1,67 @@
+/* Reference-counted string list.
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   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 3 of the License, 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, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _RC_STR_LIST_H
+#define _RC_STR_LIST_H
+
+#include <stdlib.h>
+
+#include "str-list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* A reference-counted list-of-immutable-strings type.  */
+
+typedef struct refcounted_string_list_ty refcounted_string_list_ty;
+struct refcounted_string_list_ty
+{
+  unsigned int refcount;
+  struct string_list_ty contents;
+};
+
+static inline refcounted_string_list_ty *
+add_reference (refcounted_string_list_ty *rslp)
+{
+  if (rslp != NULL)
+    rslp->refcount++;
+  return rslp;
+}
+
+static inline void
+drop_reference (refcounted_string_list_ty *rslp)
+{
+  if (rslp != NULL)
+    {
+      if (rslp->refcount > 1)
+        rslp->refcount--;
+      else
+        {
+          string_list_destroy (&rslp->contents);
+          free (rslp);
+        }
+    }
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _RC_STR_LIST_H */
index 36a8a4bdd8f1d93b7bd290871950f691ef2c3d60..b918b1b1777af7ca3a7faee5d3d83f1236665104 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext awk backend.
-   Copyright (C) 2002-2003, 2005-2009, 2015-2016 Free Software Foundation, Inc.
+   Copyright (C) 2002-2003, 2005-2009, 2018 Free Software Foundation, Inc.
 
    This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
 
 
 #include "message.h"
 #include "xgettext.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
 #include "error-progname.h"
 #include "xalloc.h"
index f818d5582919c033cad3154cbc2148243be7cffe..af35d3711ec19b252ff3523de349900de78afbc3 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 3522bc8afab28030ea82c6fce3cf8f68691c3b01..b13d77e7c2e819889bf104ad927b2348016646b8 100644 (file)
 #include <string.h>
 
 #include "message.h"
+#include "rc-str-list.h"
 #include "xgettext.h"
+#include "xg-encoding.h"
+#include "xg-mixed-string.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
 #include "error-progname.h"
 #include "xalloc.h"
index 5413aea6c9c20958366b410275011e3b1c29ef51..323c6187eea05c1d86d681039742b6c5a0dc4d3b 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index f34b92b2dd9e6aafb7057c2cf197c536d95b546a..8a26866010bc2f301a723c04be5e99180de5ed84 100644 (file)
@@ -1,6 +1,5 @@
 /* xgettext C# backend.
-   Copyright (C) 2003, 2005-2009, 2011, 2015-2016 Free Software Foundation,
-   Inc.
+   Copyright (C) 2003, 2005-2009, 2011, 2014, 2018 Free Software Foundation, Inc.
    Written by Bruno Haible <bruno@clisp.org>, 2003.
 
    This program is free software: you can redistribute it and/or modify
 #include <string.h>
 
 #include "message.h"
+#include "rc-str-list.h"
 #include "xgettext.h"
+#include "xg-encoding.h"
+#include "xg-mixed-string.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "c-ctype.h"
 #include "error.h"
 #include "error-progname.h"
index 27f0b024156e18723736a54e451c6fe6ded86da5..a8744fb60a3003146d88015f2776acbc9ab88726 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index c0b9fd284a076539725749805069acb7ac6cdd26..f0987918d118c57289006afb4b2802c2a2f41e18 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext Desktop Entry backend.
-   Copyright (C) 2014-2018 Free Software Foundation, Inc.
+   Copyright (C) 20142018 Free Software Foundation, Inc.
 
    This file was written by Daiki Ueno <ueno@gnu.org>, 2014.
 
@@ -31,6 +31,7 @@
 
 #include "message.h"
 #include "xgettext.h"
+#include "xg-message.h"
 #include "error.h"
 #include "error-progname.h"
 #include "xalloc.h"
index 47242e3a93cecc6efece795ba145b0a1a121421e..b606530559597651f2b20ad5d683b7941335f018 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 0de3807a15dbaaf158f708b85bf8038a3e5f4809..50582f22c7f857aa73dfcf965c32faebf64a956d 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext Emacs Lisp backend.
-   Copyright (C) 2001-2003, 2005-2009, 2015-2016 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003, 2005-2009, 2018 Free Software Foundation, Inc.
 
    This file was written by Bruno Haible <haible@clisp.cons.org>, 2001-2002.
 
 
 #include "message.h"
 #include "xgettext.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
 #include "xalloc.h"
 #include "hash.h"
index 99490c258e412b38110e8ad26175acb5085b3c8a..f3806de605c050730695dc94e1805b815f483797 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 8114e450a4ef603ff61419073a12a97af7813faf..3f65a5c1dcae9430c4ccd540feec715a1ebdb076 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext Java backend.
-   Copyright (C) 2003, 2005-2009, 2015-2016 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2005-2009, 2018 Free Software Foundation, Inc.
    Written by Bruno Haible <bruno@clisp.org>, 2003.
 
    This program is free software: you can redistribute it and/or modify
 #include <string.h>
 
 #include "message.h"
+#include "rc-str-list.h"
 #include "xgettext.h"
+#include "xg-encoding.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
+#include "error-progname.h"
 #include "xalloc.h"
 #include "hash.h"
 #include "po-charset.h"
index b2487e2dc9571a0a69d7de9a20a8404ca4be4b69..82a089278d403e18d19a6271be6ed33c7ee8ba92 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 09ddcfc175c3ab3dc0a734e1ca939a4f08ef808e..fc144a0364919c7944c6959cfbd49b3c73578b42 100644 (file)
@@ -1,6 +1,5 @@
 /* xgettext JavaScript backend.
-   Copyright (C) 2002-2003, 2005-2009, 2013, 2015-2018 Free Software
-   Foundation, Inc.
+   Copyright (C) 2002-2003, 2005-2009, 2013-2014, 2018 Free Software Foundation, Inc.
 
    This file was written by Andreas Stricker <andy@knitter.ch>, 2010
    It's based on x-python from Bruno Haible.
 #include <string.h>
 
 #include "message.h"
+#include "rc-str-list.h"
 #include "xgettext.h"
+#include "xg-encoding.h"
+#include "xg-mixed-string.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
 #include "error-progname.h"
 #include "progname.h"
index c59acc306676dfa110b8797fc490239f4d5cdcdc..6188750a50c0a3416143e71af07a5fea5660ba4c 100644 (file)
@@ -20,7 +20,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 90724b8828e0b4966f6bc3689c3b33e907678cf6..fb5601004334498102dfc97a9e919e04ac78669d 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext librep backend.
-   Copyright (C) 2001-2003, 2005-2009, 2015-2016 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003, 2005-2009, 2018 Free Software Foundation, Inc.
 
    This file was written by Bruno Haible <haible@clisp.cons.org>, 2001.
 
 #include "c-ctype.h"
 #include "message.h"
 #include "xgettext.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
 #include "xalloc.h"
 #include "hash.h"
index e1b735b6ede28947e893f4ee73b99367965f3aea..0da791f5eeea27679e736dfd06a5e333273d056c 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 5149b66c087129396c9fdf5c0006bcf647a2ad43..fd2802d6f9124e0fb5a850b1946f73c398386c2d 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext Lisp backend.
-   Copyright (C) 2001-2003, 2005-2009, 2015-2016 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003, 2005-2009, 2018 Free Software Foundation, Inc.
 
    This file was written by Bruno Haible <haible@clisp.cons.org>, 2001.
 
 
 #include "message.h"
 #include "xgettext.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
 #include "xalloc.h"
 #include "hash.h"
index ea7512395acdc3cae4fb1572a49aba61b66558b3..97b31dccf655504ef19f7a3de2262974fc59e09d 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 88d6319b68e02cfabc81868482ef01b66d7e14c4..bd98adf25ef3441c2cc683e1a7b250263e5e2bb3 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext Lua backend.
-   Copyright (C) 2012-2018 Free Software Foundation, Inc.
+   Copyright (C) 2012-2013, 2016, 2018 Free Software Foundation, Inc.
 
    This file was written by Ä½ubomír Remák <lubomirr@lubomirr.eu>, 2012.
 
 #include <stdlib.h>
 
 #include "message.h"
+#include "rc-str-list.h"
 #include "xgettext.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
 #include "xalloc.h"
 #include "gettext.h"
index e30c751e20c5e2e61e90bb6c503344e3a326cd0c..dca3016aa2c25de90fee3f2c711ef6c15da1ba30 100644 (file)
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
+
 
 #ifdef __cplusplus
 extern "C"
 {
 #endif
 
+
 #define EXTENSIONS_LUA \
   { "lua",    "Lua"   },                                                \
 
@@ -43,6 +45,7 @@ extern "C"
 
   extern void init_flag_table_lua (void);
 
+
 #ifdef __cplusplus
 }
 #endif
index 7256dcbe85c994801fc4cac99084c37f74804e9a..ebae6d16bba200b88ee0487aa9785ca3a1a03f33 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext Perl backend.
-   Copyright (C) 2002-2010, 2015-2018 Free Software Foundation, Inc.
+   Copyright (C) 2002-2010, 2013, 2016, 2018 Free Software Foundation, Inc.
 
    This file was written by Guido Flohr <guido@imperia.net>, 2002-2010.
 
 #include <string.h>
 
 #include "message.h"
+#include "rc-str-list.h"
 #include "xgettext.h"
+#include "xg-encoding.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
 #include "error-progname.h"
 #include "xalloc.h"
index 4832c9ba4a7afc0ab4eaa48917c781ebb4f7207a..b375714c377847921734f703601b3857bc7f9fe1 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 3ba171be8888b030a1362cf280b2cf0e7c07c141..dee1da4a4d3814ecc926bf5518951e0cb565e981 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext PHP backend.
-   Copyright (C) 2001-2003, 2005-2010, 2015-2016 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003, 2005-2010, 2014, 2018 Free Software Foundation, Inc.
 
    This file was written by Bruno Haible <bruno@clisp.org>, 2002.
 
 #include <stdlib.h>
 
 #include "message.h"
+#include "rc-str-list.h"
 #include "xgettext.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
 #include "xalloc.h"
 #include "gettext.h"
index 2a0c216f918c9de9961928857f20b6f6ba3c560d..f66abe64b1b97cbbc08aaf3304669e676bee9d7b 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index ef9df200ebd76e4a6897a7b754d5568cc35d8280..954dc96776873b3273e46730f9bc8196511ad84d 100644 (file)
@@ -1,6 +1,5 @@
 /* xgettext PO and JavaProperties backends.
-   Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2015-2016 Free
-   Software Foundation, Inc.
+   Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2014, 2018 Free Software Foundation, Inc.
 
    This file was written by Peter Miller <millerp@canb.auug.org.au>
 
index c2f8ead400cbbedf287b3bfb75e8118fe035fee1..41708db3636523e69d3078fc31ecdae1e5075fdb 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 09a422ce0bc1b97e22274dedfea89000abdab04d..20d486fcfa2dd1fd76d5599c34c2e498f1f89bde 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 94b3c30a51a8ecbc69b707e6203e90bb8637c344..86e1b355b7ba65625b8f1b329d68a1027c3622e9 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext Python backend.
-   Copyright (C) 2002-2003, 2005-2018 Free Software Foundation, Inc.
+   Copyright (C) 2002-2003, 2005-2011, 2013-2014, 2018 Free Software Foundation, Inc.
 
    This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
 
 #include <string.h>
 
 #include "message.h"
+#include "rc-str-list.h"
 #include "xgettext.h"
+#include "xg-encoding.h"
+#include "xg-mixed-string.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
 #include "error-progname.h"
 #include "progname.h"
index 8b0ff366c66499675c0f614b8a3abf2322acd5b6..d045ee603753e284da5b45b03e7b8ce9e278a8fa 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index b3cf3a95d771f31a45670576b4b4f0787f4312ab..512618d88a96d4295566af33fa26190b02cd548a 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext RST/RSJ backend.
-   Copyright (C) 2001-2003, 2005-2009, 2015-2016, 2018 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003, 2005-2009, 2018 Free Software Foundation, Inc.
 
    This file was written by Bruno Haible <haible@clisp.cons.org>, 2001.
 
@@ -33,6 +33,9 @@
 #include "po-charset.h"
 #include "message.h"
 #include "xgettext.h"
+#include "xg-encoding.h"
+#include "xg-mixed-string.h"
+#include "xg-message.h"
 #include "error.h"
 #include "error-progname.h"
 #include "xalloc.h"
index c470d0e4e51262ea73c257a1657bedfa8ee682f0..a5c2a049563fbe3bbac03aa0a1f65eff57a5346a 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index d04e655685d62567ecdd90b787c2bba51204d8e3..3faec32c3f0d7294cb21e3d2429017acf5644f7f 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext Scheme backend.
-   Copyright (C) 2004-2009, 2011, 2015-2018 Free Software Foundation, Inc.
+   Copyright (C) 2004-2009, 2011, 2014, 2018 Free Software Foundation, Inc.
 
    This file was written by Bruno Haible <bruno@clisp.org>, 2004-2005.
 
 
 #include "message.h"
 #include "xgettext.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
 #include "xalloc.h"
 #include "hash.h"
index b6ef0669a8680a8fc7f60347c78d0742d40044f2..6e5562b511595f0a57d968aa4424caa9b17f26c6 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 599f40b71bdf690dc1c1a9ca1aefaa1e8b674df3..43cfdfda3b8a2be2d2f664378ba372e71e4f1689 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext sh backend.
-   Copyright (C) 2003, 2005-2009, 2015-2016, 2018 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2005-2009, 2014, 2018 Free Software Foundation, Inc.
    Written by Bruno Haible <bruno@clisp.org>, 2003.
 
    This program is free software: you can redistribute it and/or modify
 
 #include "message.h"
 #include "xgettext.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
+#include "error-progname.h"
 #include "xalloc.h"
 #include "hash.h"
 #include "gettext.h"
index 4bda07d2f69293ad12401356ac2a85c13fce0663..44b7e67ee4850206ceb7684708bd6298afe73629 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index d240577cd2619cb1ae4038dfdcbe9c4d1a3b232c..628a5aac83e52080fa6105a134622fed072310ed 100644 (file)
@@ -1,6 +1,5 @@
 /* xgettext Smalltalk backend.
-   Copyright (C) 2002-2003, 2005-2009, 2011, 2015-2016 Free Software
-   Foundation, Inc.
+   Copyright (C) 2002-2003, 2005-2009, 2011, 2018 Free Software Foundation, Inc.
 
    This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
 
@@ -30,6 +29,7 @@
 
 #include "message.h"
 #include "xgettext.h"
+#include "xg-message.h"
 #include "error.h"
 #include "xalloc.h"
 #include "gettext.h"
index f851b56073d053e051c3aba5d5a5aeb7a08644ee..14bd5efe59730b0c5f71bc052e1075bca4167be3 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index e9ec1755f320a57deb376fe436a6d94df6b45c78..75505c48cbc942f94b5f68d0a9cf463bf9fe2bba 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 7da832814f2680db954d57a2fe95f6f622e28b28..5517bc7ace8c517877f542dbe97362d5ad328724 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext Tcl backend.
-   Copyright (C) 2002-2003, 2005-2009, 2015-2016 Free Software Foundation, Inc.
+   Copyright (C) 2002-2003, 2005-2009, 2013, 2018 Free Software Foundation, Inc.
 
    This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
 
 
 #include "message.h"
 #include "xgettext.h"
+#include "xg-encoding.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
 #include "xalloc.h"
 #include "hash.h"
index e802950eba65bd08e7a30de12903d2d0d8ae773d..3efb09fffe29f9979b6ffe4f285d183d9f43f96a 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 7b9ae761f2add5b1618de29b2d6e67febf6c9873..b74b834de334b576538a2b1ecbf4ef444aa88c4f 100644 (file)
 #include <string.h>
 
 #include "message.h"
+#include "rc-str-list.h"
 #include "xgettext.h"
+#include "xg-encoding.h"
+#include "xg-mixed-string.h"
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+#include "xg-arglist-parser.h"
+#include "xg-message.h"
 #include "error.h"
 #include "error-progname.h"
 #include "xalloc.h"
index 47ef5b5d8c45ea0235c7b0bebabb260d88883cb8..686283422554d1a2c203dba8f8c77c919f4ca3b8 100644 (file)
@@ -18,7 +18,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
index 019e85d958c0e7f83a60e5b5794e534b1fb9742e..3dc7b50a66a94cb3c5106a990b3298e22cf739cb 100644 (file)
@@ -1,6 +1,5 @@
 /* xgettext YCP backend.
-   Copyright (C) 2001-2003, 2005-2009, 2011, 2015-2016 Free Software
-   Foundation, Inc.
+   Copyright (C) 2001-2003, 2005-2009, 2011, 2018 Free Software Foundation, Inc.
 
    This file was written by Bruno Haible <haible@clisp.cons.org>, 2001.
 
 #include <stdlib.h>
 
 #include "message.h"
+#include "rc-str-list.h"
 #include "xgettext.h"
+#include "xg-arglist-context.h"
+#include "xg-message.h"
 #include "error.h"
 #include "xalloc.h"
 #include "gettext.h"
index b6d139a2282fc6f7e43d7e7a2bfeffdc5df31520..d6f836b6b3d936a6356e45283a006574ffee14b5 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
 
 
 #ifdef __cplusplus
diff --git a/gettext-tools/src/xg-arglist-callshape.c b/gettext-tools/src/xg-arglist-callshape.c
new file mode 100644 (file)
index 0000000..06b7076
--- /dev/null
@@ -0,0 +1,239 @@
+/* Resolving ambiguity of argument lists: Information given through
+   command-line options.
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   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 3 of the License, 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, see <https://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification.  */
+#include "xg-arglist-callshape.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xalloc.h"
+#include "xsize.h"
+
+
+void
+split_keywordspec (const char *spec,
+                   const char **endp, struct callshape *shapep)
+{
+  const char *p;
+  int argnum1 = 0;
+  int argnum2 = 0;
+  int argnumc = 0;
+  bool argnum1_glib_context = false;
+  bool argnum2_glib_context = false;
+  int argtotal = 0;
+  string_list_ty xcomments;
+
+  string_list_init (&xcomments);
+
+  /* Start parsing from the end.  */
+  p = spec + strlen (spec);
+  while (p > spec)
+    {
+      if (isdigit ((unsigned char) p[-1])
+          || ((p[-1] == 'c' || p[-1] == 'g' || p[-1] == 't')
+              && p - 1 > spec && isdigit ((unsigned char) p[-2])))
+        {
+          bool contextp = (p[-1] == 'c');
+          bool glibp = (p[-1] == 'g');
+          bool totalp = (p[-1] == 't');
+
+          do
+            p--;
+          while (p > spec && isdigit ((unsigned char) p[-1]));
+
+          if (p > spec && (p[-1] == ',' || p[-1] == ':'))
+            {
+              char *dummy;
+              int arg = strtol (p, &dummy, 10);
+
+              if (contextp)
+                {
+                  if (argnumc != 0)
+                    /* Only one context argument can be given.  */
+                    break;
+                  argnumc = arg;
+                }
+              else if (totalp)
+                {
+                  if (argtotal != 0)
+                    /* Only one total number of arguments can be given.  */
+                    break;
+                  argtotal = arg;
+                }
+              else
+                {
+                  if (argnum2 != 0)
+                    /* At most two normal arguments can be given.  */
+                    break;
+                  argnum2 = argnum1;
+                  argnum2_glib_context = argnum1_glib_context;
+                  argnum1 = arg;
+                  argnum1_glib_context = glibp;
+                }
+            }
+          else
+            break;
+        }
+      else if (p[-1] == '"')
+        {
+          const char *xcomment_end;
+
+          p--;
+          xcomment_end = p;
+
+          while (p > spec && p[-1] != '"')
+            p--;
+
+          if (p > spec /* && p[-1] == '"' */)
+            {
+              const char *xcomment_start;
+
+              xcomment_start = p;
+              p--;
+              if (p > spec && (p[-1] == ',' || p[-1] == ':'))
+                {
+                  size_t xcomment_len = xcomment_end - xcomment_start;
+                  char *xcomment = XNMALLOC (xcomment_len + 1, char);
+
+                  memcpy (xcomment, xcomment_start, xcomment_len);
+                  xcomment[xcomment_len] = '\0';
+                  string_list_append (&xcomments, xcomment);
+                }
+              else
+                break;
+            }
+          else
+            break;
+        }
+      else
+        break;
+
+      /* Here an element of the comma-separated list has been parsed.  */
+      if (!(p > spec && (p[-1] == ',' || p[-1] == ':')))
+        abort ();
+      p--;
+      if (*p == ':')
+        {
+          size_t i;
+
+          if (argnum1 == 0 && argnum2 == 0)
+            /* At least one non-context argument must be given.  */
+            break;
+          if (argnumc != 0
+              && (argnum1_glib_context || argnum2_glib_context))
+            /* Incompatible ways to specify the context.  */
+            break;
+          *endp = p;
+          shapep->argnum1 = (argnum1 > 0 ? argnum1 : 1);
+          shapep->argnum2 = argnum2;
+          shapep->argnumc = argnumc;
+          shapep->argnum1_glib_context = argnum1_glib_context;
+          shapep->argnum2_glib_context = argnum2_glib_context;
+          shapep->argtotal = argtotal;
+          /* Reverse the order of the xcomments.  */
+          string_list_init (&shapep->xcomments);
+          for (i = xcomments.nitems; i > 0; )
+            string_list_append (&shapep->xcomments, xcomments.item[--i]);
+          string_list_destroy (&xcomments);
+          return;
+        }
+    }
+
+  /* Couldn't parse the desired syntax.  */
+  *endp = spec + strlen (spec);
+  shapep->argnum1 = 1;
+  shapep->argnum2 = 0;
+  shapep->argnumc = 0;
+  shapep->argnum1_glib_context = false;
+  shapep->argnum2_glib_context = false;
+  shapep->argtotal = 0;
+  string_list_init (&shapep->xcomments);
+  string_list_destroy (&xcomments);
+}
+
+
+void
+insert_keyword_callshape (hash_table *table,
+                          const char *keyword, size_t keyword_len,
+                          const struct callshape *shape)
+{
+  void *old_value;
+
+  if (hash_find_entry (table, keyword, keyword_len, &old_value))
+    {
+      /* Create a one-element 'struct callshapes'.  */
+      struct callshapes *shapes = XMALLOC (struct callshapes);
+      shapes->nshapes = 1;
+      shapes->shapes[0] = *shape;
+      keyword =
+        (const char *) hash_insert_entry (table, keyword, keyword_len, shapes);
+      if (keyword == NULL)
+        abort ();
+      shapes->keyword = keyword;
+      shapes->keyword_len = keyword_len;
+    }
+  else
+    {
+      /* Found a 'struct callshapes'.  See whether it already contains the
+         desired shape.  */
+      struct callshapes *old_shapes = (struct callshapes *) old_value;
+      bool found;
+      size_t i;
+
+      found = false;
+      for (i = 0; i < old_shapes->nshapes; i++)
+        if (old_shapes->shapes[i].argnum1 == shape->argnum1
+            && old_shapes->shapes[i].argnum2 == shape->argnum2
+            && old_shapes->shapes[i].argnumc == shape->argnumc
+            && old_shapes->shapes[i].argnum1_glib_context
+               == shape->argnum1_glib_context
+            && old_shapes->shapes[i].argnum2_glib_context
+               == shape->argnum2_glib_context
+            && old_shapes->shapes[i].argtotal == shape->argtotal)
+          {
+            old_shapes->shapes[i].xcomments = shape->xcomments;
+            found = true;
+            break;
+          }
+
+      if (!found)
+        {
+          /* Replace the existing 'struct callshapes' with a new one.  */
+          struct callshapes *shapes =
+            (struct callshapes *)
+            xmalloc (xsum (sizeof (struct callshapes),
+                           xtimes (old_shapes->nshapes,
+                                   sizeof (struct callshape))));
+
+          shapes->keyword = old_shapes->keyword;
+          shapes->keyword_len = old_shapes->keyword_len;
+          shapes->nshapes = old_shapes->nshapes + 1;
+          for (i = 0; i < old_shapes->nshapes; i++)
+            shapes->shapes[i] = old_shapes->shapes[i];
+          shapes->shapes[i] = *shape;
+          if (hash_set_value (table, keyword, keyword_len, shapes))
+            abort ();
+          free (old_shapes);
+        }
+    }
+}
diff --git a/gettext-tools/src/xg-arglist-callshape.h b/gettext-tools/src/xg-arglist-callshape.h
new file mode 100644 (file)
index 0000000..6659f45
--- /dev/null
@@ -0,0 +1,69 @@
+/* Resolving ambiguity of argument lists: Information given through
+   command-line options.
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   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 3 of the License, 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, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _XGETTEXT_ARGLIST_CALLSHAPE_H
+#define _XGETTEXT_ARGLIST_CALLSHAPE_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "str-list.h"
+#include "hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Calling convention for a given keyword.  */
+struct callshape
+{
+  int argnum1; /* argument number to use for msgid */
+  int argnum2; /* argument number to use for msgid_plural */
+  int argnumc; /* argument number to use for msgctxt */
+  bool argnum1_glib_context; /* argument argnum1 has the syntax "ctxt|msgid" */
+  bool argnum2_glib_context; /* argument argnum2 has the syntax "ctxt|msgid" */
+  int argtotal; /* total number of arguments */
+  string_list_ty xcomments; /* auto-extracted comments */
+};
+
+/* Split keyword spec into keyword, argnum1, argnum2, argnumc.  */
+extern void split_keywordspec (const char *spec, const char **endp,
+                               struct callshape *shapep);
+
+/* Set of alternative calling conventions for a given keyword.  */
+struct callshapes
+{
+  const char *keyword;          /* the keyword, not NUL terminated */
+  size_t keyword_len;           /* the keyword's length */
+  size_t nshapes;
+  struct callshape shapes[1];   /* actually nshapes elements */
+};
+
+/* Insert a (keyword, callshape) pair into a hash table mapping keyword to
+   'struct callshapes *'.  */
+extern void insert_keyword_callshape (hash_table *table,
+                                      const char *keyword, size_t keyword_len,
+                                      const struct callshape *shape);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _XGETTEXT_ARGLIST_CALLSHAPE_H */
diff --git a/gettext-tools/src/xg-arglist-context.c b/gettext-tools/src/xg-arglist-context.c
new file mode 100644 (file)
index 0000000..68feba5
--- /dev/null
@@ -0,0 +1,260 @@
+/* Keeping track of the flags that apply to a string extracted
+   in a certain context.
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   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 3 of the License, 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, see <https://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification.  */
+#include "xg-arglist-context.h"
+
+#include <stdlib.h>
+
+#include "xalloc.h"
+#include "xmalloca.h"
+
+
+/* Null context.  */
+flag_context_ty null_context = { undecided, false, undecided, false };
+
+/* Transparent context.  */
+flag_context_ty passthrough_context = { undecided, true, undecided, true };
+
+
+flag_context_ty
+inherited_context (flag_context_ty outer_context,
+                   flag_context_ty modifier_context)
+{
+  flag_context_ty result = modifier_context;
+
+  if (result.pass_format1)
+    {
+      result.is_format1 = outer_context.is_format1;
+      result.pass_format1 = false;
+    }
+  if (result.pass_format2)
+    {
+      result.is_format2 = outer_context.is_format2;
+      result.pass_format2 = false;
+    }
+  if (result.pass_format3)
+    {
+      result.is_format3 = outer_context.is_format3;
+      result.pass_format3 = false;
+    }
+  return result;
+}
+
+
+/* Null context list iterator.  */
+flag_context_list_iterator_ty null_context_list_iterator = { 1, NULL };
+
+/* Transparent context list iterator.  */
+static flag_context_list_ty passthrough_context_circular_list =
+  {
+    1,
+    { undecided, true, undecided, true },
+    &passthrough_context_circular_list
+  };
+flag_context_list_iterator_ty passthrough_context_list_iterator =
+  {
+    1,
+    &passthrough_context_circular_list
+  };
+
+
+flag_context_list_iterator_ty
+flag_context_list_iterator (flag_context_list_ty *list)
+{
+  flag_context_list_iterator_ty result;
+
+  result.argnum = 1;
+  result.head = list;
+  return result;
+}
+
+
+flag_context_ty
+flag_context_list_iterator_advance (flag_context_list_iterator_ty *iter)
+{
+  if (iter->head == NULL)
+    return null_context;
+  if (iter->argnum == iter->head->argnum)
+    {
+      flag_context_ty result = iter->head->flags;
+
+      /* Special casing of circular list.  */
+      if (iter->head != iter->head->next)
+        {
+          iter->head = iter->head->next;
+          iter->argnum++;
+        }
+
+      return result;
+    }
+  else
+    {
+      iter->argnum++;
+      return null_context;
+    }
+}
+
+
+flag_context_list_ty *
+flag_context_list_table_lookup (flag_context_list_table_ty *flag_table,
+                                const void *key, size_t keylen)
+{
+  void *entry;
+
+  if (flag_table->table != NULL
+      && hash_find_entry (flag_table, key, keylen, &entry) == 0)
+    return (flag_context_list_ty *) entry;
+  else
+    return NULL;
+}
+
+
+void
+flag_context_list_table_add (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)
+{
+  /* 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)
+    hash_init (table, 100);
+  {
+    void *entry;
+
+    if (hash_find_entry (table, name_start, name_end - name_start, &entry) != 0)
+      {
+        /* Create new hash table entry.  */
+        flag_context_list_ty *list = XMALLOC (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;
+          case 2:
+            list->flags.is_format3 = value;
+            list->flags.pass_format3 = pass;
+            break;
+          default:
+            abort ();
+          }
+        list->next = NULL;
+        hash_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;
+        /* Invariant: list == (lastp != NULL ? *lastp : entry).  */
+
+        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;
+              case 2:
+                list->flags.is_format3 = value;
+                list->flags.pass_format3 = pass;
+                break;
+              default:
+                abort ();
+              }
+          }
+        else if (lastp != NULL)
+          {
+            /* Add a new list entry for this argument number.  */
+            list = XMALLOC (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;
+              case 2:
+                list->flags.is_format3 = value;
+                list->flags.pass_format3 = 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 = XMALLOC (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;
+              case 2:
+                list->flags.is_format3 = value;
+                list->flags.pass_format3 = pass;
+                break;
+              default:
+                abort ();
+              }
+            list->next = copy;
+          }
+      }
+  }
+}
diff --git a/gettext-tools/src/xg-arglist-context.h b/gettext-tools/src/xg-arglist-context.h
new file mode 100644 (file)
index 0000000..16508c3
--- /dev/null
@@ -0,0 +1,99 @@
+/* Keeping track of the flags that apply to a string extracted
+   in a certain context.
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   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 3 of the License, 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, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _XGETTEXT_ARGLIST_CONTEXT_H
+#define _XGETTEXT_ARGLIST_CONTEXT_H
+
+#include <stdbool.h>
+
+#include "hash.h"
+#include "message.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Context representing some flags.  */
+typedef struct flag_context_ty flag_context_ty;
+struct flag_context_ty
+{
+  /* Regarding the primary formatstring type.  */
+  /*enum is_format*/ unsigned int is_format1    : 3;
+  /*bool*/           unsigned int pass_format1  : 1;
+  /* Regarding the secondary formatstring type.  */
+  /*enum is_format*/ unsigned int is_format2    : 3;
+  /*bool*/           unsigned int pass_format2  : 1;
+  /* Regarding the tertiary formatstring type.  */
+  /*enum is_format*/ unsigned int is_format3    : 3;
+  /*bool*/           unsigned int pass_format3  : 1;
+};
+/* Null context.  */
+extern flag_context_ty null_context;
+/* Transparent context.  */
+extern flag_context_ty passthrough_context;
+/* Compute an inherited context.
+   The outer_context is assumed to have all pass_format* flags = false.
+   The result will then also have all pass_format* flags = false.  */
+extern flag_context_ty
+       inherited_context (flag_context_ty outer_context,
+                          flag_context_ty modifier_context);
+
+/* Context representing some flags, for each possible argument number.
+   This is a linked list, sorted according to the argument number.  */
+typedef struct flag_context_list_ty flag_context_list_ty;
+struct flag_context_list_ty
+{
+  int argnum;                   /* current argument number, > 0 */
+  flag_context_ty flags;        /* flags for current argument */
+  flag_context_list_ty *next;
+};
+
+/* Iterator through a flag_context_list_ty.  */
+typedef struct flag_context_list_iterator_ty flag_context_list_iterator_ty;
+struct flag_context_list_iterator_ty
+{
+  int argnum;                           /* current argument number, > 0 */
+  const flag_context_list_ty* head;     /* tail of list */
+};
+extern flag_context_list_iterator_ty null_context_list_iterator;
+extern flag_context_list_iterator_ty passthrough_context_list_iterator;
+extern flag_context_list_iterator_ty
+       flag_context_list_iterator (flag_context_list_ty *list);
+extern flag_context_ty
+       flag_context_list_iterator_advance (flag_context_list_iterator_ty *iter);
+
+/* For nearly each backend, we have a separate table mapping a keyword to
+   a flag_context_list_ty *.  */
+typedef hash_table /* char[] -> flag_context_list_ty * */
+        flag_context_list_table_ty;
+extern flag_context_list_ty *
+       flag_context_list_table_lookup (flag_context_list_table_ty *flag_table,
+                                       const void *key, size_t keylen);
+extern void
+       flag_context_list_table_add (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);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _XGETTEXT_ARGLIST_CONTEXT_H */
diff --git a/gettext-tools/src/xg-arglist-parser.c b/gettext-tools/src/xg-arglist-parser.c
new file mode 100644 (file)
index 0000000..bb97b41
--- /dev/null
@@ -0,0 +1,539 @@
+/* Resolving ambiguity of argument lists: Progressive parsing of an
+   argument list, keeping track of all possibilities.
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   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 3 of the License, 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, see <https://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification.  */
+#include "xg-arglist-parser.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "error-progname.h"
+#include "xalloc.h"
+#include "xsize.h"
+
+#include "xgettext.h"
+#include "xg-message.h"
+
+#include "gettext.h"
+#define _(str) gettext (str)
+
+
+struct arglist_parser *
+arglist_parser_alloc (message_list_ty *mlp, const struct callshapes *shapes)
+{
+  if (shapes == NULL || shapes->nshapes == 0)
+    {
+      struct arglist_parser *ap =
+        (struct arglist_parser *)
+        xmalloc (offsetof (struct arglist_parser, alternative[0]));
+
+      ap->mlp = mlp;
+      ap->keyword = NULL;
+      ap->keyword_len = 0;
+      ap->next_is_msgctxt = false;
+      ap->nalternatives = 0;
+
+      return ap;
+    }
+  else
+    {
+      struct arglist_parser *ap =
+        (struct arglist_parser *)
+        xmalloc (xsum (sizeof (struct arglist_parser),
+                       xtimes (shapes->nshapes - 1,
+                               sizeof (struct partial_call))));
+      size_t i;
+
+      ap->mlp = mlp;
+      ap->keyword = shapes->keyword;
+      ap->keyword_len = shapes->keyword_len;
+      ap->next_is_msgctxt = false;
+      ap->nalternatives = shapes->nshapes;
+      for (i = 0; i < shapes->nshapes; i++)
+        {
+          ap->alternative[i].argnumc = shapes->shapes[i].argnumc;
+          ap->alternative[i].argnum1 = shapes->shapes[i].argnum1;
+          ap->alternative[i].argnum2 = shapes->shapes[i].argnum2;
+          ap->alternative[i].argnum1_glib_context =
+            shapes->shapes[i].argnum1_glib_context;
+          ap->alternative[i].argnum2_glib_context =
+            shapes->shapes[i].argnum2_glib_context;
+          ap->alternative[i].argtotal = shapes->shapes[i].argtotal;
+          ap->alternative[i].xcomments = shapes->shapes[i].xcomments;
+          ap->alternative[i].msgctxt = NULL;
+          ap->alternative[i].msgctxt_pos.file_name = NULL;
+          ap->alternative[i].msgctxt_pos.line_number = (size_t)(-1);
+          ap->alternative[i].msgid = NULL;
+          ap->alternative[i].msgid_context = null_context;
+          ap->alternative[i].msgid_pos.file_name = NULL;
+          ap->alternative[i].msgid_pos.line_number = (size_t)(-1);
+          ap->alternative[i].msgid_comment = NULL;
+          ap->alternative[i].msgid_plural = NULL;
+          ap->alternative[i].msgid_plural_context = null_context;
+          ap->alternative[i].msgid_plural_pos.file_name = NULL;
+          ap->alternative[i].msgid_plural_pos.line_number = (size_t)(-1);
+        }
+
+      return ap;
+    }
+}
+
+
+struct arglist_parser *
+arglist_parser_clone (struct arglist_parser *ap)
+{
+  struct arglist_parser *copy =
+    (struct arglist_parser *)
+    xmalloc (xsum (sizeof (struct arglist_parser) - sizeof (struct partial_call),
+                   xtimes (ap->nalternatives, sizeof (struct partial_call))));
+  size_t i;
+
+  copy->mlp = ap->mlp;
+  copy->keyword = ap->keyword;
+  copy->keyword_len = ap->keyword_len;
+  copy->next_is_msgctxt = ap->next_is_msgctxt;
+  copy->nalternatives = ap->nalternatives;
+  for (i = 0; i < ap->nalternatives; i++)
+    {
+      const struct partial_call *cp = &ap->alternative[i];
+      struct partial_call *ccp = &copy->alternative[i];
+
+      ccp->argnumc = cp->argnumc;
+      ccp->argnum1 = cp->argnum1;
+      ccp->argnum2 = cp->argnum2;
+      ccp->argnum1_glib_context = cp->argnum1_glib_context;
+      ccp->argnum2_glib_context = cp->argnum2_glib_context;
+      ccp->argtotal = cp->argtotal;
+      ccp->xcomments = cp->xcomments;
+      ccp->msgctxt = (cp->msgctxt != NULL ? xstrdup (cp->msgctxt) : NULL);
+      ccp->msgctxt_pos = cp->msgctxt_pos;
+      ccp->msgid = (cp->msgid != NULL ? xstrdup (cp->msgid) : NULL);
+      ccp->msgid_context = cp->msgid_context;
+      ccp->msgid_pos = cp->msgctxt_pos;
+      ccp->msgid_comment = add_reference (cp->msgid_comment);
+      ccp->msgid_plural =
+        (cp->msgid_plural != NULL ? xstrdup (cp->msgid_plural) : NULL);
+      ccp->msgid_plural_context = cp->msgid_plural_context;
+      ccp->msgid_plural_pos = cp->msgid_plural_pos;
+    }
+
+  return copy;
+}
+
+
+void
+arglist_parser_remember (struct arglist_parser *ap,
+                         int argnum, char *string,
+                         flag_context_ty context,
+                         char *file_name, size_t line_number,
+                         refcounted_string_list_ty *comment)
+{
+  bool stored_string = false;
+  size_t nalternatives = ap->nalternatives;
+  size_t i;
+
+  if (!(argnum > 0))
+    abort ();
+  for (i = 0; i < nalternatives; i++)
+    {
+      struct partial_call *cp = &ap->alternative[i];
+
+      if (argnum == cp->argnumc)
+        {
+          cp->msgctxt = string;
+          cp->msgctxt_pos.file_name = file_name;
+          cp->msgctxt_pos.line_number = line_number;
+          stored_string = true;
+          /* Mark msgctxt as done.  */
+          cp->argnumc = 0;
+        }
+      else
+        {
+          if (argnum == cp->argnum1)
+            {
+              cp->msgid = string;
+              cp->msgid_context = context;
+              cp->msgid_pos.file_name = file_name;
+              cp->msgid_pos.line_number = line_number;
+              cp->msgid_comment = add_reference (comment);
+              stored_string = true;
+              /* Mark msgid as done.  */
+              cp->argnum1 = 0;
+            }
+          if (argnum == cp->argnum2)
+            {
+              cp->msgid_plural = string;
+              cp->msgid_plural_context = context;
+              cp->msgid_plural_pos.file_name = file_name;
+              cp->msgid_plural_pos.line_number = line_number;
+              stored_string = true;
+              /* Mark msgid_plural as done.  */
+              cp->argnum2 = 0;
+            }
+        }
+    }
+  /* Note: There is a memory leak here: When string was stored but is later
+     not used by arglist_parser_done, we don't free it.  */
+  if (!stored_string)
+    free (string);
+}
+
+
+void
+arglist_parser_remember_msgctxt (struct arglist_parser *ap,
+                                 char *string,
+                                 flag_context_ty context,
+                                 char *file_name, size_t line_number)
+{
+  bool stored_string = false;
+  size_t nalternatives = ap->nalternatives;
+  size_t i;
+
+  for (i = 0; i < nalternatives; i++)
+    {
+      struct partial_call *cp = &ap->alternative[i];
+
+      cp->msgctxt = string;
+      cp->msgctxt_pos.file_name = file_name;
+      cp->msgctxt_pos.line_number = line_number;
+      stored_string = true;
+      /* Mark msgctxt as done.  */
+      cp->argnumc = 0;
+    }
+  /* Note: There is a memory leak here: When string was stored but is later
+     not used by arglist_parser_done, we don't free it.  */
+  if (!stored_string)
+    free (string);
+}
+
+
+bool
+arglist_parser_decidedp (struct arglist_parser *ap, int argnum)
+{
+  size_t i;
+
+  /* Test whether all alternatives are decided.
+     Note: A decided alternative can be complete
+       cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0
+       && cp->argtotal == 0
+     or it can be failed if no literal strings were found at the specified
+     argument positions:
+       cp->argnumc <= argnum && cp->argnum1 <= argnum && cp->argnum2 <= argnum
+     or it can be failed if the number of arguments is exceeded:
+       cp->argtotal > 0 && cp->argtotal < argnum
+   */
+  for (i = 0; i < ap->nalternatives; i++)
+    {
+      struct partial_call *cp = &ap->alternative[i];
+
+      if (!((cp->argnumc <= argnum
+             && cp->argnum1 <= argnum
+             && cp->argnum2 <= argnum)
+            || (cp->argtotal > 0 && cp->argtotal < argnum)))
+        /* cp is still undecided.  */
+        return false;
+    }
+  return true;
+}
+
+
+void
+arglist_parser_done (struct arglist_parser *ap, int argnum)
+{
+  size_t ncomplete;
+  size_t i;
+
+  /* Determine the number of complete calls.  */
+  ncomplete = 0;
+  for (i = 0; i < ap->nalternatives; i++)
+    {
+      struct partial_call *cp = &ap->alternative[i];
+
+      if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0
+          && (cp->argtotal == 0 || cp->argtotal == argnum))
+        ncomplete++;
+    }
+
+  if (ncomplete > 0)
+    {
+      struct partial_call *best_cp = NULL;
+      bool ambiguous = false;
+
+      /* Find complete calls where msgctxt, msgid, msgid_plural are all
+         provided.  */
+      for (i = 0; i < ap->nalternatives; i++)
+        {
+          struct partial_call *cp = &ap->alternative[i];
+
+          if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0
+              && (cp->argtotal == 0 || cp->argtotal == argnum)
+              && cp->msgctxt != NULL
+              && cp->msgid != NULL
+              && cp->msgid_plural != NULL)
+            {
+              if (best_cp != NULL)
+                {
+                  ambiguous = true;
+                  break;
+                }
+              best_cp = cp;
+            }
+        }
+
+      if (best_cp == NULL)
+        {
+          struct partial_call *best_cp1 = NULL;
+          struct partial_call *best_cp2 = NULL;
+
+          /* Find complete calls where msgctxt, msgid are provided.  */
+          for (i = 0; i < ap->nalternatives; i++)
+            {
+              struct partial_call *cp = &ap->alternative[i];
+
+              if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0
+                  && (cp->argtotal == 0 || cp->argtotal == argnum)
+                  && cp->msgctxt != NULL
+                  && cp->msgid != NULL)
+                {
+                  if (best_cp1 != NULL)
+                    {
+                      ambiguous = true;
+                      break;
+                    }
+                  best_cp1 = cp;
+                }
+            }
+
+          /* Find complete calls where msgid, msgid_plural are provided.  */
+          for (i = 0; i < ap->nalternatives; i++)
+            {
+              struct partial_call *cp = &ap->alternative[i];
+
+              if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0
+                  && (cp->argtotal == 0 || cp->argtotal == argnum)
+                  && cp->msgid != NULL
+                  && cp->msgid_plural != NULL)
+                {
+                  if (best_cp2 != NULL)
+                    {
+                      ambiguous = true;
+                      break;
+                    }
+                  best_cp2 = cp;
+                }
+            }
+
+          if (best_cp1 != NULL)
+            best_cp = best_cp1;
+          if (best_cp2 != NULL)
+            {
+              if (best_cp != NULL)
+                ambiguous = true;
+              else
+                best_cp = best_cp2;
+            }
+        }
+
+      if (best_cp == NULL)
+        {
+          /* Find complete calls where msgid is provided.  */
+          for (i = 0; i < ap->nalternatives; i++)
+            {
+              struct partial_call *cp = &ap->alternative[i];
+
+              if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0
+                  && (cp->argtotal == 0 || cp->argtotal == argnum)
+                  && cp->msgid != NULL)
+                {
+                  if (best_cp != NULL)
+                    {
+                      ambiguous = true;
+                      break;
+                    }
+                  best_cp = cp;
+                }
+            }
+        }
+
+      if (ambiguous)
+        {
+          error_with_progname = false;
+          error_at_line (0, 0,
+                         best_cp->msgid_pos.file_name,
+                         best_cp->msgid_pos.line_number,
+                         _("ambiguous argument specification for keyword '%.*s'"),
+                         (int) ap->keyword_len, ap->keyword);
+          error_with_progname = true;
+        }
+
+      if (best_cp != NULL)
+        {
+          /* best_cp indicates the best found complete call.
+             Now call remember_a_message.  */
+          message_ty *mp;
+
+          /* Split strings in the GNOME glib syntax "msgctxt|msgid".  */
+          if (best_cp->argnum1_glib_context || best_cp->argnum2_glib_context)
+            /* split_keywordspec should not allow the context to be specified
+               in two different ways.  */
+            if (best_cp->msgctxt != NULL)
+              abort ();
+          if (best_cp->argnum1_glib_context)
+            {
+              const char *separator = strchr (best_cp->msgid, '|');
+
+              if (separator == NULL)
+                {
+                  error_with_progname = false;
+                  error_at_line (0, 0,
+                                 best_cp->msgid_pos.file_name,
+                                 best_cp->msgid_pos.line_number,
+                                 _("warning: missing context for keyword '%.*s'"),
+                                 (int) ap->keyword_len, ap->keyword);
+                  error_with_progname = true;
+                }
+              else
+                {
+                  size_t ctxt_len = separator - best_cp->msgid;
+                  char *ctxt = XNMALLOC (ctxt_len + 1, char);
+
+                  memcpy (ctxt, best_cp->msgid, ctxt_len);
+                  ctxt[ctxt_len] = '\0';
+                  best_cp->msgctxt = ctxt;
+                  best_cp->msgid = xstrdup (separator + 1);
+                }
+            }
+          if (best_cp->msgid_plural != NULL && best_cp->argnum2_glib_context)
+            {
+              const char *separator = strchr (best_cp->msgid_plural, '|');
+
+              if (separator == NULL)
+                {
+                  error_with_progname = false;
+                  error_at_line (0, 0,
+                                 best_cp->msgid_plural_pos.file_name,
+                                 best_cp->msgid_plural_pos.line_number,
+                                 _("warning: missing context for plural argument of keyword '%.*s'"),
+                                 (int) ap->keyword_len, ap->keyword);
+                  error_with_progname = true;
+                }
+              else
+                {
+                  size_t ctxt_len = separator - best_cp->msgid_plural;
+                  char *ctxt = XNMALLOC (ctxt_len + 1, char);
+
+                  memcpy (ctxt, best_cp->msgid_plural, ctxt_len);
+                  ctxt[ctxt_len] = '\0';
+                  if (best_cp->msgctxt == NULL)
+                    best_cp->msgctxt = ctxt;
+                  else
+                    {
+                      if (strcmp (ctxt, best_cp->msgctxt) != 0)
+                        {
+                          error_with_progname = false;
+                          error_at_line (0, 0,
+                                         best_cp->msgid_plural_pos.file_name,
+                                         best_cp->msgid_plural_pos.line_number,
+                                         _("context mismatch between singular and plural form"));
+                          error_with_progname = true;
+                        }
+                      free (ctxt);
+                    }
+                  best_cp->msgid_plural = xstrdup (separator + 1);
+                }
+            }
+
+          {
+            flag_context_ty msgid_context = best_cp->msgid_context;
+            flag_context_ty msgid_plural_context = best_cp->msgid_plural_context;
+
+            /* Special support for the 3-argument tr operator in Qt:
+               When --qt and --keyword=tr:1,1,2c,3t are specified, add to the
+               context the information that the argument is expected to be a
+               qt-plural-format.  */
+            if (recognize_qt_formatstrings ()
+                && best_cp->msgid_plural == best_cp->msgid)
+              {
+                msgid_context.is_format3 = yes_according_to_context;
+                msgid_plural_context.is_format3 = yes_according_to_context;
+              }
+
+            mp = remember_a_message (ap->mlp, best_cp->msgctxt, best_cp->msgid,
+                                     msgid_context,
+                                     &best_cp->msgid_pos,
+                                     NULL, best_cp->msgid_comment);
+            if (mp != NULL && best_cp->msgid_plural != NULL)
+              remember_a_message_plural (mp, best_cp->msgid_plural,
+                                         msgid_plural_context,
+                                         &best_cp->msgid_plural_pos,
+                                         NULL);
+          }
+
+          if (best_cp->xcomments.nitems > 0)
+            {
+              /* Add best_cp->xcomments to mp->comment_dot, unless already
+                 present.  */
+              size_t i;
+
+              for (i = 0; i < best_cp->xcomments.nitems; i++)
+                {
+                  const char *xcomment = best_cp->xcomments.item[i];
+                  bool found = false;
+
+                  if (mp != NULL && mp->comment_dot != NULL)
+                    {
+                      size_t j;
+
+                      for (j = 0; j < mp->comment_dot->nitems; j++)
+                        if (strcmp (xcomment, mp->comment_dot->item[j]) == 0)
+                          {
+                            found = true;
+                            break;
+                          }
+                    }
+                  if (!found)
+                    message_comment_dot_append (mp, xcomment);
+                }
+            }
+        }
+    }
+  else
+    {
+      /* No complete call was parsed.  */
+      /* Note: There is a memory leak here: When there is more than one
+         alternative, the same string can be stored in multiple alternatives,
+         and it's not easy to free all strings reliably.  */
+      if (ap->nalternatives == 1)
+        {
+          if (ap->alternative[0].msgctxt != NULL)
+            free (ap->alternative[0].msgctxt);
+          if (ap->alternative[0].msgid != NULL)
+            free (ap->alternative[0].msgid);
+          if (ap->alternative[0].msgid_plural != NULL)
+            free (ap->alternative[0].msgid_plural);
+        }
+    }
+
+  for (i = 0; i < ap->nalternatives; i++)
+    drop_reference (ap->alternative[i].msgid_comment);
+  free (ap);
+}
diff --git a/gettext-tools/src/xg-arglist-parser.h b/gettext-tools/src/xg-arglist-parser.h
new file mode 100644 (file)
index 0000000..bad14fc
--- /dev/null
@@ -0,0 +1,108 @@
+/* Resolving ambiguity of argument lists: Progressive parsing of an
+   argument list, keeping track of all possibilities.
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   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 3 of the License, 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, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _XGETTEXT_ARGLIST_PARSER_H
+#define _XGETTEXT_ARGLIST_PARSER_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "pos.h"
+#include "rc-str-list.h"
+#include "str-list.h"
+
+#include "xg-arglist-context.h"
+#include "xg-arglist-callshape.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Represents the progressive parsing of an argument list w.r.t. a single
+   'struct callshape'.  */
+struct partial_call
+{
+  int argnumc;                  /* number of context argument, 0 when seen */
+  int argnum1;                  /* number of singular argument, 0 when seen */
+  int argnum2;                  /* number of plural argument, 0 when seen */
+  bool argnum1_glib_context;    /* argument argnum1 has the syntax "ctxt|msgid" */
+  bool argnum2_glib_context;    /* argument argnum2 has the syntax "ctxt|msgid" */
+  int argtotal;                 /* total number of arguments, 0 if unspecified */
+  string_list_ty xcomments;     /* auto-extracted comments */
+  char *msgctxt;                /* context - owned string, or NULL */
+  lex_pos_ty msgctxt_pos;
+  char *msgid;                  /* msgid - owned string, or NULL */
+  flag_context_ty msgid_context;
+  lex_pos_ty msgid_pos;
+  refcounted_string_list_ty *msgid_comment;
+  char *msgid_plural;           /* msgid_plural - owned string, or NULL */
+  flag_context_ty msgid_plural_context;
+  lex_pos_ty msgid_plural_pos;
+};
+
+/* Represents the progressive parsing of an argument list w.r.t. an entire
+   'struct callshapes'.  */
+struct arglist_parser
+{
+  message_list_ty *mlp;         /* list where the message shall be added */
+  const char *keyword;          /* the keyword, not NUL terminated */
+  size_t keyword_len;           /* the keyword's length */
+  bool next_is_msgctxt;         /* true if the next argument is the msgctxt */
+  size_t nalternatives;         /* number of partial_call alternatives */
+  struct partial_call alternative[1]; /* partial_call alternatives */
+};
+
+/* Creates a fresh arglist_parser recognizing calls.
+   You can pass shapes = NULL for a parser not recognizing any calls.  */
+extern struct arglist_parser * arglist_parser_alloc (message_list_ty *mlp,
+                                                     const struct callshapes *shapes);
+/* Clones an arglist_parser.  */
+extern struct arglist_parser * arglist_parser_clone (struct arglist_parser *ap);
+/* Adds a string argument to an arglist_parser.  ARGNUM must be > 0.
+   STRING must be malloc()ed string; its ownership is passed to the callee.
+   FILE_NAME must be allocated with indefinite extent.
+   COMMENT may be savable_comment, or it may be a saved copy of savable_comment
+   (then add_reference must be used when saving it, and drop_reference while
+   dropping it).  Clear savable_comment.  */
+extern void arglist_parser_remember (struct arglist_parser *ap,
+                                     int argnum, char *string,
+                                     flag_context_ty context,
+                                     char *file_name, size_t line_number,
+                                     refcounted_string_list_ty *comment);
+/* Adds a string argument as msgctxt to an arglist_parser, without incrementing
+   the current argument number.
+   STRING must be malloc()ed string; its ownership is passed to the callee.
+   FILE_NAME must be allocated with indefinite extent.  */
+extern void arglist_parser_remember_msgctxt (struct arglist_parser *ap,
+                                             char *string,
+                                             flag_context_ty context,
+                                             char *file_name, size_t line_number);
+/* Tests whether an arglist_parser has is not waiting for more arguments after
+   argument ARGNUM.  */
+extern bool arglist_parser_decidedp (struct arglist_parser *ap, int argnum);
+/* Terminates the processing of an arglist_parser after argument ARGNUM and
+   deletes it.  */
+extern void arglist_parser_done (struct arglist_parser *ap, int argnum);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _XGETTEXT_ARGLIST_PARSER_H */
diff --git a/gettext-tools/src/xg-encoding.c b/gettext-tools/src/xg-encoding.c
new file mode 100644 (file)
index 0000000..1180157
--- /dev/null
@@ -0,0 +1,136 @@
+/* Keeping track of the encoding of strings to be extracted.
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   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 3 of the License, 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, see <https://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification.  */
+#include "xg-encoding.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "msgl-ascii.h"
+#include "msgl-iconv.h"
+#include "po-charset.h"
+#include "xalloc.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+
+#include "gettext.h"
+#define _(str) gettext (str)
+
+
+/* Canonicalized encoding name for all input files.  */
+const char *xgettext_global_source_encoding;
+
+#if HAVE_ICONV
+/* Converter from xgettext_global_source_encoding to UTF-8 (except from
+   ASCII or UTF-8, when this conversion is a no-op).  */
+iconv_t xgettext_global_source_iconv;
+#endif
+
+/* Canonicalized encoding name for the current input file.  */
+const char *xgettext_current_source_encoding;
+
+#if HAVE_ICONV
+/* Converter from xgettext_current_source_encoding to UTF-8 (except from
+   ASCII or UTF-8, when this conversion is a no-op).  */
+iconv_t xgettext_current_source_iconv;
+#endif
+
+
+/* Error message about non-ASCII character in a specific lexical context.  */
+char *
+non_ascii_error_message (lexical_context_ty lcontext,
+                         const char *file_name, size_t line_number)
+{
+  char buffer[21];
+  char *errmsg;
+
+  if (line_number == (size_t)(-1))
+    buffer[0] = '\0';
+  else
+    sprintf (buffer, ":%ld", (long) line_number);
+
+  switch (lcontext)
+    {
+    case lc_outside:
+      errmsg =
+        xasprintf (_("Non-ASCII character at %s%s."), file_name, buffer);
+      break;
+    case lc_comment:
+      errmsg =
+        xasprintf (_("Non-ASCII comment at or before %s%s."),
+                   file_name, buffer);
+      break;
+    case lc_string:
+      errmsg =
+        xasprintf (_("Non-ASCII string at %s%s."), file_name, buffer);
+      break;
+    default:
+      abort ();
+    }
+  return errmsg;
+}
+
+/* Convert the given string from xgettext_current_source_encoding to
+   the output file encoding (i.e. ASCII or UTF-8).
+   The resulting string is either the argument string, or freshly allocated.
+   The file_name and line_number are only used for error message purposes.  */
+char *
+from_current_source_encoding (const char *string,
+                              lexical_context_ty lcontext,
+                              const char *file_name, size_t line_number)
+{
+  if (xgettext_current_source_encoding == po_charset_ascii)
+    {
+      if (!is_ascii_string (string))
+        {
+          multiline_error (xstrdup (""),
+                           xasprintf ("%s\n%s\n",
+                                      non_ascii_error_message (lcontext,
+                                                               file_name,
+                                                               line_number),
+                                      _("\
+Please specify the source encoding through --from-code.")));
+          exit (EXIT_FAILURE);
+        }
+    }
+  else if (xgettext_current_source_encoding != po_charset_utf8)
+    {
+#if HAVE_ICONV
+      struct conversion_context context;
+
+      context.from_code = xgettext_current_source_encoding;
+      context.to_code = po_charset_utf8;
+      context.from_filename = file_name;
+      context.message = NULL;
+
+      string = convert_string_directly (xgettext_current_source_iconv, string,
+                                        &context);
+#else
+      /* If we don't have iconv(), the only supported values for
+         xgettext_global_source_encoding and thus also for
+         xgettext_current_source_encoding are ASCII and UTF-8.
+         convert_string_directly() should not be called in this case.  */
+      abort ();
+#endif
+    }
+
+  return (char *) string;
+}
diff --git a/gettext-tools/src/xg-encoding.h b/gettext-tools/src/xg-encoding.h
new file mode 100644 (file)
index 0000000..b2b571c
--- /dev/null
@@ -0,0 +1,86 @@
+/* Keeping track of the encoding of strings to be extracted.
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   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 3 of the License, 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, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _XGETTEXT_ENCODING_H
+#define _XGETTEXT_ENCODING_H
+
+#include <stddef.h>
+
+#if HAVE_ICONV
+#include <iconv.h>
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Context while building up lexical tokens.  */
+typedef enum
+  {
+    lc_outside, /* Initial context: outside of comments and strings.  */
+    lc_comment, /* Inside a comment.  */
+    lc_string,  /* Inside a string literal.  */
+
+    /* For embedded XML in programming code, like E4X in JavaScript.  */
+    lc_xml_open_tag,   /* Inside an opening tag of an XML element.  */
+    lc_xml_close_tag,  /* Inside a closing tag of an XML element.  */
+    lc_xml_content     /* Inside an XML text node.  */
+  }
+lexical_context_ty;
+
+/* Error message about non-ASCII character in a specific lexical context.  */
+extern char *non_ascii_error_message (lexical_context_ty lcontext,
+                                      const char *file_name,
+                                      size_t line_number);
+
+
+/* Canonicalized encoding name for all input files.  */
+extern const char *xgettext_global_source_encoding;
+
+#if HAVE_ICONV
+/* Converter from xgettext_global_source_encoding to UTF-8 (except from
+   ASCII or UTF-8, when this conversion is a no-op).  */
+extern iconv_t xgettext_global_source_iconv;
+#endif
+
+/* Canonicalized encoding name for the current input file.  */
+extern const char *xgettext_current_source_encoding;
+
+#if HAVE_ICONV
+/* Converter from xgettext_current_source_encoding to UTF-8 (except from
+   ASCII or UTF-8, when this conversion is a no-op).  */
+extern iconv_t xgettext_current_source_iconv;
+#endif
+
+/* Convert the given string from xgettext_current_source_encoding to
+   the output file encoding (i.e. ASCII or UTF-8).
+   The resulting string is either the argument string, or freshly allocated.
+   The lcontext, file_name and line_number are only used for error message
+   purposes.  */
+extern char *from_current_source_encoding (const char *string,
+                                           lexical_context_ty lcontext,
+                                           const char *file_name,
+                                           size_t line_number);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _XGETTEXT_ENCODING_H */
diff --git a/gettext-tools/src/xg-message.c b/gettext-tools/src/xg-message.c
new file mode 100644 (file)
index 0000000..caf58cf
--- /dev/null
@@ -0,0 +1,592 @@
+/* Extracting a message.  Accumulating the message list.
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   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 3 of the License, 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, see <https://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification.  */
+#include "xg-message.h"
+
+#include <stdio.h>
+
+#include "c-strstr.h"
+#include "error-progname.h"
+#include "format.h"
+#include "read-catalog-abstract.h"
+#include "xalloc.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+
+#include "xgettext.h"
+
+#include "gettext.h"
+#define _(str) gettext (str)
+
+
+#define CONVERT_STRING(string, lcontext) \
+  string = from_current_source_encoding (string, lcontext, pos->file_name, \
+                                         pos->line_number);
+
+
+/* Update the is_format[] flags depending on the information given in the
+   context.  */
+static void
+set_format_flags_from_context (enum is_format is_format[NFORMATS],
+                               flag_context_ty context, const char *string,
+                               lex_pos_ty *pos, const char *pretty_msgstr)
+{
+  size_t i;
+
+  if (context.is_format1 != undecided
+      || context.is_format2 != undecided
+      || context.is_format3 != undecided)
+    for (i = 0; i < NFORMATS; i++)
+      {
+        if (is_format[i] == undecided)
+          {
+            if (formatstring_parsers[i] == current_formatstring_parser1
+                && context.is_format1 != undecided)
+              is_format[i] = (enum is_format) context.is_format1;
+            if (formatstring_parsers[i] == current_formatstring_parser2
+                && context.is_format2 != undecided)
+              is_format[i] = (enum is_format) context.is_format2;
+            if (formatstring_parsers[i] == current_formatstring_parser3
+                && context.is_format3 != undecided)
+              is_format[i] = (enum is_format) context.is_format3;
+          }
+        if (possible_format_p (is_format[i]))
+          {
+            struct formatstring_parser *parser = formatstring_parsers[i];
+            char *invalid_reason = NULL;
+            void *descr = parser->parse (string, false, NULL, &invalid_reason);
+
+            if (descr != NULL)
+              parser->free (descr);
+            else
+              {
+                /* The string is not a valid format string.  */
+                if (is_format[i] != possible)
+                  {
+                    char buffer[21];
+
+                    error_with_progname = false;
+                    if (pos->line_number == (size_t)(-1))
+                      buffer[0] = '\0';
+                    else
+                      sprintf (buffer, ":%ld", (long) pos->line_number);
+                    multiline_warning (xasprintf (_("%s%s: warning: "),
+                                                  pos->file_name, buffer),
+                                       xasprintf (is_format[i] == yes_according_to_context
+                                                  ? _("Although being used in a format string position, the %s is not a valid %s format string. Reason: %s\n")
+                                                  : _("Although declared as such, the %s is not a valid %s format string. Reason: %s\n"),
+                                                  pretty_msgstr,
+                                                  format_language_pretty[i],
+                                                  invalid_reason));
+                    error_with_progname = true;
+                  }
+
+                is_format[i] = impossible;
+                free (invalid_reason);
+              }
+          }
+      }
+}
+
+
+static void
+warn_format_string (enum is_format is_format[NFORMATS], const char *string,
+                    lex_pos_ty *pos, const char *pretty_msgstr)
+{
+  if (possible_format_p (is_format[format_python])
+      && get_python_format_unnamed_arg_count (string) > 1)
+    {
+      char buffer[21];
+
+      error_with_progname = false;
+      if (pos->line_number == (size_t)(-1))
+        buffer[0] = '\0';
+      else
+        sprintf (buffer, ":%ld", (long) pos->line_number);
+      multiline_warning (xasprintf (_("%s%s: warning: "),
+                                    pos->file_name, buffer),
+                         xasprintf (_("\
+'%s' format string with unnamed arguments cannot be properly localized:\n\
+The translator cannot reorder the arguments.\n\
+Please consider using a format string with named arguments,\n\
+and a mapping instead of a tuple for the arguments.\n"),
+                                    pretty_msgstr));
+      error_with_progname = true;
+    }
+}
+
+
+message_ty *
+remember_a_message (message_list_ty *mlp, char *msgctxt, char *msgid,
+                    flag_context_ty context, lex_pos_ty *pos,
+                    const char *extracted_comment,
+                    refcounted_string_list_ty *comment)
+{
+  enum is_format is_format[NFORMATS];
+  struct argument_range range;
+  enum is_wrap do_wrap;
+  enum is_syntax_check do_syntax_check[NSYNTAXCHECKS];
+  message_ty *mp;
+  char *msgstr;
+  size_t i;
+
+  /* See whether we shall exclude this message.  */
+  if (exclude != NULL && message_list_search (exclude, msgctxt, msgid) != NULL)
+    {
+      /* Tell the lexer to reset its comment buffer, so that the next
+         message gets the correct comments.  */
+      xgettext_comment_reset ();
+      savable_comment_reset ();
+
+      if (msgctxt != NULL)
+        free (msgctxt);
+      free (msgid);
+
+      return NULL;
+    }
+
+  savable_comment_to_xgettext_comment (comment);
+
+  for (i = 0; i < NFORMATS; i++)
+    is_format[i] = undecided;
+  range.min = -1;
+  range.max = -1;
+  do_wrap = undecided;
+  for (i = 0; i < NSYNTAXCHECKS; i++)
+    do_syntax_check[i] = undecided;
+
+  if (msgctxt != NULL)
+    CONVERT_STRING (msgctxt, lc_string);
+  CONVERT_STRING (msgid, lc_string);
+
+  if (msgctxt == NULL && msgid[0] == '\0' && !xgettext_omit_header)
+    {
+      char buffer[21];
+
+      error_with_progname = false;
+      if (pos->line_number == (size_t)(-1))
+        buffer[0] = '\0';
+      else
+        sprintf (buffer, ":%ld", (long) pos->line_number);
+      multiline_warning (xasprintf (_("%s%s: warning: "), pos->file_name,
+                                    buffer),
+                         xstrdup (_("\
+Empty msgid.  It is reserved by GNU gettext:\n\
+gettext(\"\") returns the header entry with\n\
+meta information, not the empty string.\n")));
+      error_with_progname = true;
+    }
+
+  /* See if we have seen this message before.  */
+  mp = message_list_search (mlp, msgctxt, msgid);
+  if (mp != NULL)
+    {
+      if (msgctxt != NULL)
+        free (msgctxt);
+      free (msgid);
+      for (i = 0; i < NFORMATS; i++)
+        is_format[i] = mp->is_format[i];
+      do_wrap = mp->do_wrap;
+      for (i = 0; i < NSYNTAXCHECKS; i++)
+        do_syntax_check[i] = mp->do_syntax_check[i];
+    }
+  else
+    {
+      /* Construct the msgstr from the prefix and suffix, otherwise use the
+         empty string.  */
+      if (msgstr_prefix)
+        msgstr = xasprintf ("%s%s%s", msgstr_prefix, msgid, msgstr_suffix);
+      else
+        msgstr = "";
+
+      /* Allocate a new message and append the message to the list.  */
+      mp = message_alloc (msgctxt, msgid, NULL, msgstr, strlen (msgstr) + 1,
+                          pos);
+      /* Do not free msgctxt and msgid.  */
+      message_list_append (mlp, mp);
+    }
+
+  /* Determine whether the context specifies that the msgid is a format
+     string.  */
+  set_format_flags_from_context (is_format, context, mp->msgid, pos, "msgid");
+
+  /* Ask the lexer for the comments it has seen.  */
+  {
+    size_t nitems_before;
+    size_t nitems_after;
+    int j;
+    bool add_all_remaining_comments;
+    /* The string before the comment tag.  For example, If "** TRANSLATORS:"
+       is seen and the comment tag is "TRANSLATORS:",
+       then comment_tag_prefix is set to "** ".  */
+    const char *comment_tag_prefix = "";
+    size_t comment_tag_prefix_length = 0;
+
+    nitems_before = (mp->comment_dot != NULL ? mp->comment_dot->nitems : 0);
+
+    if (extracted_comment != NULL)
+      {
+        char *copy = xstrdup (extracted_comment);
+        char *rest;
+
+        rest = copy;
+        while (*rest != '\0')
+          {
+            char *newline = strchr (rest, '\n');
+
+            if (newline != NULL)
+              {
+                *newline = '\0';
+                message_comment_dot_append (mp, rest);
+                rest = newline + 1;
+              }
+            else
+              {
+                message_comment_dot_append (mp, rest);
+                break;
+              }
+          }
+        free (copy);
+      }
+
+    add_all_remaining_comments = add_all_comments;
+    for (j = 0; ; ++j)
+      {
+        const char *s = xgettext_comment (j);
+        const char *t;
+        if (s == NULL)
+          break;
+
+        CONVERT_STRING (s, lc_comment);
+
+        /* To reduce the possibility of unwanted matches we do a two
+           step match: the line must contain 'xgettext:' and one of
+           the possible format description strings.  */
+        if ((t = c_strstr (s, "xgettext:")) != NULL)
+          {
+            bool tmp_fuzzy;
+            enum is_format tmp_format[NFORMATS];
+            struct argument_range tmp_range;
+            enum is_wrap tmp_wrap;
+            enum is_syntax_check tmp_syntax_check[NSYNTAXCHECKS];
+            bool interesting;
+
+            t += strlen ("xgettext:");
+
+            po_parse_comment_special (t, &tmp_fuzzy, tmp_format, &tmp_range,
+                                      &tmp_wrap, tmp_syntax_check);
+
+            interesting = false;
+            for (i = 0; i < NFORMATS; i++)
+              if (tmp_format[i] != undecided)
+                {
+                  is_format[i] = tmp_format[i];
+                  interesting = true;
+                }
+            if (has_range_p (tmp_range))
+              {
+                range = tmp_range;
+                interesting = true;
+              }
+            if (tmp_wrap != undecided)
+              {
+                do_wrap = tmp_wrap;
+                interesting = true;
+              }
+            for (i = 0; i < NSYNTAXCHECKS; i++)
+              if (tmp_syntax_check[i] != undecided)
+                {
+                  do_syntax_check[i] = tmp_syntax_check[i];
+                  interesting = true;
+                }
+
+            /* If the "xgettext:" marker was followed by an interesting
+               keyword, and we updated our is_format/do_wrap variables,
+               we don't print the comment as a #. comment.  */
+            if (interesting)
+              continue;
+          }
+
+        if (!add_all_remaining_comments && comment_tag != NULL)
+          {
+            /* When the comment tag is seen, it drags in not only the line
+               which it starts, but all remaining comment lines.  */
+            if ((t = c_strstr (s, comment_tag)) != NULL)
+              {
+                add_all_remaining_comments = true;
+                comment_tag_prefix = s;
+                comment_tag_prefix_length = t - s;
+              }
+          }
+
+        if (add_all_remaining_comments)
+          {
+            if (strncmp (s, comment_tag_prefix, comment_tag_prefix_length) == 0)
+              s += comment_tag_prefix_length;
+            message_comment_dot_append (mp, s);
+          }
+      }
+
+    nitems_after = (mp->comment_dot != NULL ? mp->comment_dot->nitems : 0);
+
+    /* Don't add the comments if they are a repetition of the tail of the
+       already present comments.  This avoids unneeded duplication if the
+       same message appears several times, each time with the same comment.  */
+    if (nitems_before < nitems_after)
+      {
+        size_t added = nitems_after - nitems_before;
+
+        if (added <= nitems_before)
+          {
+            bool repeated = true;
+
+            for (i = 0; i < added; i++)
+              if (strcmp (mp->comment_dot->item[nitems_before - added + i],
+                          mp->comment_dot->item[nitems_before + i]) != 0)
+                {
+                  repeated = false;
+                  break;
+                }
+
+            if (repeated)
+              {
+                for (i = 0; i < added; i++)
+                  free ((char *) mp->comment_dot->item[nitems_before + i]);
+                mp->comment_dot->nitems = nitems_before;
+              }
+          }
+      }
+  }
+
+  /* If it is not already decided, through programmer comments, whether the
+     msgid is a format string, examine the msgid.  This is a heuristic.  */
+  for (i = 0; i < NFORMATS; i++)
+    {
+      if (is_format[i] == undecided
+          && (formatstring_parsers[i] == current_formatstring_parser1
+              || formatstring_parsers[i] == current_formatstring_parser2
+              || formatstring_parsers[i] == current_formatstring_parser3)
+          /* 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]))
+          /* Avoid flagging a string as c-format when it's known to be a
+             qt-format or qt-plural-format or kde-format or boost-format
+             string.  */
+          && !(i == format_c
+               && (possible_format_p (is_format[format_qt])
+                   || possible_format_p (is_format[format_qt_plural])
+                   || possible_format_p (is_format[format_kde])
+                   || possible_format_p (is_format[format_kde_kuit])
+                   || possible_format_p (is_format[format_boost])))
+          /* Avoid flagging a string as kde-format when it's known to
+             be a kde-kuit-format string.  */
+          && !(i == format_kde
+               && possible_format_p (is_format[format_kde_kuit]))
+          /* Avoid flagging a string as kde-kuit-format when it's
+             known to be a kde-format string.  Note that this relies
+             on the fact that format_kde < format_kde_kuit, so a
+             string will be marked as kde-format if both are
+             undecided.  */
+          && !(i == format_kde_kuit
+               && possible_format_p (is_format[format_kde])))
+        {
+          struct formatstring_parser *parser = formatstring_parsers[i];
+          char *invalid_reason = NULL;
+          void *descr = parser->parse (mp->msgid, false, NULL, &invalid_reason);
+
+          if (descr != NULL)
+            {
+              /* msgid is a valid format string.  We mark only those msgids
+                 as format strings which contain at least one format directive
+                 and thus are format strings with a high probability.  We
+                 don't mark strings without directives as format strings,
+                 because that would force the programmer to add
+                 "xgettext: no-c-format" anywhere where a translator wishes
+                 to use a percent sign.  So, the msgfmt checking will not be
+                 perfect.  Oh well.  */
+              if (parser->get_number_of_directives (descr) > 0
+                  && !(parser->is_unlikely_intentional != NULL
+                       && parser->is_unlikely_intentional (descr)))
+                is_format[i] = possible;
+
+              parser->free (descr);
+            }
+          else
+            {
+              /* msgid is not a valid format string.  */
+              is_format[i] = impossible;
+              free (invalid_reason);
+            }
+        }
+      mp->is_format[i] = is_format[i];
+    }
+
+  if (has_range_p (range))
+    {
+      if (has_range_p (mp->range))
+        {
+          if (range.min < mp->range.min)
+            mp->range.min = range.min;
+          if (range.max > mp->range.max)
+            mp->range.max = range.max;
+        }
+      else
+        mp->range = range;
+    }
+
+  mp->do_wrap = do_wrap == no ? no : yes;       /* By default we wrap.  */
+
+  for (i = 0; i < NSYNTAXCHECKS; i++)
+    {
+      if (do_syntax_check[i] == undecided)
+        do_syntax_check[i] = default_syntax_check[i] == yes ? yes : no;
+
+      mp->do_syntax_check[i] = do_syntax_check[i];
+    }
+
+  /* Warn about the use of non-reorderable format strings when the programming
+     language also provides reorderable format strings.  */
+  warn_format_string (is_format, mp->msgid, pos, "msgid");
+
+  /* Remember where we saw this msgid.  */
+  message_comment_filepos (mp, pos->file_name, pos->line_number);
+
+  /* Tell the lexer to reset its comment buffer, so that the next
+     message gets the correct comments.  */
+  xgettext_comment_reset ();
+  savable_comment_reset ();
+
+  return mp;
+}
+
+
+void
+remember_a_message_plural (message_ty *mp, char *string,
+                           flag_context_ty context, lex_pos_ty *pos,
+                           refcounted_string_list_ty *comment)
+{
+  char *msgid_plural;
+  char *msgstr1;
+  size_t msgstr1_len;
+  char *msgstr;
+  size_t i;
+
+  msgid_plural = string;
+
+  savable_comment_to_xgettext_comment (comment);
+
+  CONVERT_STRING (msgid_plural, lc_string);
+
+  /* See if the message is already a plural message.  */
+  if (mp->msgid_plural == NULL)
+    {
+      mp->msgid_plural = msgid_plural;
+
+      /* Construct the first plural form from the prefix and suffix,
+         otherwise use the empty string.  The translator will have to
+         provide additional plural forms.  */
+      if (msgstr_prefix)
+        msgstr1 =
+          xasprintf ("%s%s%s", msgstr_prefix, msgid_plural, msgstr_suffix);
+      else
+        msgstr1 = "";
+      msgstr1_len = strlen (msgstr1) + 1;
+      msgstr = XNMALLOC (mp->msgstr_len + msgstr1_len, char);
+      memcpy (msgstr, mp->msgstr, mp->msgstr_len);
+      memcpy (msgstr + mp->msgstr_len, msgstr1, msgstr1_len);
+      mp->msgstr = msgstr;
+      mp->msgstr_len = mp->msgstr_len + msgstr1_len;
+      if (msgstr_prefix)
+        free (msgstr1);
+
+      /* Determine whether the context specifies that the msgid_plural is a
+         format string.  */
+      set_format_flags_from_context (mp->is_format, context, mp->msgid_plural,
+                                     pos, "msgid_plural");
+
+      /* If it is not already decided, through programmer comments or
+         the msgid, whether the msgid is a format string, examine the
+         msgid_plural.  This is a heuristic.  */
+      for (i = 0; i < NFORMATS; i++)
+        if ((formatstring_parsers[i] == current_formatstring_parser1
+             || formatstring_parsers[i] == current_formatstring_parser2
+             || formatstring_parsers[i] == current_formatstring_parser3)
+            && (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]))
+            /* Avoid flagging a string as c-format when it's known to be a
+               qt-format or qt-plural-format or boost-format string.  */
+            && !(i == format_c
+                 && (possible_format_p (mp->is_format[format_qt])
+                     || possible_format_p (mp->is_format[format_qt_plural])
+                     || possible_format_p (mp->is_format[format_kde])
+                     || possible_format_p (mp->is_format[format_kde_kuit])
+                     || possible_format_p (mp->is_format[format_boost])))
+            /* Avoid flagging a string as kde-format when it's known
+               to be a kde-kuit-format string.  */
+            && !(i == format_kde
+                 && possible_format_p (mp->is_format[format_kde_kuit]))
+            /* Avoid flagging a string as kde-kuit-format when it's
+               known to be a kde-format string.  Note that this relies
+               on the fact that format_kde < format_kde_kuit, so a
+               string will be marked as kde-format if both are
+               undecided.  */
+            && !(i == format_kde_kuit
+                 && possible_format_p (mp->is_format[format_kde])))
+          {
+            struct formatstring_parser *parser = formatstring_parsers[i];
+            char *invalid_reason = NULL;
+            void *descr =
+              parser->parse (mp->msgid_plural, false, NULL, &invalid_reason);
+
+            if (descr != NULL)
+              {
+                /* Same heuristic as in remember_a_message.  */
+                if (parser->get_number_of_directives (descr) > 0
+                    && !(parser->is_unlikely_intentional != NULL
+                         && parser->is_unlikely_intentional (descr)))
+                  mp->is_format[i] = possible;
+
+                parser->free (descr);
+              }
+            else
+              {
+                /* msgid_plural is not a valid format string.  */
+                mp->is_format[i] = impossible;
+                free (invalid_reason);
+              }
+          }
+
+      /* Warn about the use of non-reorderable format strings when the programming
+         language also provides reorderable format strings.  */
+      warn_format_string (mp->is_format, mp->msgid_plural, pos, "msgid_plural");
+    }
+  else
+    free (msgid_plural);
+
+  /* Tell the lexer to reset its comment buffer, so that the next
+     message gets the correct comments.  */
+  xgettext_comment_reset ();
+  savable_comment_reset ();
+}
diff --git a/gettext-tools/src/xg-message.h b/gettext-tools/src/xg-message.h
new file mode 100644 (file)
index 0000000..621cbbc
--- /dev/null
@@ -0,0 +1,70 @@
+/* Extracting a message.  Accumulating the message list.
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   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 3 of the License, 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, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _XGETTEXT_MESSAGE_H
+#define _XGETTEXT_MESSAGE_H
+
+#include "message.h"
+#include "pos.h"
+#include "rc-str-list.h"
+
+#include "xg-arglist-context.h"
+#include "xg-encoding.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Add a message to the list of extracted messages.
+   msgctxt must be either NULL or a malloc()ed string; its ownership is passed
+   to the callee.
+   MSGID must be a malloc()ed string; its ownership is passed to the callee.
+   POS->file_name must be allocated with indefinite extent.
+   EXTRACTED_COMMENT is a comment that needs to be copied into the POT file,
+   or NULL.
+   COMMENT may be savable_comment, or it may be a saved copy of savable_comment
+   (then add_reference must be used when saving it, and drop_reference while
+   dropping it).  Clear savable_comment.
+   Return the new or found message, or NULL if the message is excluded.  */
+extern message_ty *remember_a_message (message_list_ty *mlp,
+                                       char *msgctxt,
+                                       char *msgid,
+                                       flag_context_ty context,
+                                       lex_pos_ty *pos,
+                                       const char *extracted_comment,
+                                       refcounted_string_list_ty *comment);
+
+/* Add an msgid_plural to a message previously returned by
+   remember_a_message.
+   STRING must be a malloc()ed string; its ownership is passed to the callee.
+   POS->file_name must be allocated with indefinite extent.
+   COMMENT may be savable_comment, or it may be a saved copy of savable_comment
+   (then add_reference must be used when saving it, and drop_reference while
+   dropping it).  Clear savable_comment.  */
+extern void remember_a_message_plural (message_ty *mp,
+                                       char *string,
+                                       flag_context_ty context,
+                                       lex_pos_ty *pos,
+                                       refcounted_string_list_ty *comment);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _XGETTEXT_MESSAGE_H */
diff --git a/gettext-tools/src/xg-mixed-string.c b/gettext-tools/src/xg-mixed-string.c
new file mode 100644 (file)
index 0000000..9a44856
--- /dev/null
@@ -0,0 +1,209 @@
+/* Handling strings that are given partially in the source encoding and
+   partially in Unicode.
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   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 3 of the License, 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, see <https://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification.  */
+#include "xg-mixed-string.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "unistr.h"
+#include "xalloc.h"
+
+
+struct mixed_string_buffer *
+mixed_string_buffer_alloc (lexical_context_ty lcontext,
+                           const char *logical_file_name,
+                           int line_number)
+{
+  struct mixed_string_buffer *bp = XMALLOC (struct mixed_string_buffer);
+  bp->utf8_buffer = NULL;
+  bp->utf8_buflen = 0;
+  bp->utf8_allocated = 0;
+  bp->utf16_surr = 0;
+  bp->curr_buffer = NULL;
+  bp->curr_buflen = 0;
+  bp->curr_allocated = 0;
+  bp->lcontext = lcontext;
+  bp->logical_file_name = logical_file_name;
+  bp->line_number = line_number;
+  return bp;
+}
+
+/* Auxiliary function: Append a byte to bp->curr.  */
+static inline void
+mixed_string_buffer_append_to_curr_buffer (struct mixed_string_buffer *bp,
+                                           unsigned char c)
+{
+  if (bp->curr_buflen == bp->curr_allocated)
+    {
+      bp->curr_allocated = 2 * bp->curr_allocated + 10;
+      bp->curr_buffer = xrealloc (bp->curr_buffer, bp->curr_allocated);
+    }
+  bp->curr_buffer[bp->curr_buflen++] = c;
+}
+
+/* Auxiliary function: Ensure count more bytes are available in bp->utf8.  */
+static inline void
+mixed_string_buffer_grow_utf8_buffer (struct mixed_string_buffer *bp,
+                                         size_t count)
+{
+  if (bp->utf8_buflen + count > bp->utf8_allocated)
+    {
+      size_t new_allocated = 2 * bp->utf8_allocated + 10;
+      if (new_allocated < bp->utf8_buflen + count)
+        new_allocated = bp->utf8_buflen + count;
+      bp->utf8_allocated = new_allocated;
+      bp->utf8_buffer = xrealloc (bp->utf8_buffer, new_allocated);
+    }
+}
+
+/* Auxiliary function: Append a Unicode character to bp->utf8.
+   uc must be < 0x110000.  */
+static inline void
+mixed_string_buffer_append_to_utf8_buffer (struct mixed_string_buffer *bp,
+                                           ucs4_t uc)
+{
+  unsigned char utf8buf[6];
+  int count = u8_uctomb (utf8buf, uc, 6);
+
+  if (count < 0)
+    /* The caller should have ensured that uc is not out-of-range.  */
+    abort ();
+
+  mixed_string_buffer_grow_utf8_buffer (bp, count);
+  memcpy (bp->utf8_buffer + bp->utf8_buflen, utf8buf, count);
+  bp->utf8_buflen += count;
+}
+
+/* Auxiliary function: Flush bp->utf16_surr into bp->utf8_buffer.  */
+static inline void
+mixed_string_buffer_flush_utf16_surr (struct mixed_string_buffer *bp)
+{
+  if (bp->utf16_surr != 0)
+    {
+      /* A half surrogate is invalid, therefore use U+FFFD instead.  */
+      mixed_string_buffer_append_to_utf8_buffer (bp, 0xfffd);
+      bp->utf16_surr = 0;
+    }
+}
+
+/* Auxiliary function: Flush bp->curr_buffer into bp->utf8_buffer.  */
+static inline void
+mixed_string_buffer_flush_curr_buffer (struct mixed_string_buffer *bp,
+                                       int line_number)
+{
+  if (bp->curr_buflen > 0)
+    {
+      char *curr;
+      size_t count;
+
+      mixed_string_buffer_append_to_curr_buffer (bp, '\0');
+
+      /* Convert from the source encoding to UTF-8.  */
+      curr = from_current_source_encoding (bp->curr_buffer, bp->lcontext,
+                                           bp->logical_file_name,
+                                           line_number);
+
+      /* Append it to bp->utf8_buffer.  */
+      count = strlen (curr);
+      mixed_string_buffer_grow_utf8_buffer (bp, count);
+      memcpy (bp->utf8_buffer + bp->utf8_buflen, curr, count);
+      bp->utf8_buflen += count;
+
+      if (curr != bp->curr_buffer)
+        free (curr);
+      bp->curr_buflen = 0;
+    }
+}
+
+void
+mixed_string_buffer_append_char (struct mixed_string_buffer *bp, int c)
+{
+  /* Switch from Unicode character mode to multibyte character mode.  */
+  mixed_string_buffer_flush_utf16_surr (bp);
+
+  /* When a newline is seen, convert the accumulated multibyte sequence.
+     This ensures a correct line number in the error message in case of
+     a conversion error.  The "- 1" is to account for the newline.  */
+  if (c == '\n')
+    mixed_string_buffer_flush_curr_buffer (bp, bp->line_number - 1);
+
+  mixed_string_buffer_append_to_curr_buffer (bp, (unsigned char) c);
+}
+
+void
+mixed_string_buffer_append_unicode (struct mixed_string_buffer *bp, int c)
+{
+  /* Switch from multibyte character mode to Unicode character mode.  */
+  mixed_string_buffer_flush_curr_buffer (bp, bp->line_number);
+
+  /* Test whether this character and the previous one form a Unicode
+     surrogate character pair.  */
+  if (bp->utf16_surr != 0 && (c >= 0xdc00 && c < 0xe000))
+    {
+      unsigned short utf16buf[2];
+      ucs4_t uc;
+
+      utf16buf[0] = bp->utf16_surr;
+      utf16buf[1] = c;
+      if (u16_mbtouc (&uc, utf16buf, 2) != 2)
+        abort ();
+
+      mixed_string_buffer_append_to_utf8_buffer (bp, uc);
+      bp->utf16_surr = 0;
+    }
+  else
+    {
+      mixed_string_buffer_flush_utf16_surr (bp);
+
+      if (c >= 0xd800 && c < 0xdc00)
+        bp->utf16_surr = c;
+      else if (c >= 0xdc00 && c < 0xe000)
+        {
+          /* A half surrogate is invalid, therefore use U+FFFD instead.  */
+          mixed_string_buffer_append_to_utf8_buffer (bp, 0xfffd);
+        }
+      else
+        mixed_string_buffer_append_to_utf8_buffer (bp, c);
+    }
+}
+
+char *
+mixed_string_buffer_done (struct mixed_string_buffer *bp)
+{
+  char *utf8_buffer;
+
+  /* Flush all into bp->utf8_buffer.  */
+  mixed_string_buffer_flush_utf16_surr (bp);
+  mixed_string_buffer_flush_curr_buffer (bp, bp->line_number);
+  /* NUL-terminate it.  */
+  mixed_string_buffer_grow_utf8_buffer (bp, 1);
+  bp->utf8_buffer[bp->utf8_buflen] = '\0';
+
+  /* Free curr_buffer and bp itself.  */
+  utf8_buffer = bp->utf8_buffer;
+  free (bp->curr_buffer);
+  free (bp);
+
+  /* Return it.  */
+  return utf8_buffer;
+}
diff --git a/gettext-tools/src/xg-mixed-string.h b/gettext-tools/src/xg-mixed-string.h
new file mode 100644 (file)
index 0000000..9e82bbf
--- /dev/null
@@ -0,0 +1,75 @@
+/* Handling strings that are given partially in the source encoding and
+   partially in Unicode.
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   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 3 of the License, 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, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _XGETTEXT_MIXED_STRING_H
+#define _XGETTEXT_MIXED_STRING_H
+
+#include <stddef.h>
+
+#include "xg-encoding.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* A string buffer type that allows appending bytes (in the
+   xgettext_current_source_encoding) or Unicode characters.
+   Returns the entire string in UTF-8 encoding.  */
+
+struct mixed_string_buffer
+{
+  /* The part of the string that has already been converted to UTF-8.  */
+  char *utf8_buffer;
+  size_t utf8_buflen;
+  size_t utf8_allocated;
+  /* The first half of an UTF-16 surrogate character.  */
+  unsigned short utf16_surr;
+  /* The part of the string that is still in the source encoding.  */
+  char *curr_buffer;
+  size_t curr_buflen;
+  size_t curr_allocated;
+  /* The lexical context.  Used only for error message purposes.  */
+  lexical_context_ty lcontext;
+  const char *logical_file_name;
+  int line_number;
+};
+
+/* Creates a fresh mixed_string_buffer.  */
+extern struct mixed_string_buffer *
+       mixed_string_buffer_alloc (lexical_context_ty lcontext,
+                                  const char *logical_file_name,
+                                  int line_number);
+
+/* Appends a character to a mixed_string_buffer.  */
+extern void mixed_string_buffer_append_char (struct mixed_string_buffer *bp,
+                                             int c);
+
+/* Appends a Unicode character to a mixed_string_buffer.  */
+extern void mixed_string_buffer_append_unicode (struct mixed_string_buffer *bp,
+                                                int c);
+
+/* Frees mixed_string_buffer and returns the accumulated string in UTF-8.  */
+extern char * mixed_string_buffer_done (struct mixed_string_buffer *bp);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _XGETTEXT_MIXED_STRING_H */
index adec0dff7413f911e1e398582c6dcedcb2a9d64b..9c9c038419b52e8e6de66a55e9955f1b7c6ee6d6 100644 (file)
@@ -20,6 +20,9 @@
 #endif
 #include <alloca.h>
 
+/* Specification.  */
+#include "xgettext.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <getopt.h>
 #include <locale.h>
 #include <limits.h>
 
-#include "xgettext.h"
+#if HAVE_ICONV
+#include <iconv.h>
+#endif
+
+#include "rc-str-list.h"
+#include "xg-encoding.h"
+#include "xg-arglist-context.h"
+#include "xg-message.h"
 #include "closeout.h"
 #include "dir-list.h"
 #include "file-list.h"
@@ -44,7 +54,6 @@
 #include "basename.h"
 #include "xerror.h"
 #include "xvasprintf.h"
-#include "xsize.h"
 #include "xalloc.h"
 #include "xmalloca.h"
 #include "c-strstr.h"
@@ -60,7 +69,6 @@
 #include "msgl-iconv.h"
 #include "msgl-ascii.h"
 #include "msgl-check.h"
-#include "po-xerror.h"
 #include "po-time.h"
 #include "write-catalog.h"
 #include "write-po.h"
@@ -70,7 +78,6 @@
 #include "format.h"
 #include "propername.h"
 #include "sentence.h"
-#include "unistr.h"
 #include "its.h"
 #include "locating-rule.h"
 #include "search-path.h"
 #define ENDOF(a) ((a) + SIZEOF(a))
 
 
-/* If nonzero add all comments immediately preceding one of the keywords. */
-static bool add_all_comments = false;
+/* If true, add all comments immediately preceding one of the keywords. */
+bool add_all_comments = false;
 
 /* Tag used in comment of prevailing domain.  */
-static char *comment_tag;
+char *comment_tag;
 
 /* Name of default domain file.  If not set defaults to messages.po.  */
 static const char *default_domain;
@@ -144,10 +151,10 @@ static const char *package_version = NULL;
 static const char *msgid_bugs_address = NULL;
 
 /* String used as prefix for msgstr.  */
-static const char *msgstr_prefix;
+const char *msgstr_prefix;
 
 /* String used as suffix for msgstr.  */
-static const char *msgstr_suffix;
+const char *msgstr_suffix;
 
 /* Directory in which output files are created.  */
 static char *output_dir;
@@ -192,25 +199,7 @@ static bool recognize_format_kde;
 static bool recognize_format_boost;
 
 /* Syntax checks enabled by default.  */
-static enum is_syntax_check default_syntax_check[NSYNTAXCHECKS];
-
-/* Canonicalized encoding name for all input files.  */
-const char *xgettext_global_source_encoding;
-
-#if HAVE_ICONV
-/* Converter from xgettext_global_source_encoding to UTF-8 (except from
-   ASCII or UTF-8, when this conversion is a no-op).  */
-iconv_t xgettext_global_source_iconv;
-#endif
-
-/* Canonicalized encoding name for the current input file.  */
-const char *xgettext_current_source_encoding;
-
-#if HAVE_ICONV
-/* Converter from xgettext_current_source_encoding to UTF-8 (except from
-   ASCII or UTF-8, when this conversion is a no-op).  */
-iconv_t xgettext_current_source_iconv;
-#endif
+enum is_syntax_check default_syntax_check[NSYNTAXCHECKS];
 
 static locating_rule_list_ty *its_locating_rules;
 
@@ -1316,315 +1305,6 @@ read_exclusion_file (char *filename)
 }
 
 
-void
-split_keywordspec (const char *spec,
-                   const char **endp, struct callshape *shapep)
-{
-  const char *p;
-  int argnum1 = 0;
-  int argnum2 = 0;
-  int argnumc = 0;
-  bool argnum1_glib_context = false;
-  bool argnum2_glib_context = false;
-  int argtotal = 0;
-  string_list_ty xcomments;
-
-  string_list_init (&xcomments);
-
-  /* Start parsing from the end.  */
-  p = spec + strlen (spec);
-  while (p > spec)
-    {
-      if (isdigit ((unsigned char) p[-1])
-          || ((p[-1] == 'c' || p[-1] == 'g' || p[-1] == 't')
-              && p - 1 > spec && isdigit ((unsigned char) p[-2])))
-        {
-          bool contextp = (p[-1] == 'c');
-          bool glibp = (p[-1] == 'g');
-          bool totalp = (p[-1] == 't');
-
-          do
-            p--;
-          while (p > spec && isdigit ((unsigned char) p[-1]));
-
-          if (p > spec && (p[-1] == ',' || p[-1] == ':'))
-            {
-              char *dummy;
-              int arg = strtol (p, &dummy, 10);
-
-              if (contextp)
-                {
-                  if (argnumc != 0)
-                    /* Only one context argument can be given.  */
-                    break;
-                  argnumc = arg;
-                }
-              else if (totalp)
-                {
-                  if (argtotal != 0)
-                    /* Only one total number of arguments can be given.  */
-                    break;
-                  argtotal = arg;
-                }
-              else
-                {
-                  if (argnum2 != 0)
-                    /* At most two normal arguments can be given.  */
-                    break;
-                  argnum2 = argnum1;
-                  argnum2_glib_context = argnum1_glib_context;
-                  argnum1 = arg;
-                  argnum1_glib_context = glibp;
-                }
-            }
-          else
-            break;
-        }
-      else if (p[-1] == '"')
-        {
-          const char *xcomment_end;
-
-          p--;
-          xcomment_end = p;
-
-          while (p > spec && p[-1] != '"')
-            p--;
-
-          if (p > spec /* && p[-1] == '"' */)
-            {
-              const char *xcomment_start;
-
-              xcomment_start = p;
-              p--;
-              if (p > spec && (p[-1] == ',' || p[-1] == ':'))
-                {
-                  size_t xcomment_len = xcomment_end - xcomment_start;
-                  char *xcomment = XNMALLOC (xcomment_len + 1, char);
-
-                  memcpy (xcomment, xcomment_start, xcomment_len);
-                  xcomment[xcomment_len] = '\0';
-                  string_list_append (&xcomments, xcomment);
-                }
-              else
-                break;
-            }
-          else
-            break;
-        }
-      else
-        break;
-
-      /* Here an element of the comma-separated list has been parsed.  */
-      if (!(p > spec && (p[-1] == ',' || p[-1] == ':')))
-        abort ();
-      p--;
-      if (*p == ':')
-        {
-          size_t i;
-
-          if (argnum1 == 0 && argnum2 == 0)
-            /* At least one non-context argument must be given.  */
-            break;
-          if (argnumc != 0
-              && (argnum1_glib_context || argnum2_glib_context))
-            /* Incompatible ways to specify the context.  */
-            break;
-          *endp = p;
-          shapep->argnum1 = (argnum1 > 0 ? argnum1 : 1);
-          shapep->argnum2 = argnum2;
-          shapep->argnumc = argnumc;
-          shapep->argnum1_glib_context = argnum1_glib_context;
-          shapep->argnum2_glib_context = argnum2_glib_context;
-          shapep->argtotal = argtotal;
-          /* Reverse the order of the xcomments.  */
-          string_list_init (&shapep->xcomments);
-          for (i = xcomments.nitems; i > 0; )
-            string_list_append (&shapep->xcomments, xcomments.item[--i]);
-          string_list_destroy (&xcomments);
-          return;
-        }
-    }
-
-  /* Couldn't parse the desired syntax.  */
-  *endp = spec + strlen (spec);
-  shapep->argnum1 = 1;
-  shapep->argnum2 = 0;
-  shapep->argnumc = 0;
-  shapep->argnum1_glib_context = false;
-  shapep->argnum2_glib_context = false;
-  shapep->argtotal = 0;
-  string_list_init (&shapep->xcomments);
-  string_list_destroy (&xcomments);
-}
-
-
-void
-insert_keyword_callshape (hash_table *table,
-                          const char *keyword, size_t keyword_len,
-                          const struct callshape *shape)
-{
-  void *old_value;
-
-  if (hash_find_entry (table, keyword, keyword_len, &old_value))
-    {
-      /* Create a one-element 'struct callshapes'.  */
-      struct callshapes *shapes = XMALLOC (struct callshapes);
-      shapes->nshapes = 1;
-      shapes->shapes[0] = *shape;
-      keyword =
-        (const char *) hash_insert_entry (table, keyword, keyword_len, shapes);
-      if (keyword == NULL)
-        abort ();
-      shapes->keyword = keyword;
-      shapes->keyword_len = keyword_len;
-    }
-  else
-    {
-      /* Found a 'struct callshapes'.  See whether it already contains the
-         desired shape.  */
-      struct callshapes *old_shapes = (struct callshapes *) old_value;
-      bool found;
-      size_t i;
-
-      found = false;
-      for (i = 0; i < old_shapes->nshapes; i++)
-        if (old_shapes->shapes[i].argnum1 == shape->argnum1
-            && old_shapes->shapes[i].argnum2 == shape->argnum2
-            && old_shapes->shapes[i].argnumc == shape->argnumc
-            && old_shapes->shapes[i].argnum1_glib_context
-               == shape->argnum1_glib_context
-            && old_shapes->shapes[i].argnum2_glib_context
-               == shape->argnum2_glib_context
-            && old_shapes->shapes[i].argtotal == shape->argtotal)
-          {
-            old_shapes->shapes[i].xcomments = shape->xcomments;
-            found = true;
-            break;
-          }
-
-      if (!found)
-        {
-          /* Replace the existing 'struct callshapes' with a new one.  */
-          struct callshapes *shapes =
-            (struct callshapes *)
-            xmalloc (xsum (sizeof (struct callshapes),
-                           xtimes (old_shapes->nshapes,
-                                   sizeof (struct callshape))));
-
-          shapes->keyword = old_shapes->keyword;
-          shapes->keyword_len = old_shapes->keyword_len;
-          shapes->nshapes = old_shapes->nshapes + 1;
-          for (i = 0; i < old_shapes->nshapes; i++)
-            shapes->shapes[i] = old_shapes->shapes[i];
-          shapes->shapes[i] = *shape;
-          if (hash_set_value (table, keyword, keyword_len, shapes))
-            abort ();
-          free (old_shapes);
-        }
-    }
-}
-
-
-/* Null context.  */
-flag_context_ty null_context = { undecided, false, undecided, false };
-
-/* Transparent context.  */
-flag_context_ty passthrough_context = { undecided, true, undecided, true };
-
-
-flag_context_ty
-inherited_context (flag_context_ty outer_context,
-                   flag_context_ty modifier_context)
-{
-  flag_context_ty result = modifier_context;
-
-  if (result.pass_format1)
-    {
-      result.is_format1 = outer_context.is_format1;
-      result.pass_format1 = false;
-    }
-  if (result.pass_format2)
-    {
-      result.is_format2 = outer_context.is_format2;
-      result.pass_format2 = false;
-    }
-  if (result.pass_format3)
-    {
-      result.is_format3 = outer_context.is_format3;
-      result.pass_format3 = false;
-    }
-  return result;
-}
-
-
-/* Null context list iterator.  */
-flag_context_list_iterator_ty null_context_list_iterator = { 1, NULL };
-
-/* Transparent context list iterator.  */
-static flag_context_list_ty passthrough_context_circular_list =
-  {
-    1,
-    { undecided, true, undecided, true },
-    &passthrough_context_circular_list
-  };
-flag_context_list_iterator_ty passthrough_context_list_iterator =
-  {
-    1,
-    &passthrough_context_circular_list
-  };
-
-
-flag_context_list_iterator_ty
-flag_context_list_iterator (flag_context_list_ty *list)
-{
-  flag_context_list_iterator_ty result;
-
-  result.argnum = 1;
-  result.head = list;
-  return result;
-}
-
-
-flag_context_ty
-flag_context_list_iterator_advance (flag_context_list_iterator_ty *iter)
-{
-  if (iter->head == NULL)
-    return null_context;
-  if (iter->argnum == iter->head->argnum)
-    {
-      flag_context_ty result = iter->head->flags;
-
-      /* Special casing of circular list.  */
-      if (iter->head != iter->head->next)
-        {
-          iter->head = iter->head->next;
-          iter->argnum++;
-        }
-
-      return result;
-    }
-  else
-    {
-      iter->argnum++;
-      return null_context;
-    }
-}
-
-
-flag_context_list_ty *
-flag_context_list_table_lookup (flag_context_list_table_ty *flag_table,
-                                const void *key, size_t keylen)
-{
-  void *entry;
-
-  if (flag_table->table != NULL
-      && hash_find_entry (flag_table, key, keylen, &entry) == 0)
-    return (flag_context_list_ty *) entry;
-  else
-    return NULL;
-}
-
-
 static void
 flag_context_list_table_insert (flag_context_list_table_ty *table,
                                 unsigned int index,
@@ -1655,135 +1335,13 @@ flag_context_list_table_insert (flag_context_list_table_ty *table,
         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)
-    hash_init (table, 100);
-  {
-    void *entry;
-
-    if (hash_find_entry (table, name_start, name_end - name_start, &entry) != 0)
-      {
-        /* Create new hash table entry.  */
-        flag_context_list_ty *list = XMALLOC (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;
-          case 2:
-            list->flags.is_format3 = value;
-            list->flags.pass_format3 = pass;
-            break;
-          default:
-            abort ();
-          }
-        list->next = NULL;
-        hash_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;
-        /* Invariant: list == (lastp != NULL ? *lastp : entry).  */
-
-        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;
-              case 2:
-                list->flags.is_format3 = value;
-                list->flags.pass_format3 = pass;
-                break;
-              default:
-                abort ();
-              }
-          }
-        else if (lastp != NULL)
-          {
-            /* Add a new list entry for this argument number.  */
-            list = XMALLOC (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;
-              case 2:
-                list->flags.is_format3 = value;
-                list->flags.pass_format3 = 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 = XMALLOC (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;
-              case 2:
-                list->flags.is_format3 = value;
-                list->flags.pass_format3 = pass;
-                break;
-              default:
-                abort ();
-              }
-            list->next = copy;
-          }
-      }
-  }
+  flag_context_list_table_add (table, index, name_start, name_end,
+                               argnum, value, pass);
 
   if (allocated_name != NULL)
     freea (allocated_name);
 }
 
-
 void
 xgettext_record_flag (const char *optionstring)
 {
@@ -2058,7 +1616,7 @@ xgettext_comment_add (const char *str)
   string_list_append (comment, str);
 }
 
-static const char *
+const char *
 xgettext_comment (size_t n)
 {
   if (comment == NULL || n >= comment->nitems)
@@ -2066,8 +1624,8 @@ xgettext_comment (size_t n)
   return comment->item[n];
 }
 
-static void
-xgettext_comment_reset ()
+void
+xgettext_comment_reset (void)
 {
   if (comment != NULL)
     {
@@ -2113,7 +1671,7 @@ savable_comment_reset ()
   savable_comment = NULL;
 }
 
-static void
+void
 savable_comment_to_xgettext_comment (refcounted_string_list_ty *rslp)
 {
   xgettext_comment_reset ();
@@ -2191,9 +1749,9 @@ error while opening \"%s\" for reading"), new_name);
 
 /* Language dependent format string parser.
    NULL if the language has no notion of format strings.  */
-static struct formatstring_parser *current_formatstring_parser1;
-static struct formatstring_parser *current_formatstring_parser2;
-static struct formatstring_parser *current_formatstring_parser3;
+struct formatstring_parser *current_formatstring_parser1;
+struct formatstring_parser *current_formatstring_parser2;
+struct formatstring_parser *current_formatstring_parser3;
 
 
 static void
@@ -2283,1323 +1841,11 @@ extract_from_xml_file (const char *file_name,
 }
 
 
-
-/* Error message about non-ASCII character in a specific lexical context.  */
-char *
-non_ascii_error_message (lexical_context_ty lcontext,
-                         const char *file_name, size_t line_number)
-{
-  char buffer[21];
-  char *errmsg;
-
-  if (line_number == (size_t)(-1))
-    buffer[0] = '\0';
-  else
-    sprintf (buffer, ":%ld", (long) line_number);
-
-  switch (lcontext)
-    {
-    case lc_outside:
-      errmsg =
-        xasprintf (_("Non-ASCII character at %s%s."), file_name, buffer);
-      break;
-    case lc_comment:
-      errmsg =
-        xasprintf (_("Non-ASCII comment at or before %s%s."),
-                   file_name, buffer);
-      break;
-    case lc_string:
-      errmsg =
-        xasprintf (_("Non-ASCII string at %s%s."), file_name, buffer);
-      break;
-    default:
-      abort ();
-    }
-  return errmsg;
-}
-
-/* Convert the given string from xgettext_current_source_encoding to
-   the output file encoding (i.e. ASCII or UTF-8).
-   The resulting string is either the argument string, or freshly allocated.
-   The file_name and line_number are only used for error message purposes.  */
-char *
-from_current_source_encoding (const char *string,
-                              lexical_context_ty lcontext,
-                              const char *file_name, size_t line_number)
-{
-  if (xgettext_current_source_encoding == po_charset_ascii)
-    {
-      if (!is_ascii_string (string))
-        {
-          multiline_error (xstrdup (""),
-                           xasprintf ("%s\n%s\n",
-                                      non_ascii_error_message (lcontext,
-                                                               file_name,
-                                                               line_number),
-                                      _("\
-Please specify the source encoding through --from-code.")));
-          exit (EXIT_FAILURE);
-        }
-    }
-  else if (xgettext_current_source_encoding != po_charset_utf8)
-    {
-#if HAVE_ICONV
-      struct conversion_context context;
-
-      context.from_code = xgettext_current_source_encoding;
-      context.to_code = po_charset_utf8;
-      context.from_filename = file_name;
-      context.message = NULL;
-
-      string = convert_string_directly (xgettext_current_source_iconv, string,
-                                        &context);
-#else
-      /* If we don't have iconv(), the only supported values for
-         xgettext_global_source_encoding and thus also for
-         xgettext_current_source_encoding are ASCII and UTF-8.
-         convert_string_directly() should not be called in this case.  */
-      abort ();
-#endif
-    }
-
-  return (char *) string;
-}
-
-#define CONVERT_STRING(string, lcontext) \
-  string = from_current_source_encoding (string, lcontext, pos->file_name, \
-                                         pos->line_number);
-
-
-/* Update the is_format[] flags depending on the information given in the
-   context.  */
-static void
-set_format_flags_from_context (enum is_format is_format[NFORMATS],
-                               flag_context_ty context, const char *string,
-                               lex_pos_ty *pos, const char *pretty_msgstr)
-{
-  size_t i;
-
-  if (context.is_format1 != undecided
-      || context.is_format2 != undecided
-      || context.is_format3 != undecided)
-    for (i = 0; i < NFORMATS; i++)
-      {
-        if (is_format[i] == undecided)
-          {
-            if (formatstring_parsers[i] == current_formatstring_parser1
-                && context.is_format1 != undecided)
-              is_format[i] = (enum is_format) context.is_format1;
-            if (formatstring_parsers[i] == current_formatstring_parser2
-                && context.is_format2 != undecided)
-              is_format[i] = (enum is_format) context.is_format2;
-            if (formatstring_parsers[i] == current_formatstring_parser3
-                && context.is_format3 != undecided)
-              is_format[i] = (enum is_format) context.is_format3;
-          }
-        if (possible_format_p (is_format[i]))
-          {
-            struct formatstring_parser *parser = formatstring_parsers[i];
-            char *invalid_reason = NULL;
-            void *descr = parser->parse (string, false, NULL, &invalid_reason);
-
-            if (descr != NULL)
-              parser->free (descr);
-            else
-              {
-                /* The string is not a valid format string.  */
-                if (is_format[i] != possible)
-                  {
-                    char buffer[21];
-
-                    error_with_progname = false;
-                    if (pos->line_number == (size_t)(-1))
-                      buffer[0] = '\0';
-                    else
-                      sprintf (buffer, ":%ld", (long) pos->line_number);
-                    multiline_warning (xasprintf (_("%s%s: warning: "),
-                                                  pos->file_name, buffer),
-                                       xasprintf (is_format[i] == yes_according_to_context
-                                                  ? _("Although being used in a format string position, the %s is not a valid %s format string. Reason: %s\n")
-                                                  : _("Although declared as such, the %s is not a valid %s format string. Reason: %s\n"),
-                                                  pretty_msgstr,
-                                                  format_language_pretty[i],
-                                                  invalid_reason));
-                    error_with_progname = true;
-                  }
-
-                is_format[i] = impossible;
-                free (invalid_reason);
-              }
-          }
-      }
-}
-
-
-static void
-warn_format_string (enum is_format is_format[NFORMATS], const char *string,
-                    lex_pos_ty *pos, const char *pretty_msgstr)
-{
-  if (possible_format_p (is_format[format_python])
-      && get_python_format_unnamed_arg_count (string) > 1)
-    {
-      char buffer[21];
-
-      error_with_progname = false;
-      if (pos->line_number == (size_t)(-1))
-        buffer[0] = '\0';
-      else
-        sprintf (buffer, ":%ld", (long) pos->line_number);
-      multiline_warning (xasprintf (_("%s%s: warning: "),
-                                    pos->file_name, buffer),
-                         xasprintf (_("\
-'%s' format string with unnamed arguments cannot be properly localized:\n\
-The translator cannot reorder the arguments.\n\
-Please consider using a format string with named arguments,\n\
-and a mapping instead of a tuple for the arguments.\n"),
-                                    pretty_msgstr));
-      error_with_progname = true;
-    }
-}
-
-
-message_ty *
-remember_a_message (message_list_ty *mlp, char *msgctxt, char *msgid,
-                    flag_context_ty context, lex_pos_ty *pos,
-                    const char *extracted_comment,
-                    refcounted_string_list_ty *comment)
-{
-  enum is_format is_format[NFORMATS];
-  struct argument_range range;
-  enum is_wrap do_wrap;
-  enum is_syntax_check do_syntax_check[NSYNTAXCHECKS];
-  message_ty *mp;
-  char *msgstr;
-  size_t i;
-
-  /* See whether we shall exclude this message.  */
-  if (exclude != NULL && message_list_search (exclude, msgctxt, msgid) != NULL)
-    {
-      /* Tell the lexer to reset its comment buffer, so that the next
-         message gets the correct comments.  */
-      xgettext_comment_reset ();
-      savable_comment_reset ();
-
-      if (msgctxt != NULL)
-        free (msgctxt);
-      free (msgid);
-
-      return NULL;
-    }
-
-  savable_comment_to_xgettext_comment (comment);
-
-  for (i = 0; i < NFORMATS; i++)
-    is_format[i] = undecided;
-  range.min = -1;
-  range.max = -1;
-  do_wrap = undecided;
-  for (i = 0; i < NSYNTAXCHECKS; i++)
-    do_syntax_check[i] = undecided;
-
-  if (msgctxt != NULL)
-    CONVERT_STRING (msgctxt, lc_string);
-  CONVERT_STRING (msgid, lc_string);
-
-  if (msgctxt == NULL && msgid[0] == '\0' && !xgettext_omit_header)
-    {
-      char buffer[21];
-
-      error_with_progname = false;
-      if (pos->line_number == (size_t)(-1))
-        buffer[0] = '\0';
-      else
-        sprintf (buffer, ":%ld", (long) pos->line_number);
-      multiline_warning (xasprintf (_("%s%s: warning: "), pos->file_name,
-                                    buffer),
-                         xstrdup (_("\
-Empty msgid.  It is reserved by GNU gettext:\n\
-gettext(\"\") returns the header entry with\n\
-meta information, not the empty string.\n")));
-      error_with_progname = true;
-    }
-
-  /* See if we have seen this message before.  */
-  mp = message_list_search (mlp, msgctxt, msgid);
-  if (mp != NULL)
-    {
-      if (msgctxt != NULL)
-        free (msgctxt);
-      free (msgid);
-      for (i = 0; i < NFORMATS; i++)
-        is_format[i] = mp->is_format[i];
-      do_wrap = mp->do_wrap;
-      for (i = 0; i < NSYNTAXCHECKS; i++)
-        do_syntax_check[i] = mp->do_syntax_check[i];
-    }
-  else
-    {
-      /* Construct the msgstr from the prefix and suffix, otherwise use the
-         empty string.  */
-      if (msgstr_prefix)
-        msgstr = xasprintf ("%s%s%s", msgstr_prefix, msgid, msgstr_suffix);
-      else
-        msgstr = "";
-
-      /* Allocate a new message and append the message to the list.  */
-      mp = message_alloc (msgctxt, msgid, NULL, msgstr, strlen (msgstr) + 1,
-                          pos);
-      /* Do not free msgctxt and msgid.  */
-      message_list_append (mlp, mp);
-    }
-
-  /* Determine whether the context specifies that the msgid is a format
-     string.  */
-  set_format_flags_from_context (is_format, context, mp->msgid, pos, "msgid");
-
-  /* Ask the lexer for the comments it has seen.  */
-  {
-    size_t nitems_before;
-    size_t nitems_after;
-    int j;
-    bool add_all_remaining_comments;
-    /* The string before the comment tag.  For example, If "** TRANSLATORS:"
-       is seen and the comment tag is "TRANSLATORS:",
-       then comment_tag_prefix is set to "** ".  */
-    const char *comment_tag_prefix = "";
-    size_t comment_tag_prefix_length = 0;
-
-    nitems_before = (mp->comment_dot != NULL ? mp->comment_dot->nitems : 0);
-
-    if (extracted_comment != NULL)
-      {
-        char *copy = xstrdup (extracted_comment);
-        char *rest;
-
-        rest = copy;
-        while (*rest != '\0')
-          {
-            char *newline = strchr (rest, '\n');
-
-            if (newline != NULL)
-              {
-                *newline = '\0';
-                message_comment_dot_append (mp, rest);
-                rest = newline + 1;
-              }
-            else
-              {
-                message_comment_dot_append (mp, rest);
-                break;
-              }
-          }
-        free (copy);
-      }
-
-    add_all_remaining_comments = add_all_comments;
-    for (j = 0; ; ++j)
-      {
-        const char *s = xgettext_comment (j);
-        const char *t;
-        if (s == NULL)
-          break;
-
-        CONVERT_STRING (s, lc_comment);
-
-        /* To reduce the possibility of unwanted matches we do a two
-           step match: the line must contain 'xgettext:' and one of
-           the possible format description strings.  */
-        if ((t = c_strstr (s, "xgettext:")) != NULL)
-          {
-            bool tmp_fuzzy;
-            enum is_format tmp_format[NFORMATS];
-            struct argument_range tmp_range;
-            enum is_wrap tmp_wrap;
-            enum is_syntax_check tmp_syntax_check[NSYNTAXCHECKS];
-            bool interesting;
-
-            t += strlen ("xgettext:");
-
-            po_parse_comment_special (t, &tmp_fuzzy, tmp_format, &tmp_range,
-                                      &tmp_wrap, tmp_syntax_check);
-
-            interesting = false;
-            for (i = 0; i < NFORMATS; i++)
-              if (tmp_format[i] != undecided)
-                {
-                  is_format[i] = tmp_format[i];
-                  interesting = true;
-                }
-            if (has_range_p (tmp_range))
-              {
-                range = tmp_range;
-                interesting = true;
-              }
-            if (tmp_wrap != undecided)
-              {
-                do_wrap = tmp_wrap;
-                interesting = true;
-              }
-            for (i = 0; i < NSYNTAXCHECKS; i++)
-              if (tmp_syntax_check[i] != undecided)
-                {
-                  do_syntax_check[i] = tmp_syntax_check[i];
-                  interesting = true;
-                }
-
-            /* If the "xgettext:" marker was followed by an interesting
-               keyword, and we updated our is_format/do_wrap variables,
-               we don't print the comment as a #. comment.  */
-            if (interesting)
-              continue;
-          }
-
-        if (!add_all_remaining_comments && comment_tag != NULL)
-          {
-            /* When the comment tag is seen, it drags in not only the line
-               which it starts, but all remaining comment lines.  */
-            if ((t = c_strstr (s, comment_tag)) != NULL)
-              {
-                add_all_remaining_comments = true;
-                comment_tag_prefix = s;
-                comment_tag_prefix_length = t - s;
-              }
-          }
-
-        if (add_all_remaining_comments)
-          {
-            if (strncmp (s, comment_tag_prefix, comment_tag_prefix_length) == 0)
-              s += comment_tag_prefix_length;
-            message_comment_dot_append (mp, s);
-          }
-      }
-
-    nitems_after = (mp->comment_dot != NULL ? mp->comment_dot->nitems : 0);
-
-    /* Don't add the comments if they are a repetition of the tail of the
-       already present comments.  This avoids unneeded duplication if the
-       same message appears several times, each time with the same comment.  */
-    if (nitems_before < nitems_after)
-      {
-        size_t added = nitems_after - nitems_before;
-
-        if (added <= nitems_before)
-          {
-            bool repeated = true;
-
-            for (i = 0; i < added; i++)
-              if (strcmp (mp->comment_dot->item[nitems_before - added + i],
-                          mp->comment_dot->item[nitems_before + i]) != 0)
-                {
-                  repeated = false;
-                  break;
-                }
-
-            if (repeated)
-              {
-                for (i = 0; i < added; i++)
-                  free ((char *) mp->comment_dot->item[nitems_before + i]);
-                mp->comment_dot->nitems = nitems_before;
-              }
-          }
-      }
-  }
-
-  /* If it is not already decided, through programmer comments, whether the
-     msgid is a format string, examine the msgid.  This is a heuristic.  */
-  for (i = 0; i < NFORMATS; i++)
-    {
-      if (is_format[i] == undecided
-          && (formatstring_parsers[i] == current_formatstring_parser1
-              || formatstring_parsers[i] == current_formatstring_parser2
-              || formatstring_parsers[i] == current_formatstring_parser3)
-          /* 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]))
-          /* Avoid flagging a string as c-format when it's known to be a
-             qt-format or qt-plural-format or kde-format or boost-format
-             string.  */
-          && !(i == format_c
-               && (possible_format_p (is_format[format_qt])
-                   || possible_format_p (is_format[format_qt_plural])
-                   || possible_format_p (is_format[format_kde])
-                   || possible_format_p (is_format[format_kde_kuit])
-                   || possible_format_p (is_format[format_boost])))
-          /* Avoid flagging a string as kde-format when it's known to
-             be a kde-kuit-format string.  */
-          && !(i == format_kde
-               && possible_format_p (is_format[format_kde_kuit]))
-          /* Avoid flagging a string as kde-kuit-format when it's
-             known to be a kde-format string.  Note that this relies
-             on the fact that format_kde < format_kde_kuit, so a
-             string will be marked as kde-format if both are
-             undecided.  */
-          && !(i == format_kde_kuit
-               && possible_format_p (is_format[format_kde])))
-        {
-          struct formatstring_parser *parser = formatstring_parsers[i];
-          char *invalid_reason = NULL;
-          void *descr = parser->parse (mp->msgid, false, NULL, &invalid_reason);
-
-          if (descr != NULL)
-            {
-              /* msgid is a valid format string.  We mark only those msgids
-                 as format strings which contain at least one format directive
-                 and thus are format strings with a high probability.  We
-                 don't mark strings without directives as format strings,
-                 because that would force the programmer to add
-                 "xgettext: no-c-format" anywhere where a translator wishes
-                 to use a percent sign.  So, the msgfmt checking will not be
-                 perfect.  Oh well.  */
-              if (parser->get_number_of_directives (descr) > 0
-                  && !(parser->is_unlikely_intentional != NULL
-                       && parser->is_unlikely_intentional (descr)))
-                is_format[i] = possible;
-
-              parser->free (descr);
-            }
-          else
-            {
-              /* msgid is not a valid format string.  */
-              is_format[i] = impossible;
-              free (invalid_reason);
-            }
-        }
-      mp->is_format[i] = is_format[i];
-    }
-
-  if (has_range_p (range))
-    {
-      if (has_range_p (mp->range))
-        {
-          if (range.min < mp->range.min)
-            mp->range.min = range.min;
-          if (range.max > mp->range.max)
-            mp->range.max = range.max;
-        }
-      else
-        mp->range = range;
-    }
-
-  mp->do_wrap = do_wrap == no ? no : yes;       /* By default we wrap.  */
-
-  for (i = 0; i < NSYNTAXCHECKS; i++)
-    {
-      if (do_syntax_check[i] == undecided)
-        do_syntax_check[i] = default_syntax_check[i] == yes ? yes : no;
-
-      mp->do_syntax_check[i] = do_syntax_check[i];
-    }
-
-  /* Warn about the use of non-reorderable format strings when the programming
-     language also provides reorderable format strings.  */
-  warn_format_string (is_format, mp->msgid, pos, "msgid");
-
-  /* Remember where we saw this msgid.  */
-  message_comment_filepos (mp, pos->file_name, pos->line_number);
-
-  /* Tell the lexer to reset its comment buffer, so that the next
-     message gets the correct comments.  */
-  xgettext_comment_reset ();
-  savable_comment_reset ();
-
-  return mp;
-}
-
-
-void
-remember_a_message_plural (message_ty *mp, char *string,
-                           flag_context_ty context, lex_pos_ty *pos,
-                           refcounted_string_list_ty *comment)
-{
-  char *msgid_plural;
-  char *msgstr1;
-  size_t msgstr1_len;
-  char *msgstr;
-  size_t i;
-
-  msgid_plural = string;
-
-  savable_comment_to_xgettext_comment (comment);
-
-  CONVERT_STRING (msgid_plural, lc_string);
-
-  /* See if the message is already a plural message.  */
-  if (mp->msgid_plural == NULL)
-    {
-      mp->msgid_plural = msgid_plural;
-
-      /* Construct the first plural form from the prefix and suffix,
-         otherwise use the empty string.  The translator will have to
-         provide additional plural forms.  */
-      if (msgstr_prefix)
-        msgstr1 =
-          xasprintf ("%s%s%s", msgstr_prefix, msgid_plural, msgstr_suffix);
-      else
-        msgstr1 = "";
-      msgstr1_len = strlen (msgstr1) + 1;
-      msgstr = XNMALLOC (mp->msgstr_len + msgstr1_len, char);
-      memcpy (msgstr, mp->msgstr, mp->msgstr_len);
-      memcpy (msgstr + mp->msgstr_len, msgstr1, msgstr1_len);
-      mp->msgstr = msgstr;
-      mp->msgstr_len = mp->msgstr_len + msgstr1_len;
-      if (msgstr_prefix)
-        free (msgstr1);
-
-      /* Determine whether the context specifies that the msgid_plural is a
-         format string.  */
-      set_format_flags_from_context (mp->is_format, context, mp->msgid_plural,
-                                     pos, "msgid_plural");
-
-      /* If it is not already decided, through programmer comments or
-         the msgid, whether the msgid is a format string, examine the
-         msgid_plural.  This is a heuristic.  */
-      for (i = 0; i < NFORMATS; i++)
-        if ((formatstring_parsers[i] == current_formatstring_parser1
-             || formatstring_parsers[i] == current_formatstring_parser2
-             || formatstring_parsers[i] == current_formatstring_parser3)
-            && (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]))
-            /* Avoid flagging a string as c-format when it's known to be a
-               qt-format or qt-plural-format or boost-format string.  */
-            && !(i == format_c
-                 && (possible_format_p (mp->is_format[format_qt])
-                     || possible_format_p (mp->is_format[format_qt_plural])
-                     || possible_format_p (mp->is_format[format_kde])
-                     || possible_format_p (mp->is_format[format_kde_kuit])
-                     || possible_format_p (mp->is_format[format_boost])))
-            /* Avoid flagging a string as kde-format when it's known
-               to be a kde-kuit-format string.  */
-            && !(i == format_kde
-                 && possible_format_p (mp->is_format[format_kde_kuit]))
-            /* Avoid flagging a string as kde-kuit-format when it's
-               known to be a kde-format string.  Note that this relies
-               on the fact that format_kde < format_kde_kuit, so a
-               string will be marked as kde-format if both are
-               undecided.  */
-            && !(i == format_kde_kuit
-                 && possible_format_p (mp->is_format[format_kde])))
-          {
-            struct formatstring_parser *parser = formatstring_parsers[i];
-            char *invalid_reason = NULL;
-            void *descr =
-              parser->parse (mp->msgid_plural, false, NULL, &invalid_reason);
-
-            if (descr != NULL)
-              {
-                /* Same heuristic as in remember_a_message.  */
-                if (parser->get_number_of_directives (descr) > 0
-                    && !(parser->is_unlikely_intentional != NULL
-                         && parser->is_unlikely_intentional (descr)))
-                  mp->is_format[i] = possible;
-
-                parser->free (descr);
-              }
-            else
-              {
-                /* msgid_plural is not a valid format string.  */
-                mp->is_format[i] = impossible;
-                free (invalid_reason);
-              }
-          }
-
-      /* Warn about the use of non-reorderable format strings when the programming
-         language also provides reorderable format strings.  */
-      warn_format_string (mp->is_format, mp->msgid_plural, pos, "msgid_plural");
-    }
-  else
-    free (msgid_plural);
-
-  /* Tell the lexer to reset its comment buffer, so that the next
-     message gets the correct comments.  */
-  xgettext_comment_reset ();
-  savable_comment_reset ();
-}
-
-
-struct arglist_parser *
-arglist_parser_alloc (message_list_ty *mlp, const struct callshapes *shapes)
-{
-  if (shapes == NULL || shapes->nshapes == 0)
-    {
-      struct arglist_parser *ap =
-        (struct arglist_parser *)
-        xmalloc (offsetof (struct arglist_parser, alternative[0]));
-
-      ap->mlp = mlp;
-      ap->keyword = NULL;
-      ap->keyword_len = 0;
-      ap->next_is_msgctxt = false;
-      ap->nalternatives = 0;
-
-      return ap;
-    }
-  else
-    {
-      struct arglist_parser *ap =
-        (struct arglist_parser *)
-        xmalloc (xsum (sizeof (struct arglist_parser),
-                       xtimes (shapes->nshapes - 1,
-                               sizeof (struct partial_call))));
-      size_t i;
-
-      ap->mlp = mlp;
-      ap->keyword = shapes->keyword;
-      ap->keyword_len = shapes->keyword_len;
-      ap->next_is_msgctxt = false;
-      ap->nalternatives = shapes->nshapes;
-      for (i = 0; i < shapes->nshapes; i++)
-        {
-          ap->alternative[i].argnumc = shapes->shapes[i].argnumc;
-          ap->alternative[i].argnum1 = shapes->shapes[i].argnum1;
-          ap->alternative[i].argnum2 = shapes->shapes[i].argnum2;
-          ap->alternative[i].argnum1_glib_context =
-            shapes->shapes[i].argnum1_glib_context;
-          ap->alternative[i].argnum2_glib_context =
-            shapes->shapes[i].argnum2_glib_context;
-          ap->alternative[i].argtotal = shapes->shapes[i].argtotal;
-          ap->alternative[i].xcomments = shapes->shapes[i].xcomments;
-          ap->alternative[i].msgctxt = NULL;
-          ap->alternative[i].msgctxt_pos.file_name = NULL;
-          ap->alternative[i].msgctxt_pos.line_number = (size_t)(-1);
-          ap->alternative[i].msgid = NULL;
-          ap->alternative[i].msgid_context = null_context;
-          ap->alternative[i].msgid_pos.file_name = NULL;
-          ap->alternative[i].msgid_pos.line_number = (size_t)(-1);
-          ap->alternative[i].msgid_comment = NULL;
-          ap->alternative[i].msgid_plural = NULL;
-          ap->alternative[i].msgid_plural_context = null_context;
-          ap->alternative[i].msgid_plural_pos.file_name = NULL;
-          ap->alternative[i].msgid_plural_pos.line_number = (size_t)(-1);
-        }
-
-      return ap;
-    }
-}
-
-
-struct arglist_parser *
-arglist_parser_clone (struct arglist_parser *ap)
-{
-  struct arglist_parser *copy =
-    (struct arglist_parser *)
-    xmalloc (xsum (sizeof (struct arglist_parser) - sizeof (struct partial_call),
-                   xtimes (ap->nalternatives, sizeof (struct partial_call))));
-  size_t i;
-
-  copy->mlp = ap->mlp;
-  copy->keyword = ap->keyword;
-  copy->keyword_len = ap->keyword_len;
-  copy->next_is_msgctxt = ap->next_is_msgctxt;
-  copy->nalternatives = ap->nalternatives;
-  for (i = 0; i < ap->nalternatives; i++)
-    {
-      const struct partial_call *cp = &ap->alternative[i];
-      struct partial_call *ccp = &copy->alternative[i];
-
-      ccp->argnumc = cp->argnumc;
-      ccp->argnum1 = cp->argnum1;
-      ccp->argnum2 = cp->argnum2;
-      ccp->argnum1_glib_context = cp->argnum1_glib_context;
-      ccp->argnum2_glib_context = cp->argnum2_glib_context;
-      ccp->argtotal = cp->argtotal;
-      ccp->xcomments = cp->xcomments;
-      ccp->msgctxt = (cp->msgctxt != NULL ? xstrdup (cp->msgctxt) : NULL);
-      ccp->msgctxt_pos = cp->msgctxt_pos;
-      ccp->msgid = (cp->msgid != NULL ? xstrdup (cp->msgid) : NULL);
-      ccp->msgid_context = cp->msgid_context;
-      ccp->msgid_pos = cp->msgctxt_pos;
-      ccp->msgid_comment = add_reference (cp->msgid_comment);
-      ccp->msgid_plural =
-        (cp->msgid_plural != NULL ? xstrdup (cp->msgid_plural) : NULL);
-      ccp->msgid_plural_context = cp->msgid_plural_context;
-      ccp->msgid_plural_pos = cp->msgid_plural_pos;
-    }
-
-  return copy;
-}
-
-
-void
-arglist_parser_remember (struct arglist_parser *ap,
-                         int argnum, char *string,
-                         flag_context_ty context,
-                         char *file_name, size_t line_number,
-                         refcounted_string_list_ty *comment)
-{
-  bool stored_string = false;
-  size_t nalternatives = ap->nalternatives;
-  size_t i;
-
-  if (!(argnum > 0))
-    abort ();
-  for (i = 0; i < nalternatives; i++)
-    {
-      struct partial_call *cp = &ap->alternative[i];
-
-      if (argnum == cp->argnumc)
-        {
-          cp->msgctxt = string;
-          cp->msgctxt_pos.file_name = file_name;
-          cp->msgctxt_pos.line_number = line_number;
-          stored_string = true;
-          /* Mark msgctxt as done.  */
-          cp->argnumc = 0;
-        }
-      else
-        {
-          if (argnum == cp->argnum1)
-            {
-              cp->msgid = string;
-              cp->msgid_context = context;
-              cp->msgid_pos.file_name = file_name;
-              cp->msgid_pos.line_number = line_number;
-              cp->msgid_comment = add_reference (comment);
-              stored_string = true;
-              /* Mark msgid as done.  */
-              cp->argnum1 = 0;
-            }
-          if (argnum == cp->argnum2)
-            {
-              cp->msgid_plural = string;
-              cp->msgid_plural_context = context;
-              cp->msgid_plural_pos.file_name = file_name;
-              cp->msgid_plural_pos.line_number = line_number;
-              stored_string = true;
-              /* Mark msgid_plural as done.  */
-              cp->argnum2 = 0;
-            }
-        }
-    }
-  /* Note: There is a memory leak here: When string was stored but is later
-     not used by arglist_parser_done, we don't free it.  */
-  if (!stored_string)
-    free (string);
-}
-
-
-void
-arglist_parser_remember_msgctxt (struct arglist_parser *ap,
-                                 char *string,
-                                 flag_context_ty context,
-                                 char *file_name, size_t line_number)
-{
-  bool stored_string = false;
-  size_t nalternatives = ap->nalternatives;
-  size_t i;
-
-  for (i = 0; i < nalternatives; i++)
-    {
-      struct partial_call *cp = &ap->alternative[i];
-
-      cp->msgctxt = string;
-      cp->msgctxt_pos.file_name = file_name;
-      cp->msgctxt_pos.line_number = line_number;
-      stored_string = true;
-      /* Mark msgctxt as done.  */
-      cp->argnumc = 0;
-    }
-  /* Note: There is a memory leak here: When string was stored but is later
-     not used by arglist_parser_done, we don't free it.  */
-  if (!stored_string)
-    free (string);
-}
-
-
 bool
-arglist_parser_decidedp (struct arglist_parser *ap, int argnum)
-{
-  size_t i;
-
-  /* Test whether all alternatives are decided.
-     Note: A decided alternative can be complete
-       cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0
-       && cp->argtotal == 0
-     or it can be failed if no literal strings were found at the specified
-     argument positions:
-       cp->argnumc <= argnum && cp->argnum1 <= argnum && cp->argnum2 <= argnum
-     or it can be failed if the number of arguments is exceeded:
-       cp->argtotal > 0 && cp->argtotal < argnum
-   */
-  for (i = 0; i < ap->nalternatives; i++)
-    {
-      struct partial_call *cp = &ap->alternative[i];
-
-      if (!((cp->argnumc <= argnum
-             && cp->argnum1 <= argnum
-             && cp->argnum2 <= argnum)
-            || (cp->argtotal > 0 && cp->argtotal < argnum)))
-        /* cp is still undecided.  */
-        return false;
-    }
-  return true;
-}
-
-
-void
-arglist_parser_done (struct arglist_parser *ap, int argnum)
-{
-  size_t ncomplete;
-  size_t i;
-
-  /* Determine the number of complete calls.  */
-  ncomplete = 0;
-  for (i = 0; i < ap->nalternatives; i++)
-    {
-      struct partial_call *cp = &ap->alternative[i];
-
-      if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0
-          && (cp->argtotal == 0 || cp->argtotal == argnum))
-        ncomplete++;
-    }
-
-  if (ncomplete > 0)
-    {
-      struct partial_call *best_cp = NULL;
-      bool ambiguous = false;
-
-      /* Find complete calls where msgctxt, msgid, msgid_plural are all
-         provided.  */
-      for (i = 0; i < ap->nalternatives; i++)
-        {
-          struct partial_call *cp = &ap->alternative[i];
-
-          if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0
-              && (cp->argtotal == 0 || cp->argtotal == argnum)
-              && cp->msgctxt != NULL
-              && cp->msgid != NULL
-              && cp->msgid_plural != NULL)
-            {
-              if (best_cp != NULL)
-                {
-                  ambiguous = true;
-                  break;
-                }
-              best_cp = cp;
-            }
-        }
-
-      if (best_cp == NULL)
-        {
-          struct partial_call *best_cp1 = NULL;
-          struct partial_call *best_cp2 = NULL;
-
-          /* Find complete calls where msgctxt, msgid are provided.  */
-          for (i = 0; i < ap->nalternatives; i++)
-            {
-              struct partial_call *cp = &ap->alternative[i];
-
-              if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0
-                  && (cp->argtotal == 0 || cp->argtotal == argnum)
-                  && cp->msgctxt != NULL
-                  && cp->msgid != NULL)
-                {
-                  if (best_cp1 != NULL)
-                    {
-                      ambiguous = true;
-                      break;
-                    }
-                  best_cp1 = cp;
-                }
-            }
-
-          /* Find complete calls where msgid, msgid_plural are provided.  */
-          for (i = 0; i < ap->nalternatives; i++)
-            {
-              struct partial_call *cp = &ap->alternative[i];
-
-              if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0
-                  && (cp->argtotal == 0 || cp->argtotal == argnum)
-                  && cp->msgid != NULL
-                  && cp->msgid_plural != NULL)
-                {
-                  if (best_cp2 != NULL)
-                    {
-                      ambiguous = true;
-                      break;
-                    }
-                  best_cp2 = cp;
-                }
-            }
-
-          if (best_cp1 != NULL)
-            best_cp = best_cp1;
-          if (best_cp2 != NULL)
-            {
-              if (best_cp != NULL)
-                ambiguous = true;
-              else
-                best_cp = best_cp2;
-            }
-        }
-
-      if (best_cp == NULL)
-        {
-          /* Find complete calls where msgid is provided.  */
-          for (i = 0; i < ap->nalternatives; i++)
-            {
-              struct partial_call *cp = &ap->alternative[i];
-
-              if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0
-                  && (cp->argtotal == 0 || cp->argtotal == argnum)
-                  && cp->msgid != NULL)
-                {
-                  if (best_cp != NULL)
-                    {
-                      ambiguous = true;
-                      break;
-                    }
-                  best_cp = cp;
-                }
-            }
-        }
-
-      if (ambiguous)
-        {
-          error_with_progname = false;
-          error_at_line (0, 0,
-                         best_cp->msgid_pos.file_name,
-                         best_cp->msgid_pos.line_number,
-                         _("ambiguous argument specification for keyword '%.*s'"),
-                         (int) ap->keyword_len, ap->keyword);
-          error_with_progname = true;
-        }
-
-      if (best_cp != NULL)
-        {
-          /* best_cp indicates the best found complete call.
-             Now call remember_a_message.  */
-          message_ty *mp;
-
-          /* Split strings in the GNOME glib syntax "msgctxt|msgid".  */
-          if (best_cp->argnum1_glib_context || best_cp->argnum2_glib_context)
-            /* split_keywordspec should not allow the context to be specified
-               in two different ways.  */
-            if (best_cp->msgctxt != NULL)
-              abort ();
-          if (best_cp->argnum1_glib_context)
-            {
-              const char *separator = strchr (best_cp->msgid, '|');
-
-              if (separator == NULL)
-                {
-                  error_with_progname = false;
-                  error_at_line (0, 0,
-                                 best_cp->msgid_pos.file_name,
-                                 best_cp->msgid_pos.line_number,
-                                 _("warning: missing context for keyword '%.*s'"),
-                                 (int) ap->keyword_len, ap->keyword);
-                  error_with_progname = true;
-                }
-              else
-                {
-                  size_t ctxt_len = separator - best_cp->msgid;
-                  char *ctxt = XNMALLOC (ctxt_len + 1, char);
-
-                  memcpy (ctxt, best_cp->msgid, ctxt_len);
-                  ctxt[ctxt_len] = '\0';
-                  best_cp->msgctxt = ctxt;
-                  best_cp->msgid = xstrdup (separator + 1);
-                }
-            }
-          if (best_cp->msgid_plural != NULL && best_cp->argnum2_glib_context)
-            {
-              const char *separator = strchr (best_cp->msgid_plural, '|');
-
-              if (separator == NULL)
-                {
-                  error_with_progname = false;
-                  error_at_line (0, 0,
-                                 best_cp->msgid_plural_pos.file_name,
-                                 best_cp->msgid_plural_pos.line_number,
-                                 _("warning: missing context for plural argument of keyword '%.*s'"),
-                                 (int) ap->keyword_len, ap->keyword);
-                  error_with_progname = true;
-                }
-              else
-                {
-                  size_t ctxt_len = separator - best_cp->msgid_plural;
-                  char *ctxt = XNMALLOC (ctxt_len + 1, char);
-
-                  memcpy (ctxt, best_cp->msgid_plural, ctxt_len);
-                  ctxt[ctxt_len] = '\0';
-                  if (best_cp->msgctxt == NULL)
-                    best_cp->msgctxt = ctxt;
-                  else
-                    {
-                      if (strcmp (ctxt, best_cp->msgctxt) != 0)
-                        {
-                          error_with_progname = false;
-                          error_at_line (0, 0,
-                                         best_cp->msgid_plural_pos.file_name,
-                                         best_cp->msgid_plural_pos.line_number,
-                                         _("context mismatch between singular and plural form"));
-                          error_with_progname = true;
-                        }
-                      free (ctxt);
-                    }
-                  best_cp->msgid_plural = xstrdup (separator + 1);
-                }
-            }
-
-          {
-            flag_context_ty msgid_context = best_cp->msgid_context;
-            flag_context_ty msgid_plural_context = best_cp->msgid_plural_context;
-
-            /* Special support for the 3-argument tr operator in Qt:
-               When --qt and --keyword=tr:1,1,2c,3t are specified, add to the
-               context the information that the argument is expeected to be a
-               qt-plural-format.  */
-            if (recognize_format_qt
-                && current_formatstring_parser3 == &formatstring_qt_plural
-                && best_cp->msgid_plural == best_cp->msgid)
-              {
-                msgid_context.is_format3 = yes_according_to_context;
-                msgid_plural_context.is_format3 = yes_according_to_context;
-              }
-
-            mp = remember_a_message (ap->mlp, best_cp->msgctxt, best_cp->msgid,
-                                     msgid_context,
-                                     &best_cp->msgid_pos,
-                                     NULL, best_cp->msgid_comment);
-            if (mp != NULL && best_cp->msgid_plural != NULL)
-              remember_a_message_plural (mp, best_cp->msgid_plural,
-                                         msgid_plural_context,
-                                         &best_cp->msgid_plural_pos,
-                                         NULL);
-          }
-
-          if (best_cp->xcomments.nitems > 0)
-            {
-              /* Add best_cp->xcomments to mp->comment_dot, unless already
-                 present.  */
-              size_t i;
-
-              for (i = 0; i < best_cp->xcomments.nitems; i++)
-                {
-                  const char *xcomment = best_cp->xcomments.item[i];
-                  bool found = false;
-
-                  if (mp != NULL && mp->comment_dot != NULL)
-                    {
-                      size_t j;
-
-                      for (j = 0; j < mp->comment_dot->nitems; j++)
-                        if (strcmp (xcomment, mp->comment_dot->item[j]) == 0)
-                          {
-                            found = true;
-                            break;
-                          }
-                    }
-                  if (!found)
-                    message_comment_dot_append (mp, xcomment);
-                }
-            }
-        }
-    }
-  else
-    {
-      /* No complete call was parsed.  */
-      /* Note: There is a memory leak here: When there is more than one
-         alternative, the same string can be stored in multiple alternatives,
-         and it's not easy to free all strings reliably.  */
-      if (ap->nalternatives == 1)
-        {
-          if (ap->alternative[0].msgctxt != NULL)
-            free (ap->alternative[0].msgctxt);
-          if (ap->alternative[0].msgid != NULL)
-            free (ap->alternative[0].msgid);
-          if (ap->alternative[0].msgid_plural != NULL)
-            free (ap->alternative[0].msgid_plural);
-        }
-    }
-
-  for (i = 0; i < ap->nalternatives; i++)
-    drop_reference (ap->alternative[i].msgid_comment);
-  free (ap);
-}
-
-
-struct mixed_string_buffer *
-mixed_string_buffer_alloc (lexical_context_ty lcontext,
-                           const char *logical_file_name,
-                           int line_number)
-{
-  struct mixed_string_buffer *bp = XMALLOC (struct mixed_string_buffer);
-  bp->utf8_buffer = NULL;
-  bp->utf8_buflen = 0;
-  bp->utf8_allocated = 0;
-  bp->utf16_surr = 0;
-  bp->curr_buffer = NULL;
-  bp->curr_buflen = 0;
-  bp->curr_allocated = 0;
-  bp->lcontext = lcontext;
-  bp->logical_file_name = logical_file_name;
-  bp->line_number = line_number;
-  return bp;
-}
-
-/* Auxiliary function: Append a byte to bp->curr.  */
-static inline void
-mixed_string_buffer_append_to_curr_buffer (struct mixed_string_buffer *bp,
-                                           unsigned char c)
-{
-  if (bp->curr_buflen == bp->curr_allocated)
-    {
-      bp->curr_allocated = 2 * bp->curr_allocated + 10;
-      bp->curr_buffer = xrealloc (bp->curr_buffer, bp->curr_allocated);
-    }
-  bp->curr_buffer[bp->curr_buflen++] = c;
-}
-
-/* Auxiliary function: Ensure count more bytes are available in bp->utf8.  */
-static inline void
-mixed_string_buffer_grow_utf8_buffer (struct mixed_string_buffer *bp,
-                                         size_t count)
-{
-  if (bp->utf8_buflen + count > bp->utf8_allocated)
-    {
-      size_t new_allocated = 2 * bp->utf8_allocated + 10;
-      if (new_allocated < bp->utf8_buflen + count)
-        new_allocated = bp->utf8_buflen + count;
-      bp->utf8_allocated = new_allocated;
-      bp->utf8_buffer = xrealloc (bp->utf8_buffer, new_allocated);
-    }
-}
-
-/* Auxiliary function: Append a Unicode character to bp->utf8.
-   uc must be < 0x110000.  */
-static inline void
-mixed_string_buffer_append_to_utf8_buffer (struct mixed_string_buffer *bp,
-                                           ucs4_t uc)
-{
-  unsigned char utf8buf[6];
-  int count = u8_uctomb (utf8buf, uc, 6);
-
-  if (count < 0)
-    /* The caller should have ensured that uc is not out-of-range.  */
-    abort ();
-
-  mixed_string_buffer_grow_utf8_buffer (bp, count);
-  memcpy (bp->utf8_buffer + bp->utf8_buflen, utf8buf, count);
-  bp->utf8_buflen += count;
-}
-
-/* Auxiliary function: Flush bp->utf16_surr into bp->utf8_buffer.  */
-static inline void
-mixed_string_buffer_flush_utf16_surr (struct mixed_string_buffer *bp)
-{
-  if (bp->utf16_surr != 0)
-    {
-      /* A half surrogate is invalid, therefore use U+FFFD instead.  */
-      mixed_string_buffer_append_to_utf8_buffer (bp, 0xfffd);
-      bp->utf16_surr = 0;
-    }
-}
-
-/* Auxiliary function: Flush bp->curr_buffer into bp->utf8_buffer.  */
-static inline void
-mixed_string_buffer_flush_curr_buffer (struct mixed_string_buffer *bp,
-                                       int line_number)
-{
-  if (bp->curr_buflen > 0)
-    {
-      char *curr;
-      size_t count;
-
-      mixed_string_buffer_append_to_curr_buffer (bp, '\0');
-
-      /* Convert from the source encoding to UTF-8.  */
-      curr = from_current_source_encoding (bp->curr_buffer, bp->lcontext,
-                                           bp->logical_file_name,
-                                           line_number);
-
-      /* Append it to bp->utf8_buffer.  */
-      count = strlen (curr);
-      mixed_string_buffer_grow_utf8_buffer (bp, count);
-      memcpy (bp->utf8_buffer + bp->utf8_buflen, curr, count);
-      bp->utf8_buflen += count;
-
-      if (curr != bp->curr_buffer)
-        free (curr);
-      bp->curr_buflen = 0;
-    }
-}
-
-void
-mixed_string_buffer_append_char (struct mixed_string_buffer *bp, int c)
-{
-  /* Switch from Unicode character mode to multibyte character mode.  */
-  mixed_string_buffer_flush_utf16_surr (bp);
-
-  /* When a newline is seen, convert the accumulated multibyte sequence.
-     This ensures a correct line number in the error message in case of
-     a conversion error.  The "- 1" is to account for the newline.  */
-  if (c == '\n')
-    mixed_string_buffer_flush_curr_buffer (bp, bp->line_number - 1);
-
-  mixed_string_buffer_append_to_curr_buffer (bp, (unsigned char) c);
-}
-
-void
-mixed_string_buffer_append_unicode (struct mixed_string_buffer *bp, int c)
-{
-  /* Switch from multibyte character mode to Unicode character mode.  */
-  mixed_string_buffer_flush_curr_buffer (bp, bp->line_number);
-
-  /* Test whether this character and the previous one form a Unicode
-     surrogate character pair.  */
-  if (bp->utf16_surr != 0 && (c >= 0xdc00 && c < 0xe000))
-    {
-      unsigned short utf16buf[2];
-      ucs4_t uc;
-
-      utf16buf[0] = bp->utf16_surr;
-      utf16buf[1] = c;
-      if (u16_mbtouc (&uc, utf16buf, 2) != 2)
-        abort ();
-
-      mixed_string_buffer_append_to_utf8_buffer (bp, uc);
-      bp->utf16_surr = 0;
-    }
-  else
-    {
-      mixed_string_buffer_flush_utf16_surr (bp);
-
-      if (c >= 0xd800 && c < 0xdc00)
-        bp->utf16_surr = c;
-      else if (c >= 0xdc00 && c < 0xe000)
-        {
-          /* A half surrogate is invalid, therefore use U+FFFD instead.  */
-          mixed_string_buffer_append_to_utf8_buffer (bp, 0xfffd);
-        }
-      else
-        mixed_string_buffer_append_to_utf8_buffer (bp, c);
-    }
-}
-
-char *
-mixed_string_buffer_done (struct mixed_string_buffer *bp)
+recognize_qt_formatstrings (void)
 {
-  char *utf8_buffer;
-
-  /* Flush all into bp->utf8_buffer.  */
-  mixed_string_buffer_flush_utf16_surr (bp);
-  mixed_string_buffer_flush_curr_buffer (bp, bp->line_number);
-  /* NUL-terminate it.  */
-  mixed_string_buffer_grow_utf8_buffer (bp, 1);
-  bp->utf8_buffer[bp->utf8_buflen] = '\0';
-
-  /* Free curr_buffer and bp itself.  */
-  utf8_buffer = bp->utf8_buffer;
-  free (bp->curr_buffer);
-  free (bp);
-
-  /* Return it.  */
-  return utf8_buffer;
+  return recognize_format_qt
+         && current_formatstring_parser3 == &formatstring_qt_plural;
 }
 
 
index 7c3712a9b9f9b38f04aa9a7ddbd0a270807b11ad..4d5df4ec24e90194a11ecfa90103eb02db4587c9 100644 (file)
 
 #include <stdbool.h>
 #include <stddef.h>
-#include <stdlib.h>
-
-#if HAVE_ICONV
-#include <iconv.h>
-#endif
 
 #include "message.h"
-#include "pos.h"
-#include "str-list.h"
-
-/* Declare 'line_comment' and 'input_syntax'.  */
-#include "read-catalog.h"
-
+#include "rc-str-list.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 
-/* If true, omit the header entry.
-   If false, keep the header entry present in the input.  */
-extern int xgettext_omit_header;
-
-extern bool substring_match;
-
+/* If true, add all comments immediately preceding one of the keywords. */
+extern bool add_all_comments;
 
-/* Calling convention for a given keyword.  */
-struct callshape
-{
-  int argnum1; /* argument number to use for msgid */
-  int argnum2; /* argument number to use for msgid_plural */
-  int argnumc; /* argument number to use for msgctxt */
-  bool argnum1_glib_context; /* argument argnum1 has the syntax "ctxt|msgid" */
-  bool argnum2_glib_context; /* argument argnum2 has the syntax "ctxt|msgid" */
-  int argtotal; /* total number of arguments */
-  string_list_ty xcomments; /* auto-extracted comments */
-};
+/* Tag used in comment of prevailing domain.  */
+extern char *comment_tag;
 
-/* Split keyword spec into keyword, argnum1, argnum2, argnumc.  */
-extern void split_keywordspec (const char *spec, const char **endp,
-                               struct callshape *shapep);
+/* List of messages whose msgids must not be extracted, or NULL.
+   Used by remember_a_message().  */
+extern message_list_ty *exclude;
 
-/* Set of alternative calling conventions for a given keyword.  */
-struct callshapes
-{
-  const char *keyword;          /* the keyword, not NUL terminated */
-  size_t keyword_len;           /* the keyword's length */
-  size_t nshapes;
-  struct callshape shapes[1];   /* actually nshapes elements */
-};
+/* String used as prefix for msgstr.  */
+extern const char *msgstr_prefix;
 
-/* Insert a (keyword, callshape) pair into a hash table mapping keyword to
-   'struct callshapes *'.  */
-extern void insert_keyword_callshape (hash_table *table,
-                                      const char *keyword, size_t keyword_len,
-                                      const struct callshape *shape);
+/* String used as suffix for msgstr.  */
+extern const char *msgstr_suffix;
 
+/* If true, omit the header entry.
+   If false, keep the header entry present in the input.  */
+extern int xgettext_omit_header;
 
-/* Context representing some flags.  */
-typedef struct flag_context_ty flag_context_ty;
-struct flag_context_ty
-{
-  /* Regarding the primary formatstring type.  */
-  /*enum is_format*/ unsigned int is_format1    : 3;
-  /*bool*/           unsigned int pass_format1  : 1;
-  /* Regarding the secondary formatstring type.  */
-  /*enum is_format*/ unsigned int is_format2    : 3;
-  /*bool*/           unsigned int pass_format2  : 1;
-  /* Regarding the tertiary formatstring type.  */
-  /*enum is_format*/ unsigned int is_format3    : 3;
-  /*bool*/           unsigned int pass_format3  : 1;
-};
-/* Null context.  */
-extern flag_context_ty null_context;
-/* Transparent context.  */
-extern flag_context_ty passthrough_context;
-/* Compute an inherited context.
-   The outer_context is assumed to have all pass_format* flags = false.
-   The result will then also have all pass_format* flags = false.  */
-extern flag_context_ty
-       inherited_context (flag_context_ty outer_context,
-                          flag_context_ty modifier_context);
+extern enum is_syntax_check default_syntax_check[NSYNTAXCHECKS];
 
-/* Context representing some flags, for each possible argument number.
-   This is a linked list, sorted according to the argument number.  */
-typedef struct flag_context_list_ty flag_context_list_ty;
-struct flag_context_list_ty
-{
-  int argnum;                   /* current argument number, > 0 */
-  flag_context_ty flags;        /* flags for current argument */
-  flag_context_list_ty *next;
-};
+/* Language dependent format string parser.
+   NULL if the language has no notion of format strings.  */
+extern struct formatstring_parser *current_formatstring_parser1;
+extern struct formatstring_parser *current_formatstring_parser2;
+extern struct formatstring_parser *current_formatstring_parser3;
 
-/* Iterator through a flag_context_list_ty.  */
-typedef struct flag_context_list_iterator_ty flag_context_list_iterator_ty;
-struct flag_context_list_iterator_ty
-{
-  int argnum;                           /* current argument number, > 0 */
-  const flag_context_list_ty* head;     /* tail of list */
-};
-extern flag_context_list_iterator_ty null_context_list_iterator;
-extern flag_context_list_iterator_ty passthrough_context_list_iterator;
-extern flag_context_list_iterator_ty
-       flag_context_list_iterator (flag_context_list_ty *list);
-extern flag_context_ty
-       flag_context_list_iterator_advance (flag_context_list_iterator_ty *iter);
 
-/* For nearly each backend, we have a separate table mapping a keyword to
-   a flag_context_list_ty *.  */
-typedef hash_table /* char[] -> flag_context_list_ty * */
-        flag_context_list_table_ty;
-extern flag_context_list_ty *
-       flag_context_list_table_lookup (flag_context_list_table_ty *flag_table,
-                                       const void *key, size_t keylen);
 /* Record a flag in the appropriate backend's table.  */
 extern void xgettext_record_flag (const char *optionstring);
 
 
-/* Context while building up lexical tokens.  */
-typedef enum
-  {
-    lc_outside, /* Initial context: outside of comments and strings.  */
-    lc_comment, /* Inside a comment.  */
-    lc_string,  /* Inside a string literal.  */
-
-    /* For embedded XML in programming code, like E4X in JavaScript.  */
-    lc_xml_open_tag,   /* Inside an opening tag of an XML element.  */
-    lc_xml_close_tag,  /* Inside a closing tag of an XML element.  */
-    lc_xml_content     /* Inside an XML text node.  */
-  }
-  lexical_context_ty;
-
-/* Error message about non-ASCII character in a specific lexical context.  */
-extern char *non_ascii_error_message (lexical_context_ty lcontext,
-                                      const char *file_name,
-                                      size_t line_number);
-
-
-/* Canonicalized encoding name for all input files.  */
-extern const char *xgettext_global_source_encoding;
-
-#if HAVE_ICONV
-/* Converter from xgettext_global_source_encoding to UTF-8 (except from
-   ASCII or UTF-8, when this conversion is a no-op).  */
-extern iconv_t xgettext_global_source_iconv;
-#endif
-
-/* Canonicalized encoding name for the current input file.  */
-extern const char *xgettext_current_source_encoding;
-
-#if HAVE_ICONV
-/* Converter from xgettext_current_source_encoding to UTF-8 (except from
-   ASCII or UTF-8, when this conversion is a no-op).  */
-extern iconv_t xgettext_current_source_iconv;
-#endif
-
-/* Convert the given string from xgettext_current_source_encoding to
-   the output file encoding (i.e. ASCII or UTF-8).
-   The resulting string is either the argument string, or freshly allocated.
-   The lcontext, file_name and line_number are only used for error message
-   purposes.  */
-extern char *from_current_source_encoding (const char *string,
-                                           lexical_context_ty lcontext,
-                                           const char *file_name,
-                                           size_t line_number);
-
-
-/* List of messages whose msgids must not be extracted, or NULL.
-   Used by remember_a_message().  */
-extern message_list_ty *exclude;
-
+extern const char * xgettext_comment (size_t n);
+extern void xgettext_comment_reset (void);
 
 /* Comment handling for backends which support combining adjacent strings
    even across lines.
@@ -208,179 +81,14 @@ extern message_list_ty *exclude;
    In order to avoid excessive copying of strings, we use reference
    counting.  */
 
-typedef struct refcounted_string_list_ty refcounted_string_list_ty;
-struct refcounted_string_list_ty
-{
-  unsigned int refcount;
-  struct string_list_ty contents;
-};
-
-static inline refcounted_string_list_ty *
-add_reference (refcounted_string_list_ty *rslp)
-{
-  if (rslp != NULL)
-    rslp->refcount++;
-  return rslp;
-}
-
-static inline void
-drop_reference (refcounted_string_list_ty *rslp)
-{
-  if (rslp != NULL)
-    {
-      if (rslp->refcount > 1)
-        rslp->refcount--;
-      else
-        {
-          string_list_destroy (&rslp->contents);
-          free (rslp);
-        }
-    }
-}
-
 extern refcounted_string_list_ty *savable_comment;
 extern void savable_comment_add (const char *str);
 extern void savable_comment_reset (void);
+extern void
+       savable_comment_to_xgettext_comment (refcounted_string_list_ty *rslp);
 
 
-/* Add a message to the list of extracted messages.
-   msgctxt must be either NULL or a malloc()ed string; its ownership is passed
-   to the callee.
-   MSGID must be a malloc()ed string; its ownership is passed to the callee.
-   POS->file_name must be allocated with indefinite extent.
-   EXTRACTED_COMMENT is a comment that needs to be copied into the POT file,
-   or NULL.
-   COMMENT may be savable_comment, or it may be a saved copy of savable_comment
-   (then add_reference must be used when saving it, and drop_reference while
-   dropping it).  Clear savable_comment.
-   Return the new or found message, or NULL if the message is excluded.  */
-extern message_ty *remember_a_message (message_list_ty *mlp,
-                                       char *msgctxt,
-                                       char *msgid,
-                                       flag_context_ty context,
-                                       lex_pos_ty *pos,
-                                       const char *extracted_comment,
-                                       refcounted_string_list_ty *comment);
-
-/* Add an msgid_plural to a message previously returned by
-   remember_a_message.
-   STRING must be a malloc()ed string; its ownership is passed to the callee.
-   POS->file_name must be allocated with indefinite extent.
-   COMMENT may be savable_comment, or it may be a saved copy of savable_comment
-   (then add_reference must be used when saving it, and drop_reference while
-   dropping it).  Clear savable_comment.  */
-extern void remember_a_message_plural (message_ty *mp,
-                                       char *string,
-                                       flag_context_ty context,
-                                       lex_pos_ty *pos,
-                                       refcounted_string_list_ty *comment);
-
-
-/* Represents the progressive parsing of an argument list w.r.t. a single
-   'struct callshape'.  */
-struct partial_call
-{
-  int argnumc;                  /* number of context argument, 0 when seen */
-  int argnum1;                  /* number of singular argument, 0 when seen */
-  int argnum2;                  /* number of plural argument, 0 when seen */
-  bool argnum1_glib_context;    /* argument argnum1 has the syntax "ctxt|msgid" */
-  bool argnum2_glib_context;    /* argument argnum2 has the syntax "ctxt|msgid" */
-  int argtotal;                 /* total number of arguments, 0 if unspecified */
-  string_list_ty xcomments;     /* auto-extracted comments */
-  char *msgctxt;                /* context - owned string, or NULL */
-  lex_pos_ty msgctxt_pos;
-  char *msgid;                  /* msgid - owned string, or NULL */
-  flag_context_ty msgid_context;
-  lex_pos_ty msgid_pos;
-  refcounted_string_list_ty *msgid_comment;
-  char *msgid_plural;           /* msgid_plural - owned string, or NULL */
-  flag_context_ty msgid_plural_context;
-  lex_pos_ty msgid_plural_pos;
-};
-
-/* Represents the progressive parsing of an argument list w.r.t. an entire
-   'struct callshapes'.  */
-struct arglist_parser
-{
-  message_list_ty *mlp;         /* list where the message shall be added */
-  const char *keyword;          /* the keyword, not NUL terminated */
-  size_t keyword_len;           /* the keyword's length */
-  bool next_is_msgctxt;         /* true if the next argument is the msgctxt */
-  size_t nalternatives;         /* number of partial_call alternatives */
-  struct partial_call alternative[1]; /* partial_call alternatives */
-};
-
-/* Creates a fresh arglist_parser recognizing calls.
-   You can pass shapes = NULL for a parser not recognizing any calls.  */
-extern struct arglist_parser * arglist_parser_alloc (message_list_ty *mlp,
-                                                     const struct callshapes *shapes);
-/* Clones an arglist_parser.  */
-extern struct arglist_parser * arglist_parser_clone (struct arglist_parser *ap);
-/* Adds a string argument to an arglist_parser.  ARGNUM must be > 0.
-   STRING must be malloc()ed string; its ownership is passed to the callee.
-   FILE_NAME must be allocated with indefinite extent.
-   COMMENT may be savable_comment, or it may be a saved copy of savable_comment
-   (then add_reference must be used when saving it, and drop_reference while
-   dropping it).  Clear savable_comment.  */
-extern void arglist_parser_remember (struct arglist_parser *ap,
-                                     int argnum, char *string,
-                                     flag_context_ty context,
-                                     char *file_name, size_t line_number,
-                                     refcounted_string_list_ty *comment);
-/* Adds a string argument as msgctxt to an arglist_parser, without incrementing
-   the current argument number.
-   STRING must be malloc()ed string; its ownership is passed to the callee.
-   FILE_NAME must be allocated with indefinite extent.  */
-extern void arglist_parser_remember_msgctxt (struct arglist_parser *ap,
-                                             char *string,
-                                             flag_context_ty context,
-                                             char *file_name, size_t line_number);
-/* Tests whether an arglist_parser has is not waiting for more arguments after
-   argument ARGNUM.  */
-extern bool arglist_parser_decidedp (struct arglist_parser *ap, int argnum);
-/* Terminates the processing of an arglist_parser after argument ARGNUM and
-   deletes it.  */
-extern void arglist_parser_done (struct arglist_parser *ap, int argnum);
-
-
-/* A string buffer type that allows appending bytes (in the
-   xgettext_current_source_encoding) or Unicode characters.
-   Returns the entire string in UTF-8 encoding.  */
-
-struct mixed_string_buffer
-{
-  /* The part of the string that has already been converted to UTF-8.  */
-  char *utf8_buffer;
-  size_t utf8_buflen;
-  size_t utf8_allocated;
-  /* The first half of an UTF-16 surrogate character.  */
-  unsigned short utf16_surr;
-  /* The part of the string that is still in the source encoding.  */
-  char *curr_buffer;
-  size_t curr_buflen;
-  size_t curr_allocated;
-  /* The lexical context.  Used only for error message purposes.  */
-  lexical_context_ty lcontext;
-  const char *logical_file_name;
-  int line_number;
-};
-
-/* Creates a fresh mixed_string_buffer.  */
-extern struct mixed_string_buffer *
-       mixed_string_buffer_alloc (lexical_context_ty lcontext,
-                                  const char *logical_file_name,
-                                  int line_number);
-
-/* Appends a character to a mixed_string_buffer.  */
-extern void mixed_string_buffer_append_char (struct mixed_string_buffer *bp,
-                                             int c);
-
-/* Appends a Unicode character to a mixed_string_buffer.  */
-extern void mixed_string_buffer_append_unicode (struct mixed_string_buffer *bp,
-                                                int c);
-
-/* Frees mixed_string_buffer and returns the accumulated string in UTF-8.  */
-extern char * mixed_string_buffer_done (struct mixed_string_buffer *bp);
+extern bool recognize_qt_formatstrings (void);
 
 
 #ifdef __cplusplus